@mindbase/express-knowledge 1.0.4 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,378 @@
1
+ import { Router, type Request, type Response, type NextFunction } from "express";
2
+ import { logger, validate } from "@mindbase/express-common";
3
+ import * as KnowledgeService from "../service/KnowledgeService";
4
+ import * as schemas from "../zod/knowledge.schema";
5
+
6
+ const router = Router();
7
+
8
+ // ==================== 知识库内容路由 ====================
9
+
10
+ /**
11
+ * 获取目录树
12
+ * @apiParam {number} [parentId] 父节点ID(可选,不传则获取根节点)
13
+ * @apiSuccess {number} code 状态码
14
+ * @apiSuccess {string} message 消息
15
+ * @apiSuccess {Object[]} data 目录树结构
16
+ * @apiSuccess {number} data[].id 节点ID
17
+ * @apiSuccess {string} data[].name 名称
18
+ * @apiSuccess {number} data[].type 类型(0:文件夹, 1:笔记, 2:书籍)
19
+ * @apiSuccess {number} data[].level 层级
20
+ * @apiSuccess {Object} data[].children 子节点
21
+ */
22
+ router.get("/tree", async (req: Request, res: Response, next: NextFunction) => {
23
+ const userId = res.locals.user?.id;
24
+ const permissions = res.locals.permissions || [];
25
+ const isAdmin = permissions.includes('admin') || permissions.includes('*:*:*');
26
+ const { parentId } = req.query;
27
+
28
+ try {
29
+ const tree = await KnowledgeService.getKnowledgeTree({
30
+ parentId: parentId ? Number(parentId) : null,
31
+ userId,
32
+ isAdmin,
33
+ });
34
+ res.json({ code: 200, message: "获取成功", data: tree });
35
+ } catch (error) {
36
+ logger.error("[KnowledgeRoute] 获取目录树失败:", error);
37
+ next(error);
38
+ }
39
+ });
40
+
41
+ /**
42
+ * 获取内容列表(分页)
43
+ * @apiParam {number} [pageIndex] 页码(可选,默认1)
44
+ * @apiParam {number} [pageSize] 每页数量(可选,默认10)
45
+ * @apiParam {number} [level] 层级筛选(可选)
46
+ * @apiParam {number} [type] 类型筛选(可选,0:文件夹, 1:笔记, 2:书籍)
47
+ * @apiParam {number[]} [safe] 安全等级筛选(可选)
48
+ * @apiSuccess {number} code 状态码
49
+ * @apiSuccess {string} message 消息
50
+ * @apiSuccess {Object} data 内容列表
51
+ * @apiSuccess {Object[]} data.list 内容数组
52
+ * @apiSuccess {number} data.total 总数
53
+ * @apiSuccess {number} data.pageIndex 当前页
54
+ * @apiSuccess {number} data.pageSize 每页数量
55
+ */
56
+ router.get("/list", validate(schemas.listKnowledgeSchema, "query"), async (req: Request, res: Response, next: NextFunction) => {
57
+ const userId = res.locals.user?.id;
58
+ const permissions = res.locals.permissions || [];
59
+ const isAdmin = permissions.includes('admin') || permissions.includes('*:*:*');
60
+
61
+ try {
62
+ const result = await KnowledgeService.listKnowledge({
63
+ ...req.query as any,
64
+ userId,
65
+ isAdmin,
66
+ });
67
+ res.json({ code: 200, message: "获取成功", data: result });
68
+ } catch (error) {
69
+ logger.error("[KnowledgeRoute] 获取内容列表失败:", error);
70
+ next(error);
71
+ }
72
+ });
73
+
74
+ /**
75
+ * 搜索内容
76
+ * @apiParam {string} keyword 搜索关键词(必填)
77
+ * @apiParam {number} [pageIndex] 页码(可选,默认1)
78
+ * @apiParam {number} [pageSize] 每页数量(可选,默认10)
79
+ * @apiSuccess {number} code 状态码
80
+ * @apiSuccess {string} message 消息
81
+ * @apiSuccess {Object} data 搜索结果
82
+ * @apiSuccess {Object[]} data.list 结果数组
83
+ * @apiSuccess {number} data.total 总数
84
+ */
85
+ router.get("/search", validate(schemas.searchKnowledgeSchema, "query"), async (req: Request, res: Response, next: NextFunction) => {
86
+ const userId = res.locals.user?.id;
87
+ const permissions = res.locals.permissions || [];
88
+ const isAdmin = permissions.includes('admin') || permissions.includes('*:*:*');
89
+
90
+ try {
91
+ const result = await KnowledgeService.searchKnowledge({
92
+ ...req.query as any,
93
+ userId,
94
+ isAdmin,
95
+ });
96
+ res.json({ code: 200, message: "搜索成功", data: result });
97
+ } catch (error) {
98
+ logger.error("[KnowledgeRoute] 搜索失败:", error);
99
+ next(error);
100
+ }
101
+ });
102
+
103
+ /**
104
+ * 获取内容详情
105
+ * @apiParam {number} id 内容ID(路径参数,必填)
106
+ * @apiSuccess {number} code 状态码
107
+ * @apiSuccess {string} message 消息
108
+ * @apiSuccess {Object} data 内容详情
109
+ * @apiSuccess {number} data.id ID
110
+ * @apiSuccess {string} data.name 名称
111
+ * @apiSuccess {string} data.content 内容
112
+ * @apiSuccess {string} data.description 描述
113
+ * @apiSuccess {number} data.type 类型
114
+ * @apiSuccess {number} data.safe 安全等级
115
+ * @apiSuccess {boolean} data.isTop 是否置顶
116
+ */
117
+ router.get("/:id", async (req: Request, res: Response, next: NextFunction) => {
118
+ const { id } = req.params;
119
+ const userId = res.locals.user?.id;
120
+ const permissions = res.locals.permissions || [];
121
+ const isAdmin = permissions.includes('admin') || permissions.includes('*:*:*');
122
+
123
+ try {
124
+ const knowledge = await KnowledgeService.getKnowledgeById(Number(id));
125
+
126
+ if (!knowledge) {
127
+ return res.status(404).json({ code: 404, message: "内容不存在", data: null });
128
+ }
129
+
130
+ // 权限检查
131
+ if (knowledge.safe === 2 && knowledge.userId !== userId && !isAdmin) {
132
+ return res.status(403).json({ code: 403, message: "无权访问", data: null });
133
+ }
134
+
135
+ res.json({ code: 200, message: "获取成功", data: knowledge });
136
+ } catch (error) {
137
+ logger.error("[KnowledgeRoute] 获取详情失败:", error);
138
+ next(error);
139
+ }
140
+ });
141
+
142
+ /**
143
+ * 获取子节点
144
+ * @apiParam {number} id 父节点ID(路径参数,必填)
145
+ * @apiSuccess {number} code 状态码
146
+ * @apiSuccess {string} message 消息
147
+ * @apiSuccess {Object[]} data 子节点列表
148
+ */
149
+ router.get("/:id/children", async (req: Request, res: Response, next: NextFunction) => {
150
+ const { id } = req.params;
151
+
152
+ try {
153
+ const children = await KnowledgeService.getChildren(Number(id));
154
+ res.json({ code: 200, message: "获取成功", data: children });
155
+ } catch (error) {
156
+ logger.error("[KnowledgeRoute] 获取子节点失败:", error);
157
+ next(error);
158
+ }
159
+ });
160
+
161
+ /**
162
+ * 创建内容
163
+ * @apiParam {string} name 名称(必填)
164
+ * @apiParam {number} type 类型(必填,0:文件夹, 1:笔记, 2:书籍)
165
+ * @apiParam {number} [safe] 安全等级(可选,默认0)
166
+ * @apiParam {string} [description] 描述(可选)
167
+ * @apiParam {string} [content] 内容(可选)
168
+ * @apiParam {string} [contentText] 文本内容(可选)
169
+ * @apiParam {string} [coverImage] 封面图片(可选)
170
+ * @apiParam {string} [author] 作者(可选)
171
+ * @apiParam {string} [isbn] ISBN(可选)
172
+ * @apiParam {number} [parentId] 父节点ID(可选)
173
+ * @apiSuccess {number} code 状态码
174
+ * @apiSuccess {string} message 消息
175
+ * @apiSuccess {Object} data 创建的内容信息
176
+ */
177
+ router.post("/", validate(schemas.createKnowledgeSchema), async (req: Request, res: Response, next: NextFunction) => {
178
+ const userId = res.locals.user?.id;
179
+ const { parentId, ...data } = req.body;
180
+
181
+ try {
182
+ const result = await KnowledgeService.createKnowledge({
183
+ ...data,
184
+ userId,
185
+ parentId,
186
+ });
187
+ res.json({ code: 200, message: "创建成功", data: result });
188
+ } catch (error) {
189
+ logger.error("[KnowledgeRoute] 创建失败:", error);
190
+ next(error);
191
+ }
192
+ });
193
+
194
+ /**
195
+ * 批量创建
196
+ * @apiParam {number} [parentId] 父节点ID(可选)
197
+ * @apiParam {Object[]} items 批量项目(必填)
198
+ * @apiParam {string} items[].name 名称(必填)
199
+ * @apiParam {number} items[].type 类型(必填,0:文件夹, 1:笔记, 2:书籍)
200
+ * @apiSuccess {number} code 状态码
201
+ * @apiSuccess {string} message 消息
202
+ * @apiSuccess {Object[]} data 批量创建结果
203
+ */
204
+ router.post("/batch", validate(schemas.batchCreateKnowledgeSchema), async (req: Request, res: Response, next: NextFunction) => {
205
+ const userId = res.locals.user?.id;
206
+ const { parentId, items } = req.body;
207
+
208
+ try {
209
+ const results = await KnowledgeService.createKnowledgeBatch(items, userId, parentId);
210
+ res.json({ code: 200, message: "批量创建完成", data: results });
211
+ } catch (error) {
212
+ logger.error("[KnowledgeRoute] 批量创建失败:", error);
213
+ next(error);
214
+ }
215
+ });
216
+
217
+ /**
218
+ * 批量获取详情
219
+ * @apiParam {number[]} ids 内容ID数组(必填)
220
+ * @apiSuccess {number} code 状态码
221
+ * @apiSuccess {string} message 消息
222
+ * @apiSuccess {Object[]} data 内容详情数组
223
+ */
224
+ router.post("/batch-detail", validate(schemas.batchDetailKnowledgeSchema), async (req: Request, res: Response, next: NextFunction) => {
225
+ const { ids } = req.body;
226
+
227
+ try {
228
+ const result = await KnowledgeService.getKnowledgeByIds(ids);
229
+ res.json({ code: 200, message: "获取成功", data: result });
230
+ } catch (error) {
231
+ logger.error("[KnowledgeRoute] 批量获取详情失败:", error);
232
+ next(error);
233
+ }
234
+ });
235
+
236
+ /**
237
+ * 更新内容
238
+ * @apiParam {number} id 内容ID(路径参数,必填)
239
+ * @apiParam {string} [name] 名称(可选)
240
+ * @apiParam {number} [type] 类型(可选)
241
+ * @apiParam {number} [safe] 安全等级(可选)
242
+ * @apiParam {string} [description] 描述(可选)
243
+ * @apiParam {string} [content] 内容(可选)
244
+ * @apiParam {string} [contentText] 文本内容(可选)
245
+ * @apiParam {string} [coverImage] 封面图片(可选)
246
+ * @apiParam {string} [author] 作者(可选)
247
+ * @apiParam {string} [isbn] ISBN(可选)
248
+ * @apiSuccess {number} code 状态码
249
+ * @apiSuccess {string} message 消息
250
+ * @apiSuccess {Object} data 更新后的内容信息
251
+ */
252
+ router.post("/:id", validate(schemas.updateKnowledgeSchema), async (req: Request, res: Response, next: NextFunction) => {
253
+ const { id } = req.params;
254
+ const userId = res.locals.user?.id;
255
+ const data = req.body;
256
+
257
+ try {
258
+ await KnowledgeService.updateKnowledge(Number(id), data, userId);
259
+ const result = await KnowledgeService.getKnowledgeById(Number(id));
260
+ res.json({ code: 200, message: "更新成功", data: result });
261
+ } catch (error) {
262
+ logger.error("[KnowledgeRoute] 更新失败:", error);
263
+ next(error);
264
+ }
265
+ });
266
+
267
+ /**
268
+ * 移动节点
269
+ * @apiParam {number} id 内容ID(路径参数,必填)
270
+ * @apiParam {number} newParentId 新父节点ID(必填)
271
+ * @apiSuccess {number} code 状态码
272
+ * @apiSuccess {string} message 消息
273
+ * @apiSuccess {Object} data 移动结果
274
+ */
275
+ router.post("/:id/move", validate(schemas.moveKnowledgeSchema), async (req: Request, res: Response, next: NextFunction) => {
276
+ const { id } = req.params;
277
+ const userId = res.locals.user?.id;
278
+ const { newParentId } = req.body;
279
+
280
+ try {
281
+ const result = await KnowledgeService.moveKnowledge(Number(id), newParentId, userId);
282
+ res.json({ code: 200, message: "移动成功", data: result });
283
+ } catch (error) {
284
+ logger.error("[KnowledgeRoute] 移动失败:", error);
285
+ next(error);
286
+ }
287
+ });
288
+
289
+ /**
290
+ * 更新置顶状态
291
+ * @apiParam {number} id 内容ID(路径参数,必填)
292
+ * @apiParam {number} isTop 是否置顶(必填,0:否, 1:是)
293
+ * @apiSuccess {number} code 状态码
294
+ * @apiSuccess {string} message 消息
295
+ * @apiSuccess {Object} data 更新结果
296
+ */
297
+ router.post("/:id/isTop", validate(schemas.updateIsTopSchema), async (req: Request, res: Response, next: NextFunction) => {
298
+ const { id } = req.params;
299
+ const userId = res.locals.user?.id;
300
+ const { isTop } = req.body;
301
+
302
+ try {
303
+ const result = await KnowledgeService.updateIsTop(Number(id), isTop, userId);
304
+ res.json({ code: 200, message: "更新成功", data: result });
305
+ } catch (error) {
306
+ logger.error("[KnowledgeRoute] 更新置顶状态失败:", error);
307
+ next(error);
308
+ }
309
+ });
310
+
311
+ /**
312
+ * 更新安全等级
313
+ * @apiParam {number} id 内容ID(路径参数,必填)
314
+ * @apiParam {number} safe 安全等级(必填,0:公开, 1:私有, 2:加密)
315
+ * @apiSuccess {number} code 状态码
316
+ * @apiSuccess {string} message 消息
317
+ * @apiSuccess {Object} data 更新结果
318
+ */
319
+ router.post("/:id/safe", validate(schemas.updateSafeSchema), async (req: Request, res: Response, next: NextFunction) => {
320
+ const { id } = req.params;
321
+ const userId = res.locals.user?.id;
322
+ const { safe } = req.body;
323
+
324
+ try {
325
+ const result = await KnowledgeService.updateSafe(Number(id), safe, userId);
326
+ res.json({ code: 200, message: "更新成功", data: result });
327
+ } catch (error) {
328
+ logger.error("[KnowledgeRoute] 更新安全等级失败:", error);
329
+ next(error);
330
+ }
331
+ });
332
+
333
+ /**
334
+ * 逻辑删除
335
+ * @apiParam {number} id 内容ID(路径参数,必填)
336
+ * @apiSuccess {number} code 状态码
337
+ * @apiSuccess {string} message 消息
338
+ * @apiSuccess {null} data 返回空
339
+ */
340
+ router.delete("/:id", async (req: Request, res: Response, next: NextFunction) => {
341
+ const { id } = req.params;
342
+ const userId = res.locals.user?.id;
343
+
344
+ try {
345
+ await KnowledgeService.deleteKnowledge(Number(id), userId);
346
+ res.json({ code: 200, message: "删除成功", data: null });
347
+ } catch (error) {
348
+ logger.error("[KnowledgeRoute] 删除失败:", error);
349
+ next(error);
350
+ }
351
+ });
352
+
353
+ /**
354
+ * 真实删除(管理员)
355
+ * @apiParam {number} id 内容ID(路径参数,必填)
356
+ * @apiSuccess {number} code 状态码
357
+ * @apiSuccess {string} message 消息
358
+ * @apiSuccess {null} data 返回空
359
+ */
360
+ router.delete("/:id/real", async (req: Request, res: Response, next: NextFunction) => {
361
+ const { id } = req.params;
362
+ const permissions = res.locals.permissions || [];
363
+ const isAdmin = permissions.includes('admin') || permissions.includes('*:*:*');
364
+
365
+ if (!isAdmin) {
366
+ return res.status(403).json({ code: 403, message: "需要管理员权限", data: null });
367
+ }
368
+
369
+ try {
370
+ await KnowledgeService.deleteKnowledgeReal(Number(id));
371
+ res.json({ code: 200, message: "删除成功", data: null });
372
+ } catch (error) {
373
+ logger.error("[KnowledgeRoute] 真实删除失败:", error);
374
+ next(error);
375
+ }
376
+ });
377
+
378
+ export default router;