@mindbase/express-knowledge 1.0.0 → 1.0.4

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.
@@ -9,7 +9,7 @@ import {
9
9
  updateIsTopSchema,
10
10
  updateKnowledgeSchema,
11
11
  updateSafeSchema
12
- } from "./chunk-Y47QRG3M.mjs";
12
+ } from "./chunk-2G44ILZL.mjs";
13
13
  import "./chunk-GWIBTASJ.mjs";
14
14
  import "./chunk-VHBDNZOQ.mjs";
15
15
  export {
@@ -24,4 +24,4 @@ export {
24
24
  updateKnowledgeSchema,
25
25
  updateSafeSchema
26
26
  };
27
- //# sourceMappingURL=Knowledge.schema-7KD5X7KG.mjs.map
27
+ //# sourceMappingURL=Knowledge.schema-BHFPDLIO.mjs.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  knowledge
3
- } from "./chunk-Y47QRG3M.mjs";
3
+ } from "./chunk-2G44ILZL.mjs";
4
4
  import "./chunk-GWIBTASJ.mjs";
5
5
  import "./chunk-VHBDNZOQ.mjs";
6
6
 
@@ -161,7 +161,7 @@ async function getKnowledgeTree(options) {
161
161
  conditions.push(like(knowledge.path, `${parent.path}%`));
162
162
  }
163
163
  const allNodes = await db.select().from(knowledge).where(and(...conditions)).orderBy(desc(knowledge.isTop), knowledge.name).execute();
164
- const { buildTree } = await import("./tree-builder-XZ4OA2TD.mjs");
164
+ const { buildTree } = await import("./tree-builder-LIYCLUJS.mjs");
165
165
  return buildTree(allNodes, parentId);
166
166
  }
167
167
  async function getChildren(parentId) {
@@ -300,4 +300,4 @@ export {
300
300
  updateKnowledge,
301
301
  updateSafe
302
302
  };
303
- //# sourceMappingURL=KnowledgeService-YZHS7UHY.mjs.map
303
+ //# sourceMappingURL=KnowledgeService-JDG23Q4Z.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../service/KnowledgeService.ts","../utils/path-builder.ts"],"sourcesContent":["import { knowledge } from \"../orm/Knowledge.schema\";\nimport { eq, and, or, like, sql, desc, inArray } from \"drizzle-orm\";\nimport { buildPath, getLevel, validatePath, isDescendant } from \"../utils/path-builder\";\nimport type { Knowledge, KnowledgeSafe, InsertKnowledge } from \"../types\";\n\nlet _db: any;\n\n/**\n * 初始化 Service 的数据库实例\n */\nexport function initKnowledgeService(db: any) {\n _db = db;\n}\n\nfunction getDB() {\n if (!_db) throw new Error(\"KnowledgeService not initialized with DB\");\n return _db;\n}\n\n// ==================== CRUD 操作 ====================\n\n/**\n * 创建知识库内容\n */\nexport async function createKnowledge(data: InsertKnowledge & { userId: number }) {\n const db = getDB();\n const now = new Date();\n\n // 如果有父节点,获取父节点信息\n let parentPath = \"/\";\n let level = 0;\n\n if (data.parentId) {\n const [parent] = await db\n .select()\n .from(knowledge)\n .where(and(eq(knowledge.id, data.parentId), eq(knowledge.isDelete, 0)))\n .limit(1);\n\n if (!parent) {\n throw new Error(\"父节点不存在\");\n }\n\n parentPath = parent.path;\n level = parent.level + 1;\n }\n\n // 先插入基础数据(不含 path 和 level)\n const result = await db\n .insert(knowledge)\n .values({\n name: data.name,\n type: data.type ?? 0,\n safe: data.safe ?? 0,\n userId: data.userId,\n description: data.description,\n content: data.content,\n contentText: data.contentText,\n viewType: data.viewType,\n coverImage: data.coverImage,\n author: data.author,\n isbn: data.isbn,\n created: now,\n updated: now,\n isDelete: 0,\n isTop: 0,\n path: \"/\", // 临时值\n level: 0, // 临时值\n })\n .execute();\n\n // 获取插入的 ID\n const lastId = result.lastInsertRowid as number;\n\n // 更新 path 和 level\n const finalPath = buildPath(parentPath, lastId);\n await db\n .update(knowledge)\n .set({ path: finalPath, level })\n .where(eq(knowledge.id, lastId))\n .execute();\n\n return getKnowledgeById(lastId);\n}\n\n/**\n * 批量创建知识库内容\n */\nexport async function createKnowledgeBatch(items: Array<{ name: string; type: number }>, userId: number, parentId?: number) {\n const results = [];\n\n for (const item of items) {\n try {\n const result = await createKnowledge({ ...item, userId, parentId });\n results.push({ success: true, data: result });\n } catch (error) {\n results.push({ success: false, error: error instanceof Error ? error.message : String(error) });\n }\n }\n\n return results;\n}\n\n/**\n * 分页获取知识库列表\n */\nexport async function listKnowledge(options: {\n pageIndex: number;\n pageSize: number;\n level?: number;\n type?: number;\n safe?: number[];\n userId?: number;\n isAdmin?: boolean;\n}) {\n const db = getDB();\n const { pageIndex, pageSize, level, type, safe = [0], userId, isAdmin } = options;\n\n let conditions = [eq(knowledge.isDelete, 0)];\n\n // 层级筛选\n if (level !== undefined) {\n conditions.push(eq(knowledge.level, level));\n }\n\n // 类型筛选\n if (type !== undefined) {\n conditions.push(eq(knowledge.type, type));\n }\n\n // 权限筛选\n const safeConditions = [];\n for (const s of safe) {\n switch (s) {\n case 0: // 公开\n safeConditions.push(eq(knowledge.safe, s));\n break;\n case 1: // 登录共享\n if (userId) {\n safeConditions.push(eq(knowledge.safe, s));\n }\n break;\n case 2: // 个人私密\n if (userId) {\n safeConditions.push(\n and(eq(knowledge.safe, s), isAdmin ? sql`1=1` : eq(knowledge.userId, userId))\n );\n }\n break;\n }\n }\n\n if (safeConditions.length > 0) {\n conditions.push(or(...safeConditions));\n }\n\n const offset = (pageIndex - 1) * pageSize;\n\n const list = await db\n .select()\n .from(knowledge)\n .where(and(...conditions))\n .limit(pageSize)\n .offset(offset)\n .orderBy(desc(knowledge.isTop), desc(knowledge.created))\n .execute();\n\n const [totalResult] = await db\n .select({ count: sql<number>`count(*)` })\n .from(knowledge)\n .where(and(...conditions))\n .execute();\n\n return {\n list,\n total: totalResult.count,\n pageIndex,\n pageSize,\n };\n}\n\n/**\n * 获取知识库详情\n */\nexport async function getKnowledgeById(id: number): Promise<Knowledge | null> {\n const db = getDB();\n const [item] = await db\n .select()\n .from(knowledge)\n .where(and(eq(knowledge.id, id), eq(knowledge.isDelete, 0)))\n .limit(1);\n\n return item || null;\n}\n\n/**\n * 批量获取知识库详情\n */\nexport async function getKnowledgeByIds(ids: number[]): Promise<Knowledge[]> {\n const db = getDB();\n if (ids.length === 0) return [];\n\n return await db\n .select()\n .from(knowledge)\n .where(and(inArray(knowledge.id, ids), eq(knowledge.isDelete, 0)))\n .execute();\n}\n\n/**\n * 获取知识库目录树\n */\nexport async function getKnowledgeTree(options: {\n parentId?: number | null;\n safe?: number[];\n userId?: number;\n isAdmin?: boolean;\n}) {\n const db = getDB();\n const { parentId = null, safe = [0], userId, isAdmin } = options;\n\n let conditions = [eq(knowledge.isDelete, 0)];\n\n // 权限筛选\n const safeConditions = [];\n for (const s of safe) {\n switch (s) {\n case 0: // 公开\n safeConditions.push(eq(knowledge.safe, s));\n break;\n case 1: // 登录共享\n if (userId) {\n safeConditions.push(eq(knowledge.safe, s));\n }\n break;\n case 2: // 个人私密\n if (userId) {\n safeConditions.push(\n and(eq(knowledge.safe, s), isAdmin ? sql`1=1` : eq(knowledge.userId, userId))\n );\n }\n break;\n }\n }\n\n if (safeConditions.length > 0) {\n conditions.push(or(...safeConditions));\n }\n\n // 如果指定了 parentId,只获取该节点下的子树\n if (parentId !== null) {\n const [parent] = await db\n .select()\n .from(knowledge)\n .where(and(eq(knowledge.id, parentId), eq(knowledge.isDelete, 0)))\n .limit(1);\n\n if (!parent) return [];\n\n conditions.push(like(knowledge.path, `${parent.path}%`));\n }\n\n const allNodes = await db\n .select()\n .from(knowledge)\n .where(and(...conditions))\n .orderBy(desc(knowledge.isTop), knowledge.name)\n .execute();\n\n // 使用 tree-builder 构建树形结构\n const { buildTree } = await import(\"../utils/tree-builder\");\n return buildTree(allNodes, parentId);\n}\n\n/**\n * 获取子节点列表\n */\nexport async function getChildren(parentId: number) {\n const db = getDB();\n\n const [parent] = await db\n .select()\n .from(knowledge)\n .where(and(eq(knowledge.id, parentId), eq(knowledge.isDelete, 0)))\n .limit(1);\n\n if (!parent) {\n throw new Error(\"父节点不存在\");\n }\n\n return await db\n .select()\n .from(knowledge)\n .where(\n and(\n eq(knowledge.isDelete, 0),\n like(knowledge.path, `${parent.path}%`),\n eq(knowledge.level, parent.level + 1)\n )\n )\n .orderBy(desc(knowledge.isTop), knowledge.name)\n .execute();\n}\n\n/**\n * 更新知识库内容\n */\nexport async function updateKnowledge(id: number, data: Partial<InsertKnowledge>, userId: number) {\n const db = getDB();\n const now = new Date();\n\n const result = await db\n .update(knowledge)\n .set({\n ...(data.name !== undefined && { name: data.name }),\n ...(data.description !== undefined && { description: data.description }),\n ...(data.content !== undefined && { content: data.content }),\n ...(data.contentText !== undefined && { contentText: data.contentText }),\n ...(data.viewType !== undefined && { viewType: data.viewType }),\n ...(data.coverImage !== undefined && { coverImage: data.coverImage }),\n ...(data.author !== undefined && { author: data.author }),\n ...(data.isbn !== undefined && { isbn: data.isbn }),\n updated: now,\n })\n .where(and(eq(knowledge.id, id), eq(knowledge.userId, userId)))\n .execute();\n\n return result;\n}\n\n/**\n * 移动节点(更新整个子树的 path 和 level)\n */\nexport async function moveKnowledge(nodeId: number, newParentId: number, userId: number) {\n const db = getDB();\n\n // 1. 获取节点和新父节点信息\n const [node] = await db\n .select()\n .from(knowledge)\n .where(and(eq(knowledge.id, nodeId), eq(knowledge.isDelete, 0)))\n .limit(1);\n\n const [newParent] = await db\n .select()\n .from(knowledge)\n .where(and(eq(knowledge.id, newParentId), eq(knowledge.isDelete, 0)))\n .limit(1);\n\n if (!node || !newParent) {\n throw new Error(\"节点不存在\");\n }\n\n // 权限检查\n if (node.userId !== userId) {\n throw new Error(\"无权操作此节点\");\n }\n\n // 2. 防止循环:不能移动到自己的后代节点\n if (isDescendant(node.path, newParent.path)) {\n throw new Error(\"不能移动到自己的后代节点\");\n }\n\n const oldPath = node.path;\n const newPath = buildPath(newParent.path, nodeId);\n const levelDiff = newParent.level + 1 - node.level;\n\n // 3. 批量更新整个子树\n await db\n .update(knowledge)\n .set({\n path: sql`REPLACE(${knowledge.path}, ${oldPath}, ${newPath})`,\n level: sql`${knowledge.level} + ${levelDiff}`,\n })\n .where(or(like(knowledge.path, `${oldPath}%`), eq(knowledge.path, oldPath)))\n .execute();\n\n return getKnowledgeById(nodeId);\n}\n\n/**\n * 更新置顶状态\n */\nexport async function updateIsTop(id: number, isTop: number, userId: number) {\n const db = getDB();\n const now = new Date();\n\n await db\n .update(knowledge)\n .set({ isTop, updated: now })\n .where(and(eq(knowledge.id, id), eq(knowledge.userId, userId)))\n .execute();\n\n return getKnowledgeById(id);\n}\n\n/**\n * 更新安全等级\n */\nexport async function updateSafe(id: number, safe: KnowledgeSafe, userId: number) {\n const db = getDB();\n const now = new Date();\n\n await db\n .update(knowledge)\n .set({ safe, updated: now })\n .where(and(eq(knowledge.id, id), eq(knowledge.userId, userId)))\n .execute();\n\n return getKnowledgeById(id);\n}\n\n/**\n * 逻辑删除\n */\nexport async function deleteKnowledge(id: number, userId: number) {\n const db = getDB();\n const now = new Date();\n\n await db\n .update(knowledge)\n .set({ isDelete: 1, updated: now })\n .where(and(eq(knowledge.id, id), eq(knowledge.userId, userId)))\n .execute();\n\n return { success: true };\n}\n\n/**\n * 真实删除(管理员)\n */\nexport async function deleteKnowledgeReal(id: number) {\n const db = getDB();\n\n await db\n .delete(knowledge)\n .where(eq(knowledge.id, id))\n .execute();\n\n return { success: true };\n}\n\n/**\n * 搜索知识库内容\n */\nexport async function searchKnowledge(options: {\n keyword: string;\n pageIndex?: number;\n pageSize?: number;\n safe?: number[];\n userId?: number;\n isAdmin?: boolean;\n}) {\n const db = getDB();\n const { keyword, pageIndex = 1, pageSize = 10, safe = [0], userId, isAdmin } = options;\n\n let conditions = [\n eq(knowledge.isDelete, 0),\n or(\n like(knowledge.name, `%${keyword}%`),\n like(knowledge.description, `%${keyword}%`),\n like(knowledge.contentText, `%${keyword}%`)\n ),\n ];\n\n // 权限筛选\n const safeConditions = [];\n for (const s of safe) {\n switch (s) {\n case 0: // 公开\n safeConditions.push(eq(knowledge.safe, s));\n break;\n case 1: // 登录共享\n if (userId) {\n safeConditions.push(eq(knowledge.safe, s));\n }\n break;\n case 2: // 个人私密\n if (userId) {\n safeConditions.push(\n and(eq(knowledge.safe, s), isAdmin ? sql`1=1` : eq(knowledge.userId, userId))\n );\n }\n break;\n }\n }\n\n if (safeConditions.length > 0) {\n conditions.push(or(...safeConditions));\n }\n\n const offset = (pageIndex - 1) * pageSize;\n\n const list = await db\n .select()\n .from(knowledge)\n .where(and(...conditions))\n .limit(pageSize)\n .offset(offset)\n .orderBy(desc(knowledge.created))\n .execute();\n\n const [totalResult] = await db\n .select({ count: sql<number>`count(*)` })\n .from(knowledge)\n .where(and(...conditions))\n .execute();\n\n return {\n list,\n total: totalResult.count,\n pageIndex,\n pageSize,\n };\n}\n","/**\n * 物化路径工具函数\n */\n\n/**\n * 构建新节点路径\n * @param parentPath 父节点路径\n * @param id 新节点ID\n * @returns 新节点路径\n */\nexport function buildPath(parentPath: string, id: number): string {\n return `${parentPath}${id}/`;\n}\n\n/**\n * 从路径计算层级\n * @param path 节点路径\n * @returns 层级深度(根节点为0)\n */\nexport function getLevel(path: string): number {\n // path 格式: \"/1/3/7/\" -> 分割后 ['', '1', '3', '7', '']\n // 减2是因为前后各有一个空字符串\n const parts = path.split(\"/\").filter(Boolean);\n return parts.length - 1;\n}\n\n/**\n * 验证路径是否有效(防止循环引用)\n * @param newPath 新路径\n * @param oldPath 旧路径\n * @returns 是否有效\n */\nexport function validatePath(newPath: string, oldPath: string): boolean {\n // 不能将节点移动到自己的后代节点下\n // 如果新父节点的 path 以旧节点的 path 开头,说明是后代\n return !newPath.startsWith(oldPath);\n}\n\n/**\n * 检查是否为后代节点\n * @param ancestorPath 祖先节点路径\n * @param descendantPath 后代节点路径\n * @returns 是否为后代\n */\nexport function isDescendant(ancestorPath: string, descendantPath: string): boolean {\n return descendantPath.startsWith(ancestorPath) && ancestorPath !== descendantPath;\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  knowledgeTag,
3
3
  knowledgeTagRelation
4
- } from "./chunk-S3ZXTYZ2.mjs";
4
+ } from "./chunk-SG44KRK4.mjs";
5
5
  import "./chunk-GWIBTASJ.mjs";
6
6
  import "./chunk-VHBDNZOQ.mjs";
7
7
 
@@ -109,7 +109,7 @@ async function getKnowledgeByTag(tagId, options) {
109
109
  return { list: [], total: 0, pageIndex, pageSize };
110
110
  }
111
111
  const knowledgeIds = relations.map((r) => r.knowledgeId);
112
- const { knowledge } = await import("./Knowledge.schema-7KD5X7KG.mjs");
112
+ const { knowledge } = await import("./Knowledge.schema-BHFPDLIO.mjs");
113
113
  const { eq: eq2, and: and2, or, sql, desc } = await import("drizzle-orm");
114
114
  let conditions = [
115
115
  eq2(knowledge.isDelete, 0),
@@ -160,4 +160,4 @@ export {
160
160
  removeTagFromKnowledge,
161
161
  updateTag
162
162
  };
163
- //# sourceMappingURL=TagService-VX6SB3S6.mjs.map
163
+ //# sourceMappingURL=TagService-YZAGBPG7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../service/TagService.ts"],"sourcesContent":["import { knowledgeTag, knowledgeTagRelation } from \"../orm/Tag.schema\";\nimport { eq, and, inArray } from \"drizzle-orm\";\nimport type { InsertTag } from \"../types\";\n\nlet _db: any;\n\n/**\n * 初始化 Service 的数据库实例\n */\nexport function initTagService(db: any) {\n _db = db;\n}\n\nfunction getDB() {\n if (!_db) throw new Error(\"TagService not initialized with DB\");\n return _db;\n}\n\n// ==================== 标签管理 ====================\n\n/**\n * 创建标签\n */\nexport async function createTag(data: InsertTag & { userId?: number }) {\n const db = getDB();\n const now = new Date();\n\n const result = await db\n .insert(knowledgeTag)\n .values({\n name: data.name,\n color: data.color,\n userId: data.userId,\n created: now,\n })\n .execute();\n\n const lastId = result.lastInsertRowid as number;\n return getTagById(lastId);\n}\n\n/**\n * 获取标签详情\n */\nexport async function getTagById(id: number) {\n const db = getDB();\n const [tag] = await db\n .select()\n .from(knowledgeTag)\n .where(eq(knowledgeTag.id, id))\n .limit(1);\n\n return tag || null;\n}\n\n/**\n * 获取标签列表\n */\nexport async function listTags(userId?: number) {\n const db = getDB();\n\n let conditions = [];\n if (userId !== undefined) {\n conditions.push(eq(knowledgeTag.userId, userId));\n }\n\n return await db\n .select()\n .from(knowledgeTag)\n .where(conditions.length > 0 ? and(...conditions) : undefined)\n .orderBy(knowledgeTag.name)\n .execute();\n}\n\n/**\n * 更新标签\n */\nexport async function updateTag(id: number, data: Partial<InsertTag>, userId: number) {\n const db = getDB();\n\n // 检查权限\n const [tag] = await db\n .select()\n .from(knowledgeTag)\n .where(and(eq(knowledgeTag.id, id), eq(knowledgeTag.userId, userId)))\n .limit(1);\n\n if (!tag) {\n throw new Error(\"标签不存在或无权操作\");\n }\n\n await db\n .update(knowledgeTag)\n .set({\n ...(data.name !== undefined && { name: data.name }),\n ...(data.color !== undefined && { color: data.color }),\n })\n .where(eq(knowledgeTag.id, id))\n .execute();\n\n return getTagById(id);\n}\n\n/**\n * 删除标签\n */\nexport async function deleteTag(id: number, userId: number) {\n const db = getDB();\n\n // 检查权限\n const [tag] = await db\n .select()\n .from(knowledgeTag)\n .where(and(eq(knowledgeTag.id, id), eq(knowledgeTag.userId, userId)))\n .limit(1);\n\n if (!tag) {\n throw new Error(\"标签不存在或无权操作\");\n }\n\n // 删除标签关联\n await db\n .delete(knowledgeTagRelation)\n .where(eq(knowledgeTagRelation.tagId, id))\n .execute();\n\n // 删除标签\n await db\n .delete(knowledgeTag)\n .where(eq(knowledgeTag.id, id))\n .execute();\n\n return { success: true };\n}\n\n// ==================== 标签关联 ====================\n\n/**\n * 为知识库内容添加标签\n */\nexport async function addTagToKnowledge(knowledgeId: number, tagId: number, userId: number) {\n const db = getDB();\n\n // 检查标签是否存在\n const [tag] = await db\n .select()\n .from(knowledgeTag)\n .where(eq(knowledgeTag.id, tagId))\n .limit(1);\n\n if (!tag) {\n throw new Error(\"标签不存在\");\n }\n\n // 检查是否已存在关联\n const [existing] = await db\n .select()\n .from(knowledgeTagRelation)\n .where(\n and(\n eq(knowledgeTagRelation.knowledgeId, knowledgeId),\n eq(knowledgeTagRelation.tagId, tagId)\n )\n )\n .limit(1);\n\n if (existing) {\n return { success: true, message: \"标签已存在\" };\n }\n\n // 创建关联\n await db\n .insert(knowledgeTagRelation)\n .values({\n knowledgeId,\n tagId,\n })\n .execute();\n\n return { success: true };\n}\n\n/**\n * 移除知识库内容的标签\n */\nexport async function removeTagFromKnowledge(knowledgeId: number, tagId: number) {\n const db = getDB();\n\n await db\n .delete(knowledgeTagRelation)\n .where(\n and(\n eq(knowledgeTagRelation.knowledgeId, knowledgeId),\n eq(knowledgeTagRelation.tagId, tagId)\n )\n )\n .execute();\n\n return { success: true };\n}\n\n/**\n * 获取知识库内容的标签列表\n */\nexport async function getKnowledgeTags(knowledgeId: number) {\n const db = getDB();\n\n return await db\n .select({\n id: knowledgeTag.id,\n name: knowledgeTag.name,\n color: knowledgeTag.color,\n })\n .from(knowledgeTagRelation)\n .innerJoin(knowledgeTag, eq(knowledgeTagRelation.tagId, knowledgeTag.id))\n .where(eq(knowledgeTagRelation.knowledgeId, knowledgeId))\n .execute();\n}\n\n/**\n * 按标签查询知识库内容\n */\nexport async function getKnowledgeByTag(tagId: number, options: {\n pageIndex?: number;\n pageSize?: number;\n safe?: number[];\n userId?: number;\n isAdmin?: boolean;\n}) {\n const db = getDB();\n const { pageIndex = 1, pageSize = 10, safe = [0], userId, isAdmin } = options;\n\n // 获取带有该标签的知识库 ID 列表\n const relations = await db\n .select()\n .from(knowledgeTagRelation)\n .where(eq(knowledgeTagRelation.tagId, tagId))\n .execute();\n\n if (relations.length === 0) {\n return { list: [], total: 0, pageIndex, pageSize };\n }\n\n const knowledgeIds = relations.map(r => r.knowledgeId);\n\n // 导入 knowledge 表\n const { knowledge } = await import(\"../orm/Knowledge.schema\");\n const { eq, and, or, sql, desc } = await import(\"drizzle-orm\");\n\n let conditions = [\n eq(knowledge.isDelete, 0),\n inArray(knowledge.id, knowledgeIds),\n ];\n\n // 权限筛选\n const safeConditions = [];\n for (const s of safe) {\n switch (s) {\n case 0: // 公开\n safeConditions.push(eq(knowledge.safe, s));\n break;\n case 1: // 登录共享\n if (userId) {\n safeConditions.push(eq(knowledge.safe, s));\n }\n break;\n case 2: // 个人私密\n if (userId) {\n safeConditions.push(\n and(eq(knowledge.safe, s), isAdmin ? sql`1=1` : eq(knowledge.userId, userId))\n );\n }\n break;\n }\n }\n\n if (safeConditions.length > 0) {\n conditions.push(or(...safeConditions));\n }\n\n const offset = (pageIndex - 1) * pageSize;\n\n const list = await db\n .select()\n .from(knowledge)\n .where(and(...conditions))\n .limit(pageSize)\n .offset(offset)\n .orderBy(desc(knowledge.created))\n .execute();\n\n const [totalResult] = await db\n .select({ count: sql<number>`count(*)` })\n .from(knowledge)\n .where(and(...conditions))\n .execute();\n\n return {\n list,\n total: totalResult.count,\n pageIndex,\n pageSize,\n };\n}\n"],"mappings":"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","names":["eq","and"]}
@@ -90,4 +90,4 @@ export {
90
90
  updateIsTopSchema,
91
91
  updateSafeSchema
92
92
  };
93
- //# sourceMappingURL=chunk-Y47QRG3M.mjs.map
93
+ //# sourceMappingURL=chunk-2G44ILZL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../orm/Knowledge.schema.ts"],"sourcesContent":["import { sqliteTable, text, integer } from \"drizzle-orm/sqlite-core\";\nimport { createInsertSchema, createSelectSchema } from \"drizzle-zod\";\nimport { z } from \"zod\";\n\n/**\n * 知识库主表\n */\nexport const knowledge = sqliteTable(\"knowledge\", {\n id: integer(\"id\").primaryKey({ autoIncrement: true }),\n name: text(\"name\").notNull(),\n path: text(\"path\").notNull(),\n level: integer(\"level\").notNull().default(0),\n type: integer(\"type\").notNull(), // 0:文件夹 1:笔记 2:书籍\n safe: integer(\"safe\").notNull().default(0),\n userId: integer(\"user_id\").notNull(),\n // 书籍特有字段\n coverImage: text(\"cover_image\"),\n author: text(\"author\"),\n isbn: text(\"isbn\"),\n description: text(\"description\"),\n created: integer(\"created\", { mode: \"timestamp\" }),\n updated: integer(\"updated\", { mode: \"timestamp\" }),\n isDelete: integer(\"is_delete\").notNull().default(0),\n isTop: integer(\"is_top\").notNull().default(0),\n content: text(\"content\"),\n contentText: text(\"content_text\"),\n viewType: integer(\"view_type\").default(0),\n});\n\n// ==================== Zod Schemas ====================\n\n/**\n * 创建知识库内容验证\n */\nexport const createKnowledgeSchema = createInsertSchema(knowledge as any, {\n name: z.string().min(1, \"名称不能为空\").max(255, \"名称不能超过255字符\") as any,\n type: z.coerce.number().int().min(0).max(2) as any,\n safe: z.coerce.number().int().min(0).max(2).optional() as any,\n}).pick({\n name: true,\n type: true,\n safe: true,\n description: true,\n content: true,\n contentText: true,\n viewType: true,\n coverImage: true,\n author: true,\n isbn: true,\n});\n\n/**\n * 批量创建验证\n */\nexport const batchCreateKnowledgeSchema = z.object({\n parentId: z.number().optional(),\n items: z.array(z.object({\n name: z.string().min(1, \"名称不能为空\").max(255),\n type: z.coerce.number().int().min(0).max(2),\n })).min(1, \"至少需要一个项目\"),\n});\n\n/**\n * 批量获取详情验证\n */\nexport const batchDetailKnowledgeSchema = z.object({\n ids: z.array(z.number().int().positive()).min(1, \"至少需要一个ID\"),\n});\n\n/**\n * 更新知识库内容验证\n */\nexport const updateKnowledgeSchema = createInsertSchema(knowledge as any)\n .partial()\n .extend({\n id: z.number({ required_error: \"ID不能为空\" }),\n });\n\n/**\n * 列表查询验证\n */\nexport const listKnowledgeSchema = z.object({\n pageIndex: z.coerce.number().int().positive().optional().default(1),\n pageSize: z.coerce.number().int().positive().optional().default(10),\n level: z.coerce.number().int().min(0).optional(),\n type: z.coerce.number().int().min(0).max(2).optional(),\n safe: z.array(z.coerce.number().int().min(0).max(2)).optional(),\n});\n\n/**\n * 搜索验证\n */\nexport const searchKnowledgeSchema = z.object({\n keyword: z.string().min(1, \"关键词不能为空\"),\n pageIndex: z.coerce.number().int().positive().optional().default(1),\n pageSize: z.coerce.number().int().positive().optional().default(10),\n});\n\n/**\n * 移动节点验证\n */\nexport const moveKnowledgeSchema = z.object({\n newParentId: z.number({ required_error: \"新父节点ID不能为空\" }),\n});\n\n/**\n * 更新置顶状态验证\n */\nexport const updateIsTopSchema = z.object({\n isTop: z.coerce.number().int().min(0).max(1),\n});\n\n/**\n * 更新安全等级验证\n */\nexport const updateSafeSchema = z.object({\n safe: z.coerce.number().int().min(0).max(2),\n});\n"],"mappings":"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","names":[]}
@@ -37,4 +37,4 @@ export {
37
37
  updateTagSchema,
38
38
  addTagRelationSchema
39
39
  };
40
- //# sourceMappingURL=chunk-S3ZXTYZ2.mjs.map
40
+ //# sourceMappingURL=chunk-SG44KRK4.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../orm/Tag.schema.ts"],"sourcesContent":["import { sqliteTable, text, integer } from \"drizzle-orm/sqlite-core\";\nimport { createInsertSchema, createSelectSchema } from \"drizzle-zod\";\nimport { z } from \"zod\";\n\n/**\n * 标签表\n */\nexport const knowledgeTag = sqliteTable(\"knowledge_tag\", {\n id: integer(\"id\").primaryKey({ autoIncrement: true }),\n name: text(\"name\").notNull().unique(),\n color: text(\"color\"),\n userId: integer(\"user_id\"),\n created: integer(\"created\", { mode: \"timestamp\" }),\n});\n\n/**\n * 标签关联表(多对多)\n */\nexport const knowledgeTagRelation = sqliteTable(\"knowledge_tag_relation\", {\n knowledgeId: integer(\"knowledge_id\").notNull(),\n tagId: integer(\"tag_id\").notNull(),\n});\n\n// ==================== Zod Schemas ====================\n\n/**\n * 创建标签验证\n */\nexport const createTagSchema = createInsertSchema(knowledgeTag as any, {\n name: z.string().min(1, \"标签名不能为空\").max(50, \"标签名不能超过50字符\") as any,\n color: z.string().regex(/^#[0-9A-Fa-f]{6}$/, \"颜色必须是十六进制格式\").optional() as any,\n}).pick({\n name: true,\n color: true,\n});\n\n/**\n * 更新标签验证\n */\nexport const updateTagSchema = createInsertSchema(knowledgeTag as any)\n .partial()\n .extend({\n id: z.number({ required_error: \"标签ID不能为空\" }),\n });\n\n/**\n * 添加标签关联验证\n */\nexport const addTagRelationSchema = z.object({\n tagId: z.number({ required_error: \"标签ID不能为空\" }),\n});\n"],"mappings":";;;;;AAAA,SAAS,aAAa,MAAM,eAAe;AAE3C,SAAS,SAAS;AAKX,IAAM,eAAe,YAAY,iBAAiB;AAAA,EACvD,IAAI,QAAQ,IAAI,EAAE,WAAW,EAAE,eAAe,KAAK,CAAC;AAAA,EACpD,MAAM,KAAK,MAAM,EAAE,QAAQ,EAAE,OAAO;AAAA,EACpC,OAAO,KAAK,OAAO;AAAA,EACnB,QAAQ,QAAQ,SAAS;AAAA,EACzB,SAAS,QAAQ,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,CAAC;AAKM,IAAM,uBAAuB,YAAY,0BAA0B;AAAA,EACxE,aAAa,QAAQ,cAAc,EAAE,QAAQ;AAAA,EAC7C,OAAO,QAAQ,QAAQ,EAAE,QAAQ;AACnC,CAAC;AAOM,IAAM,kBAAkB,EAAmB,cAAqB;AAAA,EACrE,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,4CAAS,EAAE,IAAI,IAAI,0DAAa;AAAA,EACxD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,oEAAa,EAAE,SAAS;AACvE,CAAC,EAAE,KAAK;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT,CAAC;AAKM,IAAM,kBAAkB,EAAmB,YAAmB,EAClE,QAAQ,EACR,OAAO;AAAA,EACN,IAAI,EAAE,OAAO,EAAE,gBAAgB,yCAAW,CAAC;AAC7C,CAAC;AAKI,IAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,OAAO,EAAE,OAAO,EAAE,gBAAgB,yCAAW,CAAC;AAChD,CAAC;","names":[]}
package/dist/index.d.mts CHANGED
@@ -3,14 +3,8 @@ import * as drizzle_orm_sqlite_core from 'drizzle-orm/sqlite-core';
3
3
  import { z } from 'zod';
4
4
 
5
5
  interface MindBaseAppOptions {
6
- /**
7
- * 启动模式:
8
- * - normal: 正常启动服务
9
- * - sync: 同步数据库 Schema(仅收集)
10
- * - migrate: 执行数据库迁移
11
- * @default "normal"
12
- */
13
- mode?: "normal" | "sync" | "migrate";
6
+ /** 服务监听主机名 @default 127.0.0.1 */
7
+ host?: string;
14
8
  /** 服务监听端口 @default 3000 */
15
9
  port?: number;
16
10
  /** 是否启用请求日志 @default true */
@@ -92,7 +86,7 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
92
86
  baseColumn: never;
93
87
  identity: undefined;
94
88
  generated: undefined;
95
- }, object>;
89
+ }, {}, {}>;
96
90
  name: drizzle_orm_sqlite_core.SQLiteColumn<{
97
91
  name: "name";
98
92
  tableName: "knowledge";
@@ -109,7 +103,9 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
109
103
  baseColumn: never;
110
104
  identity: undefined;
111
105
  generated: undefined;
112
- }, object>;
106
+ }, {}, {
107
+ length: number;
108
+ }>;
113
109
  path: drizzle_orm_sqlite_core.SQLiteColumn<{
114
110
  name: "path";
115
111
  tableName: "knowledge";
@@ -126,7 +122,9 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
126
122
  baseColumn: never;
127
123
  identity: undefined;
128
124
  generated: undefined;
129
- }, object>;
125
+ }, {}, {
126
+ length: number;
127
+ }>;
130
128
  level: drizzle_orm_sqlite_core.SQLiteColumn<{
131
129
  name: "level";
132
130
  tableName: "knowledge";
@@ -143,7 +141,7 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
143
141
  baseColumn: never;
144
142
  identity: undefined;
145
143
  generated: undefined;
146
- }, object>;
144
+ }, {}, {}>;
147
145
  type: drizzle_orm_sqlite_core.SQLiteColumn<{
148
146
  name: "type";
149
147
  tableName: "knowledge";
@@ -160,7 +158,7 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
160
158
  baseColumn: never;
161
159
  identity: undefined;
162
160
  generated: undefined;
163
- }, object>;
161
+ }, {}, {}>;
164
162
  safe: drizzle_orm_sqlite_core.SQLiteColumn<{
165
163
  name: "safe";
166
164
  tableName: "knowledge";
@@ -177,7 +175,7 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
177
175
  baseColumn: never;
178
176
  identity: undefined;
179
177
  generated: undefined;
180
- }, object>;
178
+ }, {}, {}>;
181
179
  userId: drizzle_orm_sqlite_core.SQLiteColumn<{
182
180
  name: "user_id";
183
181
  tableName: "knowledge";
@@ -194,7 +192,7 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
194
192
  baseColumn: never;
195
193
  identity: undefined;
196
194
  generated: undefined;
197
- }, object>;
195
+ }, {}, {}>;
198
196
  coverImage: drizzle_orm_sqlite_core.SQLiteColumn<{
199
197
  name: "cover_image";
200
198
  tableName: "knowledge";
@@ -211,7 +209,9 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
211
209
  baseColumn: never;
212
210
  identity: undefined;
213
211
  generated: undefined;
214
- }, object>;
212
+ }, {}, {
213
+ length: number;
214
+ }>;
215
215
  author: drizzle_orm_sqlite_core.SQLiteColumn<{
216
216
  name: "author";
217
217
  tableName: "knowledge";
@@ -228,7 +228,9 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
228
228
  baseColumn: never;
229
229
  identity: undefined;
230
230
  generated: undefined;
231
- }, object>;
231
+ }, {}, {
232
+ length: number;
233
+ }>;
232
234
  isbn: drizzle_orm_sqlite_core.SQLiteColumn<{
233
235
  name: "isbn";
234
236
  tableName: "knowledge";
@@ -245,7 +247,9 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
245
247
  baseColumn: never;
246
248
  identity: undefined;
247
249
  generated: undefined;
248
- }, object>;
250
+ }, {}, {
251
+ length: number;
252
+ }>;
249
253
  description: drizzle_orm_sqlite_core.SQLiteColumn<{
250
254
  name: "description";
251
255
  tableName: "knowledge";
@@ -262,7 +266,9 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
262
266
  baseColumn: never;
263
267
  identity: undefined;
264
268
  generated: undefined;
265
- }, object>;
269
+ }, {}, {
270
+ length: number;
271
+ }>;
266
272
  created: drizzle_orm_sqlite_core.SQLiteColumn<{
267
273
  name: "created";
268
274
  tableName: "knowledge";
@@ -279,7 +285,7 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
279
285
  baseColumn: never;
280
286
  identity: undefined;
281
287
  generated: undefined;
282
- }, object>;
288
+ }, {}, {}>;
283
289
  updated: drizzle_orm_sqlite_core.SQLiteColumn<{
284
290
  name: "updated";
285
291
  tableName: "knowledge";
@@ -296,7 +302,7 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
296
302
  baseColumn: never;
297
303
  identity: undefined;
298
304
  generated: undefined;
299
- }, object>;
305
+ }, {}, {}>;
300
306
  isDelete: drizzle_orm_sqlite_core.SQLiteColumn<{
301
307
  name: "is_delete";
302
308
  tableName: "knowledge";
@@ -313,7 +319,7 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
313
319
  baseColumn: never;
314
320
  identity: undefined;
315
321
  generated: undefined;
316
- }, object>;
322
+ }, {}, {}>;
317
323
  isTop: drizzle_orm_sqlite_core.SQLiteColumn<{
318
324
  name: "is_top";
319
325
  tableName: "knowledge";
@@ -330,7 +336,7 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
330
336
  baseColumn: never;
331
337
  identity: undefined;
332
338
  generated: undefined;
333
- }, object>;
339
+ }, {}, {}>;
334
340
  content: drizzle_orm_sqlite_core.SQLiteColumn<{
335
341
  name: "content";
336
342
  tableName: "knowledge";
@@ -347,7 +353,9 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
347
353
  baseColumn: never;
348
354
  identity: undefined;
349
355
  generated: undefined;
350
- }, object>;
356
+ }, {}, {
357
+ length: number;
358
+ }>;
351
359
  contentText: drizzle_orm_sqlite_core.SQLiteColumn<{
352
360
  name: "content_text";
353
361
  tableName: "knowledge";
@@ -364,7 +372,9 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
364
372
  baseColumn: never;
365
373
  identity: undefined;
366
374
  generated: undefined;
367
- }, object>;
375
+ }, {}, {
376
+ length: number;
377
+ }>;
368
378
  viewType: drizzle_orm_sqlite_core.SQLiteColumn<{
369
379
  name: "view_type";
370
380
  tableName: "knowledge";
@@ -381,7 +391,7 @@ declare const knowledge: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
381
391
  baseColumn: never;
382
392
  identity: undefined;
383
393
  generated: undefined;
384
- }, object>;
394
+ }, {}, {}>;
385
395
  };
386
396
  dialect: "sqlite";
387
397
  }>;
@@ -3991,7 +4001,7 @@ declare const knowledgeTag: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
3991
4001
  baseColumn: never;
3992
4002
  identity: undefined;
3993
4003
  generated: undefined;
3994
- }, object>;
4004
+ }, {}, {}>;
3995
4005
  name: drizzle_orm_sqlite_core.SQLiteColumn<{
3996
4006
  name: "name";
3997
4007
  tableName: "knowledge_tag";
@@ -4008,7 +4018,9 @@ declare const knowledgeTag: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
4008
4018
  baseColumn: never;
4009
4019
  identity: undefined;
4010
4020
  generated: undefined;
4011
- }, object>;
4021
+ }, {}, {
4022
+ length: number;
4023
+ }>;
4012
4024
  color: drizzle_orm_sqlite_core.SQLiteColumn<{
4013
4025
  name: "color";
4014
4026
  tableName: "knowledge_tag";
@@ -4025,7 +4037,9 @@ declare const knowledgeTag: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
4025
4037
  baseColumn: never;
4026
4038
  identity: undefined;
4027
4039
  generated: undefined;
4028
- }, object>;
4040
+ }, {}, {
4041
+ length: number;
4042
+ }>;
4029
4043
  userId: drizzle_orm_sqlite_core.SQLiteColumn<{
4030
4044
  name: "user_id";
4031
4045
  tableName: "knowledge_tag";
@@ -4042,7 +4056,7 @@ declare const knowledgeTag: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
4042
4056
  baseColumn: never;
4043
4057
  identity: undefined;
4044
4058
  generated: undefined;
4045
- }, object>;
4059
+ }, {}, {}>;
4046
4060
  created: drizzle_orm_sqlite_core.SQLiteColumn<{
4047
4061
  name: "created";
4048
4062
  tableName: "knowledge_tag";
@@ -4059,7 +4073,7 @@ declare const knowledgeTag: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
4059
4073
  baseColumn: never;
4060
4074
  identity: undefined;
4061
4075
  generated: undefined;
4062
- }, object>;
4076
+ }, {}, {}>;
4063
4077
  };
4064
4078
  dialect: "sqlite";
4065
4079
  }>;
@@ -4086,7 +4100,7 @@ declare const knowledgeTagRelation: drizzle_orm_sqlite_core.SQLiteTableWithColum
4086
4100
  baseColumn: never;
4087
4101
  identity: undefined;
4088
4102
  generated: undefined;
4089
- }, object>;
4103
+ }, {}, {}>;
4090
4104
  tagId: drizzle_orm_sqlite_core.SQLiteColumn<{
4091
4105
  name: "tag_id";
4092
4106
  tableName: "knowledge_tag_relation";
@@ -4103,7 +4117,7 @@ declare const knowledgeTagRelation: drizzle_orm_sqlite_core.SQLiteTableWithColum
4103
4117
  baseColumn: never;
4104
4118
  identity: undefined;
4105
4119
  generated: undefined;
4106
- }, object>;
4120
+ }, {}, {}>;
4107
4121
  };
4108
4122
  dialect: "sqlite";
4109
4123
  }>;
package/dist/index.mjs CHANGED
@@ -9,14 +9,14 @@ import {
9
9
  updateIsTopSchema,
10
10
  updateKnowledgeSchema,
11
11
  updateSafeSchema
12
- } from "./chunk-Y47QRG3M.mjs";
12
+ } from "./chunk-2G44ILZL.mjs";
13
13
  import {
14
14
  addTagRelationSchema,
15
15
  createTagSchema,
16
16
  knowledgeTag,
17
17
  knowledgeTagRelation,
18
18
  updateTagSchema
19
- } from "./chunk-S3ZXTYZ2.mjs";
19
+ } from "./chunk-SG44KRK4.mjs";
20
20
  import "./chunk-GWIBTASJ.mjs";
21
21
  import {
22
22
  __filename
@@ -28,8 +28,8 @@ import { logger } from "@mindbase/express-common";
28
28
  var KnowledgeModule_default = createModule(async (app) => {
29
29
  const db = app.getDB();
30
30
  logger.info("[KnowledgeModule] \u521D\u59CB\u5316 knowledge \u6A21\u5757");
31
- const { initKnowledgeService } = await import("./KnowledgeService-YZHS7UHY.mjs");
32
- const { initTagService } = await import("./TagService-VX6SB3S6.mjs");
31
+ const { initKnowledgeService } = await import("./KnowledgeService-JDG23Q4Z.mjs");
32
+ const { initTagService } = await import("./TagService-YZAGBPG7.mjs");
33
33
  initKnowledgeService(db);
34
34
  initTagService(db);
35
35
  logger.info("[KnowledgeModule] \u6A21\u5757\u52A0\u8F7D\u5B8C\u6210");
@@ -32,4 +32,4 @@ function buildNodeTree(node, allNodes) {
32
32
  export {
33
33
  buildTree
34
34
  };
35
- //# sourceMappingURL=tree-builder-XZ4OA2TD.mjs.map
35
+ //# sourceMappingURL=tree-builder-LIYCLUJS.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../utils/tree-builder.ts"],"sourcesContent":["import type { KnowledgeTreeNode, Knowledge } from \"../types\";\n\n/**\n * 将扁平数组转换为树形结构\n * @param nodes 扁平节点数组\n * @param parentId 父节点ID(可选,用于构建子树)\n * @returns 树形结构\n */\nexport function buildTree(\n nodes: Knowledge[],\n parentId: number | null = null\n): KnowledgeTreeNode[] {\n // 按 isTop 降序、created 降序排序\n const sortedNodes = [...nodes].sort((a, b) => {\n if (a.isTop !== b.isTop) {\n return b.isTop - a.isTop;\n }\n return b.created.getTime() - a.created.getTime();\n });\n\n // 如果指定了 parentId,先找到该节点\n if (parentId !== null) {\n const parent = sortedNodes.find(n => n.id === parentId);\n if (!parent) return [];\n\n // 查找该节点的直接子节点\n const children = sortedNodes.filter(n =>\n n.path.startsWith(parent.path) &&\n n.level === parent.level + 1\n );\n\n return children.map(child => buildNodeTree(child, sortedNodes));\n }\n\n // 否则返回根节点(level=0)的树\n const rootNodes = sortedNodes.filter(n => n.level === 0);\n return rootNodes.map(node => buildNodeTree(node, sortedNodes));\n}\n\n/**\n * 递归构建节点树\n */\nfunction buildNodeTree(node: Knowledge, allNodes: Knowledge[]): KnowledgeTreeNode {\n const treeNode: KnowledgeTreeNode = { ...node };\n\n // 查找直接子节点\n const children = allNodes.filter(n =>\n n.path.startsWith(node.path) &&\n n.level === node.level + 1\n );\n\n if (children.length > 0) {\n treeNode.children = children.map(child => buildNodeTree(child, allNodes));\n }\n\n return treeNode;\n}\n"],"mappings":";;;AAQO,SAAS,UACd,OACA,WAA0B,MACL;AAErB,QAAM,cAAc,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM;AAC5C,QAAI,EAAE,UAAU,EAAE,OAAO;AACvB,aAAO,EAAE,QAAQ,EAAE;AAAA,IACrB;AACA,WAAO,EAAE,QAAQ,QAAQ,IAAI,EAAE,QAAQ,QAAQ;AAAA,EACjD,CAAC;AAGD,MAAI,aAAa,MAAM;AACrB,UAAM,SAAS,YAAY,KAAK,OAAK,EAAE,OAAO,QAAQ;AACtD,QAAI,CAAC,OAAQ,QAAO,CAAC;AAGrB,UAAM,WAAW,YAAY;AAAA,MAAO,OAClC,EAAE,KAAK,WAAW,OAAO,IAAI,KAC7B,EAAE,UAAU,OAAO,QAAQ;AAAA,IAC7B;AAEA,WAAO,SAAS,IAAI,WAAS,cAAc,OAAO,WAAW,CAAC;AAAA,EAChE;AAGA,QAAM,YAAY,YAAY,OAAO,OAAK,EAAE,UAAU,CAAC;AACvD,SAAO,UAAU,IAAI,UAAQ,cAAc,MAAM,WAAW,CAAC;AAC/D;AAKA,SAAS,cAAc,MAAiB,UAA0C;AAChF,QAAM,WAA8B,EAAE,GAAG,KAAK;AAG9C,QAAM,WAAW,SAAS;AAAA,IAAO,OAC/B,EAAE,KAAK,WAAW,KAAK,IAAI,KAC3B,EAAE,UAAU,KAAK,QAAQ;AAAA,EAC3B;AAEA,MAAI,SAAS,SAAS,GAAG;AACvB,aAAS,WAAW,SAAS,IAAI,WAAS,cAAc,OAAO,QAAQ,CAAC;AAAA,EAC1E;AAEA,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -1,13 +1,11 @@
1
1
  {
2
2
  "name": "@mindbase/express-knowledge",
3
- "version": "1.0.0",
4
- "main": "./dist/index.js",
5
- "types": "./dist/index.d.ts",
3
+ "version": "1.0.4",
6
4
  "exports": {
7
5
  ".": {
8
6
  "development": "./index.ts",
9
- "import": "./dist/index.js",
10
- "types": "./dist/index.d.ts"
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.mjs"
11
9
  }
12
10
  },
13
11
  "files": [
@@ -17,11 +15,9 @@
17
15
  "node": ">=20.0.0"
18
16
  },
19
17
  "peerDependencies": {
20
- "@mindbase/express-auth": "*",
21
- "@mindbase/express-common": "*"
22
- },
23
- "dependencies": {
24
- "drizzle-orm": "^0.36.0",
18
+ "@mindbase/express-auth": "^1.0.0",
19
+ "@mindbase/express-common": "^1.0.0",
20
+ "drizzle-orm": "^0.44.7",
25
21
  "zod": "^3.24.0"
26
22
  },
27
23
  "devDependencies": {
@@ -34,4 +30,4 @@
34
30
  "publishConfig": {
35
31
  "access": "public"
36
32
  }
37
- }
33
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../service/KnowledgeService.ts","../utils/path-builder.ts"],"sourcesContent":["import { knowledge } from \"../orm/Knowledge.schema\";\r\nimport { eq, and, or, like, sql, desc, inArray } from \"drizzle-orm\";\r\nimport { buildPath, getLevel, validatePath, isDescendant } from \"../utils/path-builder\";\r\nimport type { Knowledge, KnowledgeSafe, InsertKnowledge } from \"../types\";\r\n\r\nlet _db: any;\r\n\r\n/**\r\n * 初始化 Service 的数据库实例\r\n */\r\nexport function initKnowledgeService(db: any) {\r\n _db = db;\r\n}\r\n\r\nfunction getDB() {\r\n if (!_db) throw new Error(\"KnowledgeService not initialized with DB\");\r\n return _db;\r\n}\r\n\r\n// ==================== CRUD 操作 ====================\r\n\r\n/**\r\n * 创建知识库内容\r\n */\r\nexport async function createKnowledge(data: InsertKnowledge & { userId: number }) {\r\n const db = getDB();\r\n const now = new Date();\r\n\r\n // 如果有父节点,获取父节点信息\r\n let parentPath = \"/\";\r\n let level = 0;\r\n\r\n if (data.parentId) {\r\n const [parent] = await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(eq(knowledge.id, data.parentId), eq(knowledge.isDelete, 0)))\r\n .limit(1);\r\n\r\n if (!parent) {\r\n throw new Error(\"父节点不存在\");\r\n }\r\n\r\n parentPath = parent.path;\r\n level = parent.level + 1;\r\n }\r\n\r\n // 先插入基础数据(不含 path 和 level)\r\n const result = await db\r\n .insert(knowledge)\r\n .values({\r\n name: data.name,\r\n type: data.type ?? 0,\r\n safe: data.safe ?? 0,\r\n userId: data.userId,\r\n description: data.description,\r\n content: data.content,\r\n contentText: data.contentText,\r\n viewType: data.viewType,\r\n coverImage: data.coverImage,\r\n author: data.author,\r\n isbn: data.isbn,\r\n created: now,\r\n updated: now,\r\n isDelete: 0,\r\n isTop: 0,\r\n path: \"/\", // 临时值\r\n level: 0, // 临时值\r\n })\r\n .execute();\r\n\r\n // 获取插入的 ID\r\n const lastId = result.lastInsertRowid as number;\r\n\r\n // 更新 path 和 level\r\n const finalPath = buildPath(parentPath, lastId);\r\n await db\r\n .update(knowledge)\r\n .set({ path: finalPath, level })\r\n .where(eq(knowledge.id, lastId))\r\n .execute();\r\n\r\n return getKnowledgeById(lastId);\r\n}\r\n\r\n/**\r\n * 批量创建知识库内容\r\n */\r\nexport async function createKnowledgeBatch(items: Array<{ name: string; type: number }>, userId: number, parentId?: number) {\r\n const results = [];\r\n\r\n for (const item of items) {\r\n try {\r\n const result = await createKnowledge({ ...item, userId, parentId });\r\n results.push({ success: true, data: result });\r\n } catch (error) {\r\n results.push({ success: false, error: error instanceof Error ? error.message : String(error) });\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * 分页获取知识库列表\r\n */\r\nexport async function listKnowledge(options: {\r\n pageIndex: number;\r\n pageSize: number;\r\n level?: number;\r\n type?: number;\r\n safe?: number[];\r\n userId?: number;\r\n isAdmin?: boolean;\r\n}) {\r\n const db = getDB();\r\n const { pageIndex, pageSize, level, type, safe = [0], userId, isAdmin } = options;\r\n\r\n let conditions = [eq(knowledge.isDelete, 0)];\r\n\r\n // 层级筛选\r\n if (level !== undefined) {\r\n conditions.push(eq(knowledge.level, level));\r\n }\r\n\r\n // 类型筛选\r\n if (type !== undefined) {\r\n conditions.push(eq(knowledge.type, type));\r\n }\r\n\r\n // 权限筛选\r\n const safeConditions = [];\r\n for (const s of safe) {\r\n switch (s) {\r\n case 0: // 公开\r\n safeConditions.push(eq(knowledge.safe, s));\r\n break;\r\n case 1: // 登录共享\r\n if (userId) {\r\n safeConditions.push(eq(knowledge.safe, s));\r\n }\r\n break;\r\n case 2: // 个人私密\r\n if (userId) {\r\n safeConditions.push(\r\n and(eq(knowledge.safe, s), isAdmin ? sql`1=1` : eq(knowledge.userId, userId))\r\n );\r\n }\r\n break;\r\n }\r\n }\r\n\r\n if (safeConditions.length > 0) {\r\n conditions.push(or(...safeConditions));\r\n }\r\n\r\n const offset = (pageIndex - 1) * pageSize;\r\n\r\n const list = await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(...conditions))\r\n .limit(pageSize)\r\n .offset(offset)\r\n .orderBy(desc(knowledge.isTop), desc(knowledge.created))\r\n .execute();\r\n\r\n const [totalResult] = await db\r\n .select({ count: sql<number>`count(*)` })\r\n .from(knowledge)\r\n .where(and(...conditions))\r\n .execute();\r\n\r\n return {\r\n list,\r\n total: totalResult.count,\r\n pageIndex,\r\n pageSize,\r\n };\r\n}\r\n\r\n/**\r\n * 获取知识库详情\r\n */\r\nexport async function getKnowledgeById(id: number): Promise<Knowledge | null> {\r\n const db = getDB();\r\n const [item] = await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(eq(knowledge.id, id), eq(knowledge.isDelete, 0)))\r\n .limit(1);\r\n\r\n return item || null;\r\n}\r\n\r\n/**\r\n * 批量获取知识库详情\r\n */\r\nexport async function getKnowledgeByIds(ids: number[]): Promise<Knowledge[]> {\r\n const db = getDB();\r\n if (ids.length === 0) return [];\r\n\r\n return await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(inArray(knowledge.id, ids), eq(knowledge.isDelete, 0)))\r\n .execute();\r\n}\r\n\r\n/**\r\n * 获取知识库目录树\r\n */\r\nexport async function getKnowledgeTree(options: {\r\n parentId?: number | null;\r\n safe?: number[];\r\n userId?: number;\r\n isAdmin?: boolean;\r\n}) {\r\n const db = getDB();\r\n const { parentId = null, safe = [0], userId, isAdmin } = options;\r\n\r\n let conditions = [eq(knowledge.isDelete, 0)];\r\n\r\n // 权限筛选\r\n const safeConditions = [];\r\n for (const s of safe) {\r\n switch (s) {\r\n case 0: // 公开\r\n safeConditions.push(eq(knowledge.safe, s));\r\n break;\r\n case 1: // 登录共享\r\n if (userId) {\r\n safeConditions.push(eq(knowledge.safe, s));\r\n }\r\n break;\r\n case 2: // 个人私密\r\n if (userId) {\r\n safeConditions.push(\r\n and(eq(knowledge.safe, s), isAdmin ? sql`1=1` : eq(knowledge.userId, userId))\r\n );\r\n }\r\n break;\r\n }\r\n }\r\n\r\n if (safeConditions.length > 0) {\r\n conditions.push(or(...safeConditions));\r\n }\r\n\r\n // 如果指定了 parentId,只获取该节点下的子树\r\n if (parentId !== null) {\r\n const [parent] = await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(eq(knowledge.id, parentId), eq(knowledge.isDelete, 0)))\r\n .limit(1);\r\n\r\n if (!parent) return [];\r\n\r\n conditions.push(like(knowledge.path, `${parent.path}%`));\r\n }\r\n\r\n const allNodes = await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(...conditions))\r\n .orderBy(desc(knowledge.isTop), knowledge.name)\r\n .execute();\r\n\r\n // 使用 tree-builder 构建树形结构\r\n const { buildTree } = await import(\"../utils/tree-builder\");\r\n return buildTree(allNodes, parentId);\r\n}\r\n\r\n/**\r\n * 获取子节点列表\r\n */\r\nexport async function getChildren(parentId: number) {\r\n const db = getDB();\r\n\r\n const [parent] = await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(eq(knowledge.id, parentId), eq(knowledge.isDelete, 0)))\r\n .limit(1);\r\n\r\n if (!parent) {\r\n throw new Error(\"父节点不存在\");\r\n }\r\n\r\n return await db\r\n .select()\r\n .from(knowledge)\r\n .where(\r\n and(\r\n eq(knowledge.isDelete, 0),\r\n like(knowledge.path, `${parent.path}%`),\r\n eq(knowledge.level, parent.level + 1)\r\n )\r\n )\r\n .orderBy(desc(knowledge.isTop), knowledge.name)\r\n .execute();\r\n}\r\n\r\n/**\r\n * 更新知识库内容\r\n */\r\nexport async function updateKnowledge(id: number, data: Partial<InsertKnowledge>, userId: number) {\r\n const db = getDB();\r\n const now = new Date();\r\n\r\n const result = await db\r\n .update(knowledge)\r\n .set({\r\n ...(data.name !== undefined && { name: data.name }),\r\n ...(data.description !== undefined && { description: data.description }),\r\n ...(data.content !== undefined && { content: data.content }),\r\n ...(data.contentText !== undefined && { contentText: data.contentText }),\r\n ...(data.viewType !== undefined && { viewType: data.viewType }),\r\n ...(data.coverImage !== undefined && { coverImage: data.coverImage }),\r\n ...(data.author !== undefined && { author: data.author }),\r\n ...(data.isbn !== undefined && { isbn: data.isbn }),\r\n updated: now,\r\n })\r\n .where(and(eq(knowledge.id, id), eq(knowledge.userId, userId)))\r\n .execute();\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 移动节点(更新整个子树的 path 和 level)\r\n */\r\nexport async function moveKnowledge(nodeId: number, newParentId: number, userId: number) {\r\n const db = getDB();\r\n\r\n // 1. 获取节点和新父节点信息\r\n const [node] = await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(eq(knowledge.id, nodeId), eq(knowledge.isDelete, 0)))\r\n .limit(1);\r\n\r\n const [newParent] = await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(eq(knowledge.id, newParentId), eq(knowledge.isDelete, 0)))\r\n .limit(1);\r\n\r\n if (!node || !newParent) {\r\n throw new Error(\"节点不存在\");\r\n }\r\n\r\n // 权限检查\r\n if (node.userId !== userId) {\r\n throw new Error(\"无权操作此节点\");\r\n }\r\n\r\n // 2. 防止循环:不能移动到自己的后代节点\r\n if (isDescendant(node.path, newParent.path)) {\r\n throw new Error(\"不能移动到自己的后代节点\");\r\n }\r\n\r\n const oldPath = node.path;\r\n const newPath = buildPath(newParent.path, nodeId);\r\n const levelDiff = newParent.level + 1 - node.level;\r\n\r\n // 3. 批量更新整个子树\r\n await db\r\n .update(knowledge)\r\n .set({\r\n path: sql`REPLACE(${knowledge.path}, ${oldPath}, ${newPath})`,\r\n level: sql`${knowledge.level} + ${levelDiff}`,\r\n })\r\n .where(or(like(knowledge.path, `${oldPath}%`), eq(knowledge.path, oldPath)))\r\n .execute();\r\n\r\n return getKnowledgeById(nodeId);\r\n}\r\n\r\n/**\r\n * 更新置顶状态\r\n */\r\nexport async function updateIsTop(id: number, isTop: number, userId: number) {\r\n const db = getDB();\r\n const now = new Date();\r\n\r\n await db\r\n .update(knowledge)\r\n .set({ isTop, updated: now })\r\n .where(and(eq(knowledge.id, id), eq(knowledge.userId, userId)))\r\n .execute();\r\n\r\n return getKnowledgeById(id);\r\n}\r\n\r\n/**\r\n * 更新安全等级\r\n */\r\nexport async function updateSafe(id: number, safe: KnowledgeSafe, userId: number) {\r\n const db = getDB();\r\n const now = new Date();\r\n\r\n await db\r\n .update(knowledge)\r\n .set({ safe, updated: now })\r\n .where(and(eq(knowledge.id, id), eq(knowledge.userId, userId)))\r\n .execute();\r\n\r\n return getKnowledgeById(id);\r\n}\r\n\r\n/**\r\n * 逻辑删除\r\n */\r\nexport async function deleteKnowledge(id: number, userId: number) {\r\n const db = getDB();\r\n const now = new Date();\r\n\r\n await db\r\n .update(knowledge)\r\n .set({ isDelete: 1, updated: now })\r\n .where(and(eq(knowledge.id, id), eq(knowledge.userId, userId)))\r\n .execute();\r\n\r\n return { success: true };\r\n}\r\n\r\n/**\r\n * 真实删除(管理员)\r\n */\r\nexport async function deleteKnowledgeReal(id: number) {\r\n const db = getDB();\r\n\r\n await db\r\n .delete(knowledge)\r\n .where(eq(knowledge.id, id))\r\n .execute();\r\n\r\n return { success: true };\r\n}\r\n\r\n/**\r\n * 搜索知识库内容\r\n */\r\nexport async function searchKnowledge(options: {\r\n keyword: string;\r\n pageIndex?: number;\r\n pageSize?: number;\r\n safe?: number[];\r\n userId?: number;\r\n isAdmin?: boolean;\r\n}) {\r\n const db = getDB();\r\n const { keyword, pageIndex = 1, pageSize = 10, safe = [0], userId, isAdmin } = options;\r\n\r\n let conditions = [\r\n eq(knowledge.isDelete, 0),\r\n or(\r\n like(knowledge.name, `%${keyword}%`),\r\n like(knowledge.description, `%${keyword}%`),\r\n like(knowledge.contentText, `%${keyword}%`)\r\n ),\r\n ];\r\n\r\n // 权限筛选\r\n const safeConditions = [];\r\n for (const s of safe) {\r\n switch (s) {\r\n case 0: // 公开\r\n safeConditions.push(eq(knowledge.safe, s));\r\n break;\r\n case 1: // 登录共享\r\n if (userId) {\r\n safeConditions.push(eq(knowledge.safe, s));\r\n }\r\n break;\r\n case 2: // 个人私密\r\n if (userId) {\r\n safeConditions.push(\r\n and(eq(knowledge.safe, s), isAdmin ? sql`1=1` : eq(knowledge.userId, userId))\r\n );\r\n }\r\n break;\r\n }\r\n }\r\n\r\n if (safeConditions.length > 0) {\r\n conditions.push(or(...safeConditions));\r\n }\r\n\r\n const offset = (pageIndex - 1) * pageSize;\r\n\r\n const list = await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(...conditions))\r\n .limit(pageSize)\r\n .offset(offset)\r\n .orderBy(desc(knowledge.created))\r\n .execute();\r\n\r\n const [totalResult] = await db\r\n .select({ count: sql<number>`count(*)` })\r\n .from(knowledge)\r\n .where(and(...conditions))\r\n .execute();\r\n\r\n return {\r\n list,\r\n total: totalResult.count,\r\n pageIndex,\r\n pageSize,\r\n };\r\n}\r\n","/**\r\n * 物化路径工具函数\r\n */\r\n\r\n/**\r\n * 构建新节点路径\r\n * @param parentPath 父节点路径\r\n * @param id 新节点ID\r\n * @returns 新节点路径\r\n */\r\nexport function buildPath(parentPath: string, id: number): string {\r\n return `${parentPath}${id}/`;\r\n}\r\n\r\n/**\r\n * 从路径计算层级\r\n * @param path 节点路径\r\n * @returns 层级深度(根节点为0)\r\n */\r\nexport function getLevel(path: string): number {\r\n // path 格式: \"/1/3/7/\" -> 分割后 ['', '1', '3', '7', '']\r\n // 减2是因为前后各有一个空字符串\r\n const parts = path.split(\"/\").filter(Boolean);\r\n return parts.length - 1;\r\n}\r\n\r\n/**\r\n * 验证路径是否有效(防止循环引用)\r\n * @param newPath 新路径\r\n * @param oldPath 旧路径\r\n * @returns 是否有效\r\n */\r\nexport function validatePath(newPath: string, oldPath: string): boolean {\r\n // 不能将节点移动到自己的后代节点下\r\n // 如果新父节点的 path 以旧节点的 path 开头,说明是后代\r\n return !newPath.startsWith(oldPath);\r\n}\r\n\r\n/**\r\n * 检查是否为后代节点\r\n * @param ancestorPath 祖先节点路径\r\n * @param descendantPath 后代节点路径\r\n * @returns 是否为后代\r\n */\r\nexport function isDescendant(ancestorPath: string, descendantPath: string): boolean {\r\n return descendantPath.startsWith(ancestorPath) && ancestorPath !== descendantPath;\r\n}\r\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../service/TagService.ts"],"sourcesContent":["import { knowledgeTag, knowledgeTagRelation } from \"../orm/Tag.schema\";\r\nimport { eq, and, inArray } from \"drizzle-orm\";\r\nimport type { InsertTag } from \"../types\";\r\n\r\nlet _db: any;\r\n\r\n/**\r\n * 初始化 Service 的数据库实例\r\n */\r\nexport function initTagService(db: any) {\r\n _db = db;\r\n}\r\n\r\nfunction getDB() {\r\n if (!_db) throw new Error(\"TagService not initialized with DB\");\r\n return _db;\r\n}\r\n\r\n// ==================== 标签管理 ====================\r\n\r\n/**\r\n * 创建标签\r\n */\r\nexport async function createTag(data: InsertTag & { userId?: number }) {\r\n const db = getDB();\r\n const now = new Date();\r\n\r\n const result = await db\r\n .insert(knowledgeTag)\r\n .values({\r\n name: data.name,\r\n color: data.color,\r\n userId: data.userId,\r\n created: now,\r\n })\r\n .execute();\r\n\r\n const lastId = result.lastInsertRowid as number;\r\n return getTagById(lastId);\r\n}\r\n\r\n/**\r\n * 获取标签详情\r\n */\r\nexport async function getTagById(id: number) {\r\n const db = getDB();\r\n const [tag] = await db\r\n .select()\r\n .from(knowledgeTag)\r\n .where(eq(knowledgeTag.id, id))\r\n .limit(1);\r\n\r\n return tag || null;\r\n}\r\n\r\n/**\r\n * 获取标签列表\r\n */\r\nexport async function listTags(userId?: number) {\r\n const db = getDB();\r\n\r\n let conditions = [];\r\n if (userId !== undefined) {\r\n conditions.push(eq(knowledgeTag.userId, userId));\r\n }\r\n\r\n return await db\r\n .select()\r\n .from(knowledgeTag)\r\n .where(conditions.length > 0 ? and(...conditions) : undefined)\r\n .orderBy(knowledgeTag.name)\r\n .execute();\r\n}\r\n\r\n/**\r\n * 更新标签\r\n */\r\nexport async function updateTag(id: number, data: Partial<InsertTag>, userId: number) {\r\n const db = getDB();\r\n\r\n // 检查权限\r\n const [tag] = await db\r\n .select()\r\n .from(knowledgeTag)\r\n .where(and(eq(knowledgeTag.id, id), eq(knowledgeTag.userId, userId)))\r\n .limit(1);\r\n\r\n if (!tag) {\r\n throw new Error(\"标签不存在或无权操作\");\r\n }\r\n\r\n await db\r\n .update(knowledgeTag)\r\n .set({\r\n ...(data.name !== undefined && { name: data.name }),\r\n ...(data.color !== undefined && { color: data.color }),\r\n })\r\n .where(eq(knowledgeTag.id, id))\r\n .execute();\r\n\r\n return getTagById(id);\r\n}\r\n\r\n/**\r\n * 删除标签\r\n */\r\nexport async function deleteTag(id: number, userId: number) {\r\n const db = getDB();\r\n\r\n // 检查权限\r\n const [tag] = await db\r\n .select()\r\n .from(knowledgeTag)\r\n .where(and(eq(knowledgeTag.id, id), eq(knowledgeTag.userId, userId)))\r\n .limit(1);\r\n\r\n if (!tag) {\r\n throw new Error(\"标签不存在或无权操作\");\r\n }\r\n\r\n // 删除标签关联\r\n await db\r\n .delete(knowledgeTagRelation)\r\n .where(eq(knowledgeTagRelation.tagId, id))\r\n .execute();\r\n\r\n // 删除标签\r\n await db\r\n .delete(knowledgeTag)\r\n .where(eq(knowledgeTag.id, id))\r\n .execute();\r\n\r\n return { success: true };\r\n}\r\n\r\n// ==================== 标签关联 ====================\r\n\r\n/**\r\n * 为知识库内容添加标签\r\n */\r\nexport async function addTagToKnowledge(knowledgeId: number, tagId: number, userId: number) {\r\n const db = getDB();\r\n\r\n // 检查标签是否存在\r\n const [tag] = await db\r\n .select()\r\n .from(knowledgeTag)\r\n .where(eq(knowledgeTag.id, tagId))\r\n .limit(1);\r\n\r\n if (!tag) {\r\n throw new Error(\"标签不存在\");\r\n }\r\n\r\n // 检查是否已存在关联\r\n const [existing] = await db\r\n .select()\r\n .from(knowledgeTagRelation)\r\n .where(\r\n and(\r\n eq(knowledgeTagRelation.knowledgeId, knowledgeId),\r\n eq(knowledgeTagRelation.tagId, tagId)\r\n )\r\n )\r\n .limit(1);\r\n\r\n if (existing) {\r\n return { success: true, message: \"标签已存在\" };\r\n }\r\n\r\n // 创建关联\r\n await db\r\n .insert(knowledgeTagRelation)\r\n .values({\r\n knowledgeId,\r\n tagId,\r\n })\r\n .execute();\r\n\r\n return { success: true };\r\n}\r\n\r\n/**\r\n * 移除知识库内容的标签\r\n */\r\nexport async function removeTagFromKnowledge(knowledgeId: number, tagId: number) {\r\n const db = getDB();\r\n\r\n await db\r\n .delete(knowledgeTagRelation)\r\n .where(\r\n and(\r\n eq(knowledgeTagRelation.knowledgeId, knowledgeId),\r\n eq(knowledgeTagRelation.tagId, tagId)\r\n )\r\n )\r\n .execute();\r\n\r\n return { success: true };\r\n}\r\n\r\n/**\r\n * 获取知识库内容的标签列表\r\n */\r\nexport async function getKnowledgeTags(knowledgeId: number) {\r\n const db = getDB();\r\n\r\n return await db\r\n .select({\r\n id: knowledgeTag.id,\r\n name: knowledgeTag.name,\r\n color: knowledgeTag.color,\r\n })\r\n .from(knowledgeTagRelation)\r\n .innerJoin(knowledgeTag, eq(knowledgeTagRelation.tagId, knowledgeTag.id))\r\n .where(eq(knowledgeTagRelation.knowledgeId, knowledgeId))\r\n .execute();\r\n}\r\n\r\n/**\r\n * 按标签查询知识库内容\r\n */\r\nexport async function getKnowledgeByTag(tagId: number, options: {\r\n pageIndex?: number;\r\n pageSize?: number;\r\n safe?: number[];\r\n userId?: number;\r\n isAdmin?: boolean;\r\n}) {\r\n const db = getDB();\r\n const { pageIndex = 1, pageSize = 10, safe = [0], userId, isAdmin } = options;\r\n\r\n // 获取带有该标签的知识库 ID 列表\r\n const relations = await db\r\n .select()\r\n .from(knowledgeTagRelation)\r\n .where(eq(knowledgeTagRelation.tagId, tagId))\r\n .execute();\r\n\r\n if (relations.length === 0) {\r\n return { list: [], total: 0, pageIndex, pageSize };\r\n }\r\n\r\n const knowledgeIds = relations.map(r => r.knowledgeId);\r\n\r\n // 导入 knowledge 表\r\n const { knowledge } = await import(\"../orm/Knowledge.schema\");\r\n const { eq, and, or, sql, desc } = await import(\"drizzle-orm\");\r\n\r\n let conditions = [\r\n eq(knowledge.isDelete, 0),\r\n inArray(knowledge.id, knowledgeIds),\r\n ];\r\n\r\n // 权限筛选\r\n const safeConditions = [];\r\n for (const s of safe) {\r\n switch (s) {\r\n case 0: // 公开\r\n safeConditions.push(eq(knowledge.safe, s));\r\n break;\r\n case 1: // 登录共享\r\n if (userId) {\r\n safeConditions.push(eq(knowledge.safe, s));\r\n }\r\n break;\r\n case 2: // 个人私密\r\n if (userId) {\r\n safeConditions.push(\r\n and(eq(knowledge.safe, s), isAdmin ? sql`1=1` : eq(knowledge.userId, userId))\r\n );\r\n }\r\n break;\r\n }\r\n }\r\n\r\n if (safeConditions.length > 0) {\r\n conditions.push(or(...safeConditions));\r\n }\r\n\r\n const offset = (pageIndex - 1) * pageSize;\r\n\r\n const list = await db\r\n .select()\r\n .from(knowledge)\r\n .where(and(...conditions))\r\n .limit(pageSize)\r\n .offset(offset)\r\n .orderBy(desc(knowledge.created))\r\n .execute();\r\n\r\n const [totalResult] = await db\r\n .select({ count: sql<number>`count(*)` })\r\n .from(knowledge)\r\n .where(and(...conditions))\r\n .execute();\r\n\r\n return {\r\n list,\r\n total: totalResult.count,\r\n pageIndex,\r\n pageSize,\r\n };\r\n}\r\n"],"mappings":"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","names":["eq","and"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../orm/Tag.schema.ts"],"sourcesContent":["import { sqliteTable, text, integer } from \"drizzle-orm/sqlite-core\";\r\nimport { createInsertSchema, createSelectSchema } from \"drizzle-zod\";\r\nimport { z } from \"zod\";\r\n\r\n/**\r\n * 标签表\r\n */\r\nexport const knowledgeTag = sqliteTable(\"knowledge_tag\", {\r\n id: integer(\"id\").primaryKey({ autoIncrement: true }),\r\n name: text(\"name\").notNull().unique(),\r\n color: text(\"color\"),\r\n userId: integer(\"user_id\"),\r\n created: integer(\"created\", { mode: \"timestamp\" }),\r\n});\r\n\r\n/**\r\n * 标签关联表(多对多)\r\n */\r\nexport const knowledgeTagRelation = sqliteTable(\"knowledge_tag_relation\", {\r\n knowledgeId: integer(\"knowledge_id\").notNull(),\r\n tagId: integer(\"tag_id\").notNull(),\r\n});\r\n\r\n// ==================== Zod Schemas ====================\r\n\r\n/**\r\n * 创建标签验证\r\n */\r\nexport const createTagSchema = createInsertSchema(knowledgeTag as any, {\r\n name: z.string().min(1, \"标签名不能为空\").max(50, \"标签名不能超过50字符\") as any,\r\n color: z.string().regex(/^#[0-9A-Fa-f]{6}$/, \"颜色必须是十六进制格式\").optional() as any,\r\n}).pick({\r\n name: true,\r\n color: true,\r\n});\r\n\r\n/**\r\n * 更新标签验证\r\n */\r\nexport const updateTagSchema = createInsertSchema(knowledgeTag as any)\r\n .partial()\r\n .extend({\r\n id: z.number({ required_error: \"标签ID不能为空\" }),\r\n });\r\n\r\n/**\r\n * 添加标签关联验证\r\n */\r\nexport const addTagRelationSchema = z.object({\r\n tagId: z.number({ required_error: \"标签ID不能为空\" }),\r\n});\r\n"],"mappings":";;;;;AAAA,SAAS,aAAa,MAAM,eAAe;AAE3C,SAAS,SAAS;AAKX,IAAM,eAAe,YAAY,iBAAiB;AAAA,EACvD,IAAI,QAAQ,IAAI,EAAE,WAAW,EAAE,eAAe,KAAK,CAAC;AAAA,EACpD,MAAM,KAAK,MAAM,EAAE,QAAQ,EAAE,OAAO;AAAA,EACpC,OAAO,KAAK,OAAO;AAAA,EACnB,QAAQ,QAAQ,SAAS;AAAA,EACzB,SAAS,QAAQ,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,CAAC;AAKM,IAAM,uBAAuB,YAAY,0BAA0B;AAAA,EACxE,aAAa,QAAQ,cAAc,EAAE,QAAQ;AAAA,EAC7C,OAAO,QAAQ,QAAQ,EAAE,QAAQ;AACnC,CAAC;AAOM,IAAM,kBAAkB,EAAmB,cAAqB;AAAA,EACrE,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,4CAAS,EAAE,IAAI,IAAI,0DAAa;AAAA,EACxD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,oEAAa,EAAE,SAAS;AACvE,CAAC,EAAE,KAAK;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT,CAAC;AAKM,IAAM,kBAAkB,EAAmB,YAAmB,EAClE,QAAQ,EACR,OAAO;AAAA,EACN,IAAI,EAAE,OAAO,EAAE,gBAAgB,yCAAW,CAAC;AAC7C,CAAC;AAKI,IAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,OAAO,EAAE,OAAO,EAAE,gBAAgB,yCAAW,CAAC;AAChD,CAAC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../orm/Knowledge.schema.ts"],"sourcesContent":["import { sqliteTable, text, integer } from \"drizzle-orm/sqlite-core\";\r\nimport { createInsertSchema, createSelectSchema } from \"drizzle-zod\";\r\nimport { z } from \"zod\";\r\n\r\n/**\r\n * 知识库主表\r\n */\r\nexport const knowledge = sqliteTable(\"knowledge\", {\r\n id: integer(\"id\").primaryKey({ autoIncrement: true }),\r\n name: text(\"name\").notNull(),\r\n path: text(\"path\").notNull(),\r\n level: integer(\"level\").notNull().default(0),\r\n type: integer(\"type\").notNull(), // 0:文件夹 1:笔记 2:书籍\r\n safe: integer(\"safe\").notNull().default(0),\r\n userId: integer(\"user_id\").notNull(),\r\n // 书籍特有字段\r\n coverImage: text(\"cover_image\"),\r\n author: text(\"author\"),\r\n isbn: text(\"isbn\"),\r\n description: text(\"description\"),\r\n created: integer(\"created\", { mode: \"timestamp\" }),\r\n updated: integer(\"updated\", { mode: \"timestamp\" }),\r\n isDelete: integer(\"is_delete\").notNull().default(0),\r\n isTop: integer(\"is_top\").notNull().default(0),\r\n content: text(\"content\"),\r\n contentText: text(\"content_text\"),\r\n viewType: integer(\"view_type\").default(0),\r\n});\r\n\r\n// ==================== Zod Schemas ====================\r\n\r\n/**\r\n * 创建知识库内容验证\r\n */\r\nexport const createKnowledgeSchema = createInsertSchema(knowledge as any, {\r\n name: z.string().min(1, \"名称不能为空\").max(255, \"名称不能超过255字符\") as any,\r\n type: z.coerce.number().int().min(0).max(2) as any,\r\n safe: z.coerce.number().int().min(0).max(2).optional() as any,\r\n}).pick({\r\n name: true,\r\n type: true,\r\n safe: true,\r\n description: true,\r\n content: true,\r\n contentText: true,\r\n viewType: true,\r\n coverImage: true,\r\n author: true,\r\n isbn: true,\r\n});\r\n\r\n/**\r\n * 批量创建验证\r\n */\r\nexport const batchCreateKnowledgeSchema = z.object({\r\n parentId: z.number().optional(),\r\n items: z.array(z.object({\r\n name: z.string().min(1, \"名称不能为空\").max(255),\r\n type: z.coerce.number().int().min(0).max(2),\r\n })).min(1, \"至少需要一个项目\"),\r\n});\r\n\r\n/**\r\n * 批量获取详情验证\r\n */\r\nexport const batchDetailKnowledgeSchema = z.object({\r\n ids: z.array(z.number().int().positive()).min(1, \"至少需要一个ID\"),\r\n});\r\n\r\n/**\r\n * 更新知识库内容验证\r\n */\r\nexport const updateKnowledgeSchema = createInsertSchema(knowledge as any)\r\n .partial()\r\n .extend({\r\n id: z.number({ required_error: \"ID不能为空\" }),\r\n });\r\n\r\n/**\r\n * 列表查询验证\r\n */\r\nexport const listKnowledgeSchema = z.object({\r\n pageIndex: z.coerce.number().int().positive().optional().default(1),\r\n pageSize: z.coerce.number().int().positive().optional().default(10),\r\n level: z.coerce.number().int().min(0).optional(),\r\n type: z.coerce.number().int().min(0).max(2).optional(),\r\n safe: z.array(z.coerce.number().int().min(0).max(2)).optional(),\r\n});\r\n\r\n/**\r\n * 搜索验证\r\n */\r\nexport const searchKnowledgeSchema = z.object({\r\n keyword: z.string().min(1, \"关键词不能为空\"),\r\n pageIndex: z.coerce.number().int().positive().optional().default(1),\r\n pageSize: z.coerce.number().int().positive().optional().default(10),\r\n});\r\n\r\n/**\r\n * 移动节点验证\r\n */\r\nexport const moveKnowledgeSchema = z.object({\r\n newParentId: z.number({ required_error: \"新父节点ID不能为空\" }),\r\n});\r\n\r\n/**\r\n * 更新置顶状态验证\r\n */\r\nexport const updateIsTopSchema = z.object({\r\n isTop: z.coerce.number().int().min(0).max(1),\r\n});\r\n\r\n/**\r\n * 更新安全等级验证\r\n */\r\nexport const updateSafeSchema = z.object({\r\n safe: z.coerce.number().int().min(0).max(2),\r\n});\r\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../utils/tree-builder.ts"],"sourcesContent":["import type { KnowledgeTreeNode, Knowledge } from \"../types\";\r\n\r\n/**\r\n * 将扁平数组转换为树形结构\r\n * @param nodes 扁平节点数组\r\n * @param parentId 父节点ID(可选,用于构建子树)\r\n * @returns 树形结构\r\n */\r\nexport function buildTree(\r\n nodes: Knowledge[],\r\n parentId: number | null = null\r\n): KnowledgeTreeNode[] {\r\n // 按 isTop 降序、created 降序排序\r\n const sortedNodes = [...nodes].sort((a, b) => {\r\n if (a.isTop !== b.isTop) {\r\n return b.isTop - a.isTop;\r\n }\r\n return b.created.getTime() - a.created.getTime();\r\n });\r\n\r\n // 如果指定了 parentId,先找到该节点\r\n if (parentId !== null) {\r\n const parent = sortedNodes.find(n => n.id === parentId);\r\n if (!parent) return [];\r\n\r\n // 查找该节点的直接子节点\r\n const children = sortedNodes.filter(n =>\r\n n.path.startsWith(parent.path) &&\r\n n.level === parent.level + 1\r\n );\r\n\r\n return children.map(child => buildNodeTree(child, sortedNodes));\r\n }\r\n\r\n // 否则返回根节点(level=0)的树\r\n const rootNodes = sortedNodes.filter(n => n.level === 0);\r\n return rootNodes.map(node => buildNodeTree(node, sortedNodes));\r\n}\r\n\r\n/**\r\n * 递归构建节点树\r\n */\r\nfunction buildNodeTree(node: Knowledge, allNodes: Knowledge[]): KnowledgeTreeNode {\r\n const treeNode: KnowledgeTreeNode = { ...node };\r\n\r\n // 查找直接子节点\r\n const children = allNodes.filter(n =>\r\n n.path.startsWith(node.path) &&\r\n n.level === node.level + 1\r\n );\r\n\r\n if (children.length > 0) {\r\n treeNode.children = children.map(child => buildNodeTree(child, allNodes));\r\n }\r\n\r\n return treeNode;\r\n}\r\n"],"mappings":";;;AAQO,SAAS,UACd,OACA,WAA0B,MACL;AAErB,QAAM,cAAc,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM;AAC5C,QAAI,EAAE,UAAU,EAAE,OAAO;AACvB,aAAO,EAAE,QAAQ,EAAE;AAAA,IACrB;AACA,WAAO,EAAE,QAAQ,QAAQ,IAAI,EAAE,QAAQ,QAAQ;AAAA,EACjD,CAAC;AAGD,MAAI,aAAa,MAAM;AACrB,UAAM,SAAS,YAAY,KAAK,OAAK,EAAE,OAAO,QAAQ;AACtD,QAAI,CAAC,OAAQ,QAAO,CAAC;AAGrB,UAAM,WAAW,YAAY;AAAA,MAAO,OAClC,EAAE,KAAK,WAAW,OAAO,IAAI,KAC7B,EAAE,UAAU,OAAO,QAAQ;AAAA,IAC7B;AAEA,WAAO,SAAS,IAAI,WAAS,cAAc,OAAO,WAAW,CAAC;AAAA,EAChE;AAGA,QAAM,YAAY,YAAY,OAAO,OAAK,EAAE,UAAU,CAAC;AACvD,SAAO,UAAU,IAAI,UAAQ,cAAc,MAAM,WAAW,CAAC;AAC/D;AAKA,SAAS,cAAc,MAAiB,UAA0C;AAChF,QAAM,WAA8B,EAAE,GAAG,KAAK;AAG9C,QAAM,WAAW,SAAS;AAAA,IAAO,OAC/B,EAAE,KAAK,WAAW,KAAK,IAAI,KAC3B,EAAE,UAAU,KAAK,QAAQ;AAAA,EAC3B;AAEA,MAAI,SAAS,SAAS,GAAG;AACvB,aAAS,WAAW,SAAS,IAAI,WAAS,cAAc,OAAO,QAAQ,CAAC;AAAA,EAC1E;AAEA,SAAO;AACT;","names":[]}