创建标签模块

tag 目录下 tag.controller.ts、tag.middleware.ts、tag.router.ts、tag.service.ts。然后在 index.ts 中导入并使用路由。

tag.controller.ts:

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

tag.middleware.ts:

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

tag.router.ts:

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

const router = express.Router();

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

tag.service.ts:

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

创建标签数据表

1
2
3
4
5
ALTER TABLE `tag` (
`id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL UNIQUE KEY
) DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci;

定义存储标签数据的功能

新建 tag.model.ts,在这个文件里可以定义去导出一个类,用它去描述一下标签这种数据。

1
2
3
4
export class TagModel {
id?:number;
name?:string;
}

tag.service.ts:

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

/**
* 创建标签
*/
export const createTag = async (tag: TagModel) => {
// 准备查询
const statement = `
INSERT INTO tag
SET ?
`;
// 执行查询
const [data] = await connection.promise().query(statement, tag);

// 提供数据
return data as any;
};

定义按名字查找标签的功能

tag.service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 按名字查找标签
*/
export const getTagByName = async (tagName: string) => {
// 准备查询
const statement = `
SELECT id, name FROM tag
WHERE name = ?
`;

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

// 提供数据
return data[0];
};

定义创建标签用的接口

tag.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
import { Request, Response, NextFunction } from "express";
import { createTag, getTagByName } from "./tag.service";

/**
* 创建标签
*/
export const store = async (
request: Request,
response: Response,
next: NextFunction
) => {
// 准备数据
const { name } = request.body;

try {
// 查找标签
const tag = await getTagByName(name);

// 如果标签存在就报错
if (tag) throw new Error("TAG_ALREADY_EXISTS");

// 存储标签
const data = await createTag({ name });

// 做出响应
response.status(201).send(data);
} catch (error) {
next(error); //在app.middleware.ts文件中统一处理报错
}
};

tag.router.ts:

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

const router = express.Router();

/**
* 创建标签
*/
router.post("/tags", authGuard, tagController.store);

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

内容标签:创建保存内容与标签关系用的数据表

1
2
3
4
5
6
7
8
9
10
CREATE TABLE `post_tag`(
`postId` INT(11) NOT NULL,
`tagId` INT(11) NOT NULL,
PRIMARY KEY(`postId`,`tagId`),
FOREIGN KEY(`postId`) REFERENCES `post`(`id`)
ON DELETE NO ACTION ON UPDATE NO ACTION,
FOREIGN KEY(`tagId`) REFERENCES `tag`(`id`)
ON DELETE NO ACTION ON UPDATE NO ACTION
) DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci;

定义保存内容标签服务

给内容打上标签就是在 post_tag 数据表中创建一条数据记录,记录一下内容的 id,还有给这个内容贴上标签的 id。

post.service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 保存内容标签
*/
export const createPostTag = async (postId: number, tagId: number) => {
// 准备查询
const statement = `
INSERT INTO post_tag (postId,tagId)
VALUES(?,?)
`;

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

//提供数据
return data;
};

定义检查内容标签服务

post.service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 检查内容标签
*/
export const postHasTag = async (postId: number, tagId: number) => {
// 准备查询
const statement = `
SELECT * FROM post_tag
WHERE postId=? AND tagId=?
`;

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

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

定义添加内容标签接口

post.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
39
40
41
42
43
44
45
46
47
48
/**
* 添加内容标签
*/
export const storePostTag = async (
request: Request,
response: Response,
next: NextFunction
) => {
// 准备数据
const { postId } = request.params;
const { name } = request.body;

let tag: TagModel;

// 查找标签
try {
tag = await getTagByName(name);
} catch (error) {
return next(error);
}
// 找到标签,验证内容标签
if (tag) {
try {
const postTag = await postHasTag(parseInt(postId, 10), tag.id);
if (postTag) return next(new Error("POST_ALREADY_HAS_THIS_TAG")); //在app.middleware.ts中统一处理报错
} catch (error) {
return next(error);
}
}

// 没找到标签,创建这个标签
if (!tag) {
try {
const data = await createTag({ name });
tag = { id: data.insertId };
} catch (error) {
return next(error);
}
}

// 给内容打上标签
try {
await createPostTag(parseInt(postId, 10), tag.id);
response.sendStatus(201);
} catch (error) {
return next(error);
}
};

post.router.ts:

1
2
3
4
5
6
7
8
9
/**
* 添加内容标签
*/
router.post(
"/posts/:postId/tag",
authGuard,
accessControl({ possession: true }),
postController.storePostTag
);

定义删除内容标签服务

post.service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 删除内容标签
*/
export const deletePostTag = async (postId: number, tagId: number) => {
// 准备查询
const statement = `
DELETE FROM post_tag
WHERE postId = ? AND tagId = ?
`;
// 执行查询
const [data] = await connection.promise().query(statement, [postId, tagId]);

// 提供数据
return data;
};