创建评论模块

comment 目录下创建 comment.controller.ts、comment.router.ts、comment.service.ts、comment.middleware.ts 。

comment.controller.ts:

1
import { Request, Response, NextFunction } from "express";

comment.router.ts:

1
2
3
4
5
6
7
8
9
import express from "express";
import * as commentController from "./comment.controller";

const router = express.Router();

/**
* 导出路由
*/
export default router;

comment.service.ts:

1
import { connection } from "../app/database/mysql";

comment.middleware.ts:

1
import { Request, Response, NextFunction } from "express";

在 app/index.ts 中引入 comment.router 并使用路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import express from "express";
import postRouter from "../post/post.router";
import userRouter from "../user/user.router";
import authRouter from "../auth/auth.router";
import fileRouter from "../file/file.router";
import tagRouter from "../tag/tag.router";
import commentRouter from "../comment/comment.router";
import { defaultErrorHandler } from "./app.middleware";

/**
* 创建应用
*/
const app = express();

/**
* 导入处理json使用的中间件
*/
app.use(express.json());

/**
* 路由
*/
app.use(
postRouter,
userRouter,
authRouter,
fileRouter,
tagRouter,
commentRouter
);

/**
* 默认异常处理器
*/
app.use(defaultErrorHandler);

/**
* 导出应用
*/
export default app;

创建评论数据表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE `comment`(
`id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`content` LONGTEXT,
`postId` INT(11) NOT NULL,
`userId` INT(11) NOT NULL,
`parentId` INT(11) DEFAULT NULL,

FOREIGN KEY (`postId`) REFERENCES `post`(`id`)
ON DELETE NO ACTION ON UPDATE NO ACTION,

FOREIGN KEY (`userId`) REFERENCES `user`(`id`)
ON DELETE NO ACTION ON UPDATE NO ACTION,

FOREIGN KEY (`parentId`) REFERENCES `comment`(`id`)
ON DELETE NO ACTION ON UPDATE NO ACTION
) DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci;

定义存储评论数据的功能

新建 comment.model.ts,用来描述参数类型

1
2
3
4
5
6
7
export class CommentModel {
id?: number;
content?: string;
postId?: number;
userId?: number;
parentId?: number;
}

comment.service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { connection } from "../app/database/mysql";
import { CommentModel } from "./comment.model";

/**
* 创建评论
*/
export const createComment = async (comment: CommentModel) => {
// 准备查询
const statement = `
INSERT INTO comment
SET ?
`;

// 执行查询
const [data] = await connection.promise().query(statement, comment);

// 提供数据
return data;
};

定义发表评论用的接口

comment.controller.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { Request, Response, NextFunction } from "express";
import { createComment } from "./comment.service";

/**
* 发表评论
*/
export const store = async (
request: Request,
response: Response,
next: NextFunction
) => {
// 准备数据
const { id: userId } = request.user;
const { content, postId } = request.body;

const comment = {
content,
postId,
userId,
};

try {
// 保存评论
const data = await createComment(comment);

// 做出响应
response.status(201).send(data);
} catch (error) {
next(error);
}
};

comment.router.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import express from "express";
import { authGuard } from "../auth/auth.middleware";
import * as commentController from "./comment.controller";

const router = express.Router();

/**
* 发表评论
*/
router.post("/comments", authGuard, commentController.store);

/**
* 导出路由
*/
export default router;

定义检查评论是否为回复评论的功能

comment.service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 检查评论是否是回复评论
*/
export const isReplyComment = async (commentId: number) => {
// 准备查询
const statement = `
SELECT parentId FROM comment
WHERE id = ?
`;
// 执行查询
const [data] = await connection.promise().query(statement, commentId);

// 提供数据
return data[0].parentId ? true : false;
};

定义回复评论接口

comment.controller.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 回复评论
*/
export const reply = async (
request: Request,
response: Response,
next: NextFunction
) => {
// 准备数据
const { commentId } = request.params;
const parentId = parseInt(commentId, 10);
const { id: userId } = request.user;
const { content, postId } = request.body;

const comment = {
content,
postId,
userId,
parentId,
};

try {
// 检查评论是否为回复评论
const reply = await isReplyComment(parentId);
if (reply) return next(new Error("UNABLE_TO_REPLY_THIS_COMMENT")); //在app.middleware.ts中统一处理
} catch (error) {
return next(error);
}

try {
// 回复评论
const data = await createComment(comment);
// 做出响应
response.status(201).send(data);
} catch (error) {
next(error);
}
};

comment.router.ts:

1
2
3
4
/**
* 回复评论
*/
router.post("/comments/:commentId/reply", authGuard, commentController.reply);

定义修改评论内容用的功能

comment.service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 修改评论
*/
export const updateComment = async (comment: CommentModel) => {
// 准备数据
const { id, content } = comment;

// 准备查询
const statement = `
UPDATE comment
SET content = ?
WHERE id = ?
`;

// 执行查询
const [data] = await connection.promise().query(statement, [content, id]);

// 提供数据
return data;
};

定义修改评论接口

comment.controller.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 修改评论
*/
export const update = async (
request: Request,
response: Response,
next: NextFunction
) => {
// 准备数据
const { commentId } = request.params;
const { content } = request.body;

const comment = {
id: parseInt(commentId, 10),
content,
};

try {
// 修改评论
const data = await updateComment(comment);
// 做出响应
response.send(data);
} catch (error) {
next(error);
}
};

comment.router.ts:

1
2
3
4
5
6
7
8
9
/**
* 修改评论
*/
router.patch(
"/comments/:commentId",
authGuard,
accessControl({ possession: true }),
commentController.update
);

定义删除评论用的功能

comment.service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 删除评论
*/
export const deleteComment = async (commentId: number) => {
// 准备查询
const statement = `
DELETE FROM comment
WHERE id = ?
`;

// 执行查询
const [data] = await connection.promise().query(statement, commentId);

// 提供数据
return data;
};

定义删除评论接口

comment.controller.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 删除评论
*/
export const destroy = async (
request: Request,
response: Response,
next: NextFunction
) => {
// 准备数据
const { commentId } = request.params;

try {
// 删除评论
const data = await deleteComment(parseInt(commentId, 10));

// 做出响应
response.send(data);
} catch (error) {
next(error);
}
};

comment.router.ts:

1
2
3
4
5
6
7
8
9
/**
* 删除评论
*/
router.delete(
"/comments/:commentId",
authGuard,
accessControl({ possession: true }),
commentController.destroy
);