From 292a0042ab21203e0d352a1bffaa30ac5f121403 Mon Sep 17 00:00:00 2001 From: zhishi <1951671751@qq.com> Date: Sun, 29 Mar 2026 21:18:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=8E=B7=E5=8F=96=E8=A7=86?= =?UTF-8?q?=E8=A7=89=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router.ts | 192 +++++++++++++------------ src/routes/project/addVisualManual.ts | 102 +++++++++++++ src/routes/project/editVisualManual.ts | 30 ++-- src/utils/getArtPrompt.ts | 9 +- 4 files changed, 222 insertions(+), 111 deletions(-) create mode 100644 src/routes/project/addVisualManual.ts diff --git a/src/router.ts b/src/router.ts index 8d43229..7590ee1 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,4 +1,4 @@ -// @routes-hash 5a19c42c22d44ec4c7fe2fc7245535d9 +// @routes-hash 48f3cafa7563a8ec5d646e1f88ef8bfd import { Express } from "express"; import route1 from "./routes/agents/clearMemory"; @@ -66,53 +66,54 @@ import route62 from "./routes/production/workbench/getVideoModelDetail"; import route63 from "./routes/production/workbench/videoPolling"; import route64 from "./routes/project/addProject"; import route65 from "./routes/project/addVisual"; -import route66 from "./routes/project/deleteVisualManual"; -import route67 from "./routes/project/delProject"; -import route68 from "./routes/project/editProject"; -import route69 from "./routes/project/editVisualManual"; -import route70 from "./routes/project/getProject"; -import route71 from "./routes/project/getVisualManual"; -import route72 from "./routes/project/visualManual"; -import route73 from "./routes/script/addScript"; -import route74 from "./routes/script/delScript"; -import route75 from "./routes/script/exportScript"; -import route76 from "./routes/script/extractAssets"; -import route77 from "./routes/script/getScrptApi"; -import route78 from "./routes/script/pollScriptAssets"; -import route79 from "./routes/script/updateScript"; -import route80 from "./routes/scriptAgent/getPlanData"; -import route81 from "./routes/scriptAgent/setPlanData"; -import route82 from "./routes/setting/about/checkUpdate"; -import route83 from "./routes/setting/about/downloadApp"; -import route84 from "./routes/setting/agentDeploy/agentSetKey"; -import route85 from "./routes/setting/agentDeploy/deployAgentModel"; -import route86 from "./routes/setting/agentDeploy/getAgentDeploy"; -import route87 from "./routes/setting/dbConfig/clearData"; -import route88 from "./routes/setting/dev/getSwitchAiDevTool"; -import route89 from "./routes/setting/dev/updateSwitchAiDevTool"; -import route90 from "./routes/setting/fileManagement/openFolder"; -import route91 from "./routes/setting/getTextModel"; -import route92 from "./routes/setting/loginConfig/getUser"; -import route93 from "./routes/setting/loginConfig/updateUserPwd"; -import route94 from "./routes/setting/memoryConfig/delAllMemory"; -import route95 from "./routes/setting/memoryConfig/getMemory"; -import route96 from "./routes/setting/memoryConfig/sureMemory"; -import route97 from "./routes/setting/promptManage/getPrompt"; -import route98 from "./routes/setting/promptManage/updatePrompt"; -import route99 from "./routes/setting/skillManagement/getSkillContent"; -import route100 from "./routes/setting/skillManagement/getSkillList"; -import route101 from "./routes/setting/skillManagement/saveSkillContent"; -import route102 from "./routes/setting/vendorConfig/addVendor"; -import route103 from "./routes/setting/vendorConfig/deleteVendor"; -import route104 from "./routes/setting/vendorConfig/getVendorList"; -import route105 from "./routes/setting/vendorConfig/modelTest"; -import route106 from "./routes/setting/vendorConfig/updateCode"; -import route107 from "./routes/setting/vendorConfig/updateVendor"; -import route108 from "./routes/task/getProject"; -import route109 from "./routes/task/getTaskApi"; -import route110 from "./routes/task/getTaskCategories"; -import route111 from "./routes/task/taskDetails"; -import route112 from "./routes/test/test"; +import route66 from "./routes/project/addVisualManual"; +import route67 from "./routes/project/deleteVisualManual"; +import route68 from "./routes/project/delProject"; +import route69 from "./routes/project/editProject"; +import route70 from "./routes/project/editVisualManual"; +import route71 from "./routes/project/getProject"; +import route72 from "./routes/project/getVisualManual"; +import route73 from "./routes/project/visualManual"; +import route74 from "./routes/script/addScript"; +import route75 from "./routes/script/delScript"; +import route76 from "./routes/script/exportScript"; +import route77 from "./routes/script/extractAssets"; +import route78 from "./routes/script/getScrptApi"; +import route79 from "./routes/script/pollScriptAssets"; +import route80 from "./routes/script/updateScript"; +import route81 from "./routes/scriptAgent/getPlanData"; +import route82 from "./routes/scriptAgent/setPlanData"; +import route83 from "./routes/setting/about/checkUpdate"; +import route84 from "./routes/setting/about/downloadApp"; +import route85 from "./routes/setting/agentDeploy/agentSetKey"; +import route86 from "./routes/setting/agentDeploy/deployAgentModel"; +import route87 from "./routes/setting/agentDeploy/getAgentDeploy"; +import route88 from "./routes/setting/dbConfig/clearData"; +import route89 from "./routes/setting/dev/getSwitchAiDevTool"; +import route90 from "./routes/setting/dev/updateSwitchAiDevTool"; +import route91 from "./routes/setting/fileManagement/openFolder"; +import route92 from "./routes/setting/getTextModel"; +import route93 from "./routes/setting/loginConfig/getUser"; +import route94 from "./routes/setting/loginConfig/updateUserPwd"; +import route95 from "./routes/setting/memoryConfig/delAllMemory"; +import route96 from "./routes/setting/memoryConfig/getMemory"; +import route97 from "./routes/setting/memoryConfig/sureMemory"; +import route98 from "./routes/setting/promptManage/getPrompt"; +import route99 from "./routes/setting/promptManage/updatePrompt"; +import route100 from "./routes/setting/skillManagement/getSkillContent"; +import route101 from "./routes/setting/skillManagement/getSkillList"; +import route102 from "./routes/setting/skillManagement/saveSkillContent"; +import route103 from "./routes/setting/vendorConfig/addVendor"; +import route104 from "./routes/setting/vendorConfig/deleteVendor"; +import route105 from "./routes/setting/vendorConfig/getVendorList"; +import route106 from "./routes/setting/vendorConfig/modelTest"; +import route107 from "./routes/setting/vendorConfig/updateCode"; +import route108 from "./routes/setting/vendorConfig/updateVendor"; +import route109 from "./routes/task/getProject"; +import route110 from "./routes/task/getTaskApi"; +import route111 from "./routes/task/getTaskCategories"; +import route112 from "./routes/task/taskDetails"; +import route113 from "./routes/test/test"; export default async (app: Express) => { app.use("/api/agents/clearMemory", route1); @@ -180,51 +181,52 @@ export default async (app: Express) => { app.use("/api/production/workbench/videoPolling", route63); app.use("/api/project/addProject", route64); app.use("/api/project/addVisual", route65); - app.use("/api/project/deleteVisualManual", route66); - app.use("/api/project/delProject", route67); - app.use("/api/project/editProject", route68); - app.use("/api/project/editVisualManual", route69); - app.use("/api/project/getProject", route70); - app.use("/api/project/getVisualManual", route71); - app.use("/api/project/visualManual", route72); - app.use("/api/script/addScript", route73); - app.use("/api/script/delScript", route74); - app.use("/api/script/exportScript", route75); - app.use("/api/script/extractAssets", route76); - app.use("/api/script/getScrptApi", route77); - app.use("/api/script/pollScriptAssets", route78); - app.use("/api/script/updateScript", route79); - app.use("/api/scriptAgent/getPlanData", route80); - app.use("/api/scriptAgent/setPlanData", route81); - app.use("/api/setting/about/checkUpdate", route82); - app.use("/api/setting/about/downloadApp", route83); - app.use("/api/setting/agentDeploy/agentSetKey", route84); - app.use("/api/setting/agentDeploy/deployAgentModel", route85); - app.use("/api/setting/agentDeploy/getAgentDeploy", route86); - app.use("/api/setting/dbConfig/clearData", route87); - app.use("/api/setting/dev/getSwitchAiDevTool", route88); - app.use("/api/setting/dev/updateSwitchAiDevTool", route89); - app.use("/api/setting/fileManagement/openFolder", route90); - app.use("/api/setting/getTextModel", route91); - app.use("/api/setting/loginConfig/getUser", route92); - app.use("/api/setting/loginConfig/updateUserPwd", route93); - app.use("/api/setting/memoryConfig/delAllMemory", route94); - app.use("/api/setting/memoryConfig/getMemory", route95); - app.use("/api/setting/memoryConfig/sureMemory", route96); - app.use("/api/setting/promptManage/getPrompt", route97); - app.use("/api/setting/promptManage/updatePrompt", route98); - app.use("/api/setting/skillManagement/getSkillContent", route99); - app.use("/api/setting/skillManagement/getSkillList", route100); - app.use("/api/setting/skillManagement/saveSkillContent", route101); - app.use("/api/setting/vendorConfig/addVendor", route102); - app.use("/api/setting/vendorConfig/deleteVendor", route103); - app.use("/api/setting/vendorConfig/getVendorList", route104); - app.use("/api/setting/vendorConfig/modelTest", route105); - app.use("/api/setting/vendorConfig/updateCode", route106); - app.use("/api/setting/vendorConfig/updateVendor", route107); - app.use("/api/task/getProject", route108); - app.use("/api/task/getTaskApi", route109); - app.use("/api/task/getTaskCategories", route110); - app.use("/api/task/taskDetails", route111); - app.use("/api/test/test", route112); + app.use("/api/project/addVisualManual", route66); + app.use("/api/project/deleteVisualManual", route67); + app.use("/api/project/delProject", route68); + app.use("/api/project/editProject", route69); + app.use("/api/project/editVisualManual", route70); + app.use("/api/project/getProject", route71); + app.use("/api/project/getVisualManual", route72); + app.use("/api/project/visualManual", route73); + app.use("/api/script/addScript", route74); + app.use("/api/script/delScript", route75); + app.use("/api/script/exportScript", route76); + app.use("/api/script/extractAssets", route77); + app.use("/api/script/getScrptApi", route78); + app.use("/api/script/pollScriptAssets", route79); + app.use("/api/script/updateScript", route80); + app.use("/api/scriptAgent/getPlanData", route81); + app.use("/api/scriptAgent/setPlanData", route82); + app.use("/api/setting/about/checkUpdate", route83); + app.use("/api/setting/about/downloadApp", route84); + app.use("/api/setting/agentDeploy/agentSetKey", route85); + app.use("/api/setting/agentDeploy/deployAgentModel", route86); + app.use("/api/setting/agentDeploy/getAgentDeploy", route87); + app.use("/api/setting/dbConfig/clearData", route88); + app.use("/api/setting/dev/getSwitchAiDevTool", route89); + app.use("/api/setting/dev/updateSwitchAiDevTool", route90); + app.use("/api/setting/fileManagement/openFolder", route91); + app.use("/api/setting/getTextModel", route92); + app.use("/api/setting/loginConfig/getUser", route93); + app.use("/api/setting/loginConfig/updateUserPwd", route94); + app.use("/api/setting/memoryConfig/delAllMemory", route95); + app.use("/api/setting/memoryConfig/getMemory", route96); + app.use("/api/setting/memoryConfig/sureMemory", route97); + app.use("/api/setting/promptManage/getPrompt", route98); + app.use("/api/setting/promptManage/updatePrompt", route99); + app.use("/api/setting/skillManagement/getSkillContent", route100); + app.use("/api/setting/skillManagement/getSkillList", route101); + app.use("/api/setting/skillManagement/saveSkillContent", route102); + app.use("/api/setting/vendorConfig/addVendor", route103); + app.use("/api/setting/vendorConfig/deleteVendor", route104); + app.use("/api/setting/vendorConfig/getVendorList", route105); + app.use("/api/setting/vendorConfig/modelTest", route106); + app.use("/api/setting/vendorConfig/updateCode", route107); + app.use("/api/setting/vendorConfig/updateVendor", route108); + app.use("/api/task/getProject", route109); + app.use("/api/task/getTaskApi", route110); + app.use("/api/task/getTaskCategories", route111); + app.use("/api/task/taskDetails", route112); + app.use("/api/test/test", route113); } diff --git a/src/routes/project/addVisualManual.ts b/src/routes/project/addVisualManual.ts new file mode 100644 index 0000000..e25f6a6 --- /dev/null +++ b/src/routes/project/addVisualManual.ts @@ -0,0 +1,102 @@ +import express from "express"; +import u from "@/utils"; +import { error, success } from "@/lib/responseFormat"; +import fs from "fs"; +import path from "path"; +import { validateFields } from "@/middleware/middleware"; +import { z } from "zod"; +const router = express.Router(); + +// 编辑视觉手册 +export default router.post( + "/", + validateFields({ + name: z.string(), + images: z.array(z.string()), + data: z.array( + z.object({ + label: z.string(), + value: z.string(), + data: z.string(), + }), + ), + }), + async (req, res) => { + try { + const { name, images, data } = req.body as { + name: string; + images: string[]; + data: { label: string; value: string; data: string }[]; + }; + + if (/^\d+$/.test(name)) { + res.status(400).send(error("名称不能为纯数字")); + return; + } + + const mainPath = u.getPath(["skills", "art_prompts", name]); + if (fs.existsSync(mainPath)) { + return res.status(400).send(error("请勿填写重复名称的视觉手册")); + } + // 字段映射表(与 getVisualManual 保持一致) + const DATA_MAP: { value: string; subDir?: string }[] = [ + { value: "README" }, + { value: "prefix" }, + { value: "art_character", subDir: "art_prompt" }, + { value: "art_character_derivative", subDir: "art_prompt" }, + { value: "art_prop", subDir: "art_prompt" }, + { value: "art_prop_derivative", subDir: "art_prompt" }, + { value: "art_scene", subDir: "art_prompt" }, + { value: "art_scene_derivative", subDir: "art_prompt" }, + { value: "art_storyboard", subDir: "art_prompt" }, + { value: "art_storyboard_video", subDir: "art_prompt" }, + { value: "director_planning", subDir: "driector_skills" }, + { value: "director_storyboard_table", subDir: "driector_skills" }, + ]; + + // 根据 DATA_MAP 构建 value -> subDir 的映射 + const SUB_DIR_MAP = new Map(DATA_MAP.map(({ value, subDir }) => [value, subDir ?? ""])); + + // 合法的 value 值集合,用于校验 + const VALID_KEYS = new Set(DATA_MAP.map(({ value }) => value)); + + for (const item of data) { + if (!VALID_KEYS.has(item.value)) continue; + + const subDir = SUB_DIR_MAP.get(item.value)!; + const dirArr = subDir ? [mainPath, subDir] : [mainPath]; + const filePath = u.getPath([...dirArr, `${item.value}.md`]); + + const fileDir = path.dirname(filePath); + // 目录不存在时递归创建 + if (!fs.existsSync(fileDir)) { + fs.mkdirSync(fileDir, { recursive: true }); + } + fs.writeFileSync(filePath, item.data, "utf-8"); + } + const ossImagesDir = u.getPath(["oss", name]); + + let existingFiles: string[] = []; + try { + const allFiles = fs.readdirSync(ossImagesDir); + existingFiles = allFiles.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)); + } catch {} + + const retainedFileNames = new Set(images.filter((item) => item.startsWith("http")).map((url) => path.basename(new URL(url).pathname))); + + for (const file of existingFiles) { + if (!retainedFileNames.has(file)) { + await u.oss.deleteFile(`${name}/${file}`); + } + } + + for (const item of images) { + if (!item.startsWith("http")) await u.oss.writeFile(`${name}/${u.uuid()}.jpg`, item); + } + + res.status(200).send(success()); + } catch (err) { + res.status(500).send({ error: String(err) }); + } + }, +); diff --git a/src/routes/project/editVisualManual.ts b/src/routes/project/editVisualManual.ts index 142975f..6171620 100644 --- a/src/routes/project/editVisualManual.ts +++ b/src/routes/project/editVisualManual.ts @@ -35,21 +35,23 @@ export default router.post( } const mainPath = u.getPath(["skills", "art_prompts", name]); - + if (!fs.existsSync(mainPath)) { + return res.status(400).send(error("视觉手册不存在")); + } // 字段映射表(与 getVisualManual 保持一致) - const DATA_MAP: { label: string; value: string; subDir?: string }[] = [ - { label: "README", value: "README" }, - { label: "前缀", value: "prefix" }, - { label: "角色", value: "art_character", subDir: "art_prompt" }, - { label: "角色衍生", value: "art_character_derivative", subDir: "art_prompt" }, - { label: "道具", value: "art_prop", subDir: "art_prompt" }, - { label: "道具衍生", value: "art_prop_derivative", subDir: "art_prompt" }, - { label: "场景", value: "art_scene", subDir: "art_prompt" }, - { label: "场景衍生", value: "art_scene_derivative", subDir: "art_prompt" }, - { label: "分镜", value: "art_storyboard", subDir: "art_prompt" }, - { label: "分镜视频", value: "art_storyboard_video", subDir: "art_prompt" }, - { label: "技法-导演规划", value: "director_planning", subDir: "driector_skills" }, - { label: "技法-分镜表设计", value: "director_storyboard_table", subDir: "driector_skills" }, + const DATA_MAP: { value: string; subDir?: string }[] = [ + { value: "README" }, + { value: "prefix" }, + { value: "art_character", subDir: "art_prompt" }, + { value: "art_character_derivative", subDir: "art_prompt" }, + { value: "art_prop", subDir: "art_prompt" }, + { value: "art_prop_derivative", subDir: "art_prompt" }, + { value: "art_scene", subDir: "art_prompt" }, + { value: "art_scene_derivative", subDir: "art_prompt" }, + { value: "art_storyboard", subDir: "art_prompt" }, + { value: "art_storyboard_video", subDir: "art_prompt" }, + { value: "director_planning", subDir: "driector_skills" }, + { value: "director_storyboard_table", subDir: "driector_skills" }, ]; // 根据 DATA_MAP 构建 value -> subDir 的映射 diff --git a/src/utils/getArtPrompt.ts b/src/utils/getArtPrompt.ts index 08b0782..1b6d679 100644 --- a/src/utils/getArtPrompt.ts +++ b/src/utils/getArtPrompt.ts @@ -15,14 +15,19 @@ export function getArtPrompt(styleName: string, fileName: string): string { return ""; } + // 获取 prefix.md 内容 + const prefixFile = findFileRecursive(baseDir, "prefix.md"); + const prefixContent = prefixFile ? fs.readFileSync(prefixFile, "utf-8") : ""; + const target = fileName.endsWith(".md") ? fileName : `${fileName}.md`; const found = findFileRecursive(baseDir, target); if (!found) { - return ""; + return prefixContent; } - return fs.readFileSync(found, "utf-8"); + const fileContent = fs.readFileSync(found, "utf-8"); + return prefixContent ? `${prefixContent}\n${fileContent}` : fileContent; } /** * 传入风格目录名,获取该风格下所有 .md 文件内容,按文件名映射返回