import { Knex } from "knex"; import { v4 as uuid } from "uuid"; interface TableSchema { name: string; builder: (table: Knex.CreateTableBuilder) => void; initData?: (knex: Knex) => Promise; } export default async (knex: Knex, forceInit: boolean = false): Promise => { const tables: TableSchema[] = [ // 用户表 { name: "o_user", builder: (table) => { table.integer("id").notNullable(); table.text("name"); table.text("password"); table.primary(["id"]); table.unique(["id"]); }, initData: async (knex) => { await knex("o_user").insert([{ id: 1, name: "admin", password: "admin123" }]); }, }, //项目表 { name: "o_project", builder: (table) => { table.integer("id"); table.string("projectType"); table.string("imageModel"); table.string("videoModel"); table.text("name"); table.text("intro"); table.text("type"); table.text("artStyle"); table.text("videoRatio"); table.integer("createTime"); table.integer("userId"); table.primary(["id"]); }, }, //风格表 { name: "o_artStyle", builder: (table) => { table.integer("id").notNullable(); table.string("name"); table.text("fileUrl"); table.text("label"); table.text("prompt"); table.primary(["id"]); table.unique(["id"]); }, initData: async (knex) => {}, }, //Agent配置表 { name: "o_agentDeploy", builder: (table) => { table.integer("id").notNullable(); table.string("model"); table.string("key"); table.string("modelName"); table.integer("vendorId"); table.string("desc"); table.string("name"); table.boolean("disabled").defaultTo(false); table.primary(["id"]); table.unique(["id"]); }, initData: async (knex) => { await knex("o_agentDeploy").insert([ { model: "", modelName: "", vendorId: null, key: "scriptAgent", name: "剧本Agent", desc: "用于读取原文生成故事骨架、改编策略,建议使用具备强大文本理解和生成能力的模型", disabled: false, }, { model: "", modelName: "", vendorId: null, key: "productionAgent", name: "生产Agent", desc: "对工作流进行调度和管理,建议使用具备较强的逻辑推理和任务管理能力的模型", disabled: false, }, { model: "", modelName: "", vendorId: null, key: "universalAgent", name: "通用Agent", desc: "用于小说事件提取、资产提示词生成、台词提取等边缘功能,建议使用具备较强文本处理能力的模型", disabled: false, }, { model: "", modelName: "", vendorId: null, key: "ttsDubbing", name: "TTS配音", desc: "根据剧本内容生成角色配音,支持多种声音风格和情绪", disabled: true, }, ]); }, }, //设置表 { name: "o_setting", builder: (table) => { table.text("key"); table.text("value"); table.primary(["key"]); table.unique(["key"]); }, initData: async (knex) => { await knex("o_setting").insert([ { key: "tokenKey", value: uuid().slice(0, 8), }, { key: "messagesPerSummary", value: 3, }, { key: "shortTermLimit", value: 5, }, { key: "summaryMaxLength", value: 500, }, { key: "summaryLimit", value: 10, }, { key: "ragLimit", value: 3, }, { key: "deepRetrieveSummaryLimit", value: 5, }, { key: "modelOnnxFile", value: '["all-MiniLM-L6-v2", "onnx", "model_fp16.onnx"]', }, { key: "modelDtype", value: "fp16", }, ]); }, }, //任务中心表 { name: "o_tasks", builder: (table) => { table.integer("id").notNullable(); table.integer("projectId"); table.string("taskClass"); table.string("relatedObjects"); table.string("model"); table.text("describe"); table.string("state"); table.integer("startTime"); table.text("reason"); table.primary(["id"]); table.unique(["id"]); }, initData: async (knex) => {}, }, //小说原文表 { name: "o_novel", builder: (table) => { table.integer("id").notNullable(); table.integer("chapterIndex"); table.text("reel"); table.text("chapter"); table.text("chapterData"); table.integer("projectId"); table.integer("eventState"); table.text("event"); table.text("errorReason"); table.integer("createTime"); table.primary(["id"]); table.unique(["id"]); }, }, //小说事件表 { name: "o_event", builder: (table) => { table.integer("id").notNullable(); table.string("name"); table.string("detail"); table.integer("createTime"); table.primary(["id"]); table.unique(["id"]); }, }, //事件-章节表 { name: "o_eventChapter", builder: (table) => { table.integer("id").notNullable(); table.integer("eventId").unsigned().references("id").inTable("o_event"); table.integer("novelId").unsigned().references("id").inTable("o_novel"); table.primary(["id"]); table.unique(["id"]); }, }, //大纲表 { name: "o_outline", builder: (table) => { table.integer("id").notNullable(); table.integer("episode"); table.text("data"); table.integer("projectId"); table.primary(["id"]); table.unique(["id"]); }, }, //大纲-原文表 { name: "o_outlineNovel", builder: (table) => { table.integer("id").notNullable(); table.integer("outlineId").unsigned().references("id").inTable("o_outline"); table.integer("novelId").unsigned().references("id").inTable("o_novel"); table.primary(["id"]); table.unique(["id"]); }, }, //剧本 { name: "o_script", builder: (table) => { table.integer("id").notNullable(); table.text("name"); table.text("content"); table.integer("projectId"); table.integer("createTime"); table.primary(["id"]); table.unique(["id"]); }, }, //资产表 { name: "o_assets", builder: (table) => { table.integer("id").notNullable(); table.text("name"); table.text("prompt"); table.text("remark"); table.text("type"); table.text("describe"); table.integer("scriptId"); //剧本id table.integer("imageId").unsigned().references("id").inTable("o_image"); table.integer("assetsId"); table.integer("projectId"); table.integer("startTime"); table.primary(["id"]); table.unique(["id"]); }, initData: async (knex) => {}, }, //生成图片表 { name: "o_image", builder: (table) => { table.integer("id").notNullable(); table.text("filePath"); table.text("type"); table.integer("assetsId"); table.text("model"); table.text("resolution"); table.text("state"); table.primary(["id"]); table.unique(["id"]); }, }, //分镜 { name: "o_storyboard", builder: (table) => { table.integer("id").notNullable(); table.integer("scriptId"); table.text("title"); table.text("prompt"); table.text("description"); table.text("filePath"); table.text("model"); table.text("mode"); table.text("duration"); table.text("resolution"); table.text("frameMode"); table.text("camera"); table.text("sound"); table.text("lines"); table.text("state"); table.text("reason"); table.text("index"); table.integer("createTime"); table.primary(["id"]); table.unique(["id"]); }, }, //flowData-剧本 { name: "o_agentWorkData", builder: (table) => { table.integer("id").notNullable(); table.integer("projectId"); table.integer("episodesId"); table.string("key"); //用户其他方式索引 table.string("data"); table.integer("createTime"); table.integer("updateTime"); table.primary(["id"]); table.unique(["id"]); }, }, //视频 { name: "o_video", builder: (table) => { table.integer("id").notNullable(); table.text("filePath"); table.text("errorReason"); table.integer("time"); table.text("state"); table.integer("scriptId"); table.integer("storyboardId"); table.primary(["id"]); table.unique(["id"]); }, }, { name: "o_videoConfig", builder: (table) => { table.integer("id").notNullable(); table.integer("storyboardId"); table.integer("videoId"); table.integer("audio"); // 声音 table.text("model"); // 模型 table.text("mode"); // 模式: table.text("data"); // 所选数据集图片 JSON table.text("resolution"); // 分辨率 table.integer("duration"); // 时长 table.text("prompt"); // 提示词 table.integer("createTime"); // 创建时间 table.integer("updateTime"); // 更新时间 table.primary(["id"]); table.unique(["id"]); }, }, //供应商配置表 { name: "o_vendorConfig", builder: (table) => { table.integer("id").notNullable(); table.text("name"); table.text("version"); table.text("icon"); table.text("inputs"); // 输入项配置 JSON table.text("inputValues"); // 输入项值 JSON table.text("models"); // 模型配置 JSON table.text("code"); // 模型配置 JSON table.integer("createTime"); table.primary(["id"]); table.unique(["id"]); }, }, //供应商配置表 { name: "o_vendorConfig", builder: (table) => { table.integer("id").notNullable(); table.text("name"); table.text("version"); table.text("icon"); table.text("inputs"); // 输入项配置 JSON table.text("inputValues"); // 输入项值 JSON table.text("models"); // 模型配置 JSON table.text("code"); // 模型配置 JSON table.integer("createTime"); table.primary(["id"]); table.unique(["id"]); }, }, //记忆表(message=原始消息, summary=压缩摘要) { name: "memories", builder: (table) => { table.text("id").notNullable(); table.text("isolationKey").notNullable(); // 记忆隔离键 table.text("type").notNullable(); // 'message' | 'summary' table.text("role"); // 'user' | 'assistant' table.text("content").notNullable(); table.text("embedding"); // 向量嵌入 JSON table.text("relatedMessageIds"); // summary关联的message id列表 JSON table.integer("summarized").defaultTo(0); // message是否已被总结 0/1 table.integer("createTime").notNullable(); table.primary(["id"]); table.index(["isolationKey", "type"]); table.index(["isolationKey", "summarized"]); }, }, //图片工作流表 { name: "o_imageFlow", builder: (table) => { table.integer("id").notNullable(); table.text("flowData").notNullable(); table.integer("storyboardId"); table.integer("assetsId"); table.primary(["id"]); table.unique(["id"]); }, }, { name: "o_assets2Storyboard", builder: (table) => { table.integer("storyboardId").notNullable(); table.integer("assetId").notNullable(); table.primary(["storyboardId", "assetId"]); table.unique(["storyboardId", "assetId"]); }, }, { name: "o_scriptAssets", builder: (table) => { table.integer("scriptId").notNullable(); table.integer("assetId").notNullable(); table.primary(["scriptId", "assetId"]); table.unique(["scriptId", "assetId"]); }, }, ]; for (const t of tables) { const tableExists = await knex.schema.hasTable(t.name); if (!tableExists || forceInit) { if (tableExists && forceInit) { await knex.schema.dropTable(t.name); console.log("[初始化数据库] 已存在表删除并重建:", t.name); } else { console.log("[初始化数据库] 创建数据表:", t.name); } await knex.schema.createTable(t.name, t.builder); if (t.initData) { await t.initData(knex); console.log("[初始化数据库] 表数据初始化:", t.name); } } } };