# sucaigongfang 调研报告 > 调研对象:`参考项目/sucaigongfang/`(「素材工坊」图片素材自动化生产系统,v1.0.0,git `main` @ `d1b01fa`,2026-09-26) > 调研方式:只读代码勘察 + 文档比对,**以代码为准**。未执行 `pnpm install` / 构建 / 迁移 / 长任务。 > 对照文档:`docs/PRD/平台电商素材生产平台_PRD_V0.1.md` 的 1.1(P-01~P-07)与 1.2(功能框架)。 > 口径约定:LOC/文件数均不含 `node_modules`、`dist`、`.cache`、`.npm-cache`、`uploads` 内二进制。 --- ## 0. 结论速览 **一句话定位**:素材工坊是一套**面向信息流广告投放(电商唤端 + 券商)的图片素材批量生产线**——设计师上传 PSD 模板、优化师上传商品图并填写字段,系统按「模板 × 商品图」交叉批量合成图片,自动命名、打包、通知,并配套完整的账号/权限/客户/配置/任务治理体系。 **架构形态**:pnpm workspace 双包。后端 Express 5 + Sequelize 6 + PostgreSQL + Redis/BullMQ + `ag-psd`/`@napi-rs/canvas`/`sharp`/`fontkit` 渲染 + `@imgly/background-removal-node`(ONNX) 抠图 + `ws` 实时推送;前端 React 19 + Arco Design + Tailwind 4 + zustand + axios + react-router 7 + Vite 8。**API 与 BullMQ Worker 同进程**(`backend/src/app.ts:65-66`、`app.ts:402`),单容器部署,文件存储为**本地磁盘**(`backend/uploads`,854MB/775 文件在本机副本)。 **成熟度**: | 维度 | 结论 | 证据 | | --- | --- | --- | | 版本/形态 | v1.0.0,可部署的生产态(容器化 + 1Panel 反代 + 回滚脚本) | `package.json:1-3`、`deploy/docker-compose.yml:1-40`、`deploy/deploy.sh:1-30` | | 提交活跃度 | 249 commits,2026-03-23 → 2026-09-26;3 月 85 / 4 月 33 / **9 月 131**(5–8 月空档) | `git log --format='%ad' --date=format:'%Y-%m'` | | 代码规模 | 后端 `src` 30,835 行 / 137 文件;前端 `src` 32,797 行 / 74 文件;测试 2,078 行 | 见 §6 口径命令 | | 测试 | `node:test` 175 个用例 / 14 文件,**全部纯函数,无 DB/HTTP 集成测试、无 E2E** | `backend/tests/*.test.ts`、`package.json:24` | | 质量门禁 | 本地 CI 6 步硬卡:prettier/eslint/单测/前端设计 token 静态校验/tsc/构建 | `deploy/ci-local.sh:24-45` | | 部署 | 前后端双镜像 + host/bridge 网络 + Node 24-slim + nginx 反代 `/api` `/uploads` `/ws` | `backend/Dockerfile`、`frontend/Dockerfile`、`frontend/nginx.conf.template:20-40` | | 文档 | `docs/` 33 篇 md + 4 份方舟 PDF,但 **`docs/` 整个目录被 .gitignore 忽略(110 个文件 0 入库)** | `.gitignore:122`、`git ls-files docs` = 0 | **结论表(能力域 → 复用结论)** | 能力域 | 复用结论 | 关键原因 | 粗估人日 | | --- | --- | --- | --- | | 后端工程地基(Express/Sequelize/PG/迁移/日志/健康检查) | 直接复用 | 结构规整;umzug 迁移已落地;日志/健康检查/进程兜底齐全 | 3–5 | | 认证与账号安全(JWT/强制改密/口令策略/登录限流/口令审计) | 直接复用 | 服务端强制、令牌版本失效、一次性随机口令,安全设计超标 | 2–3 | | RBAC 角色权限 + 客户级数据范围 | 直接复用 | 权限 key 目录 + `fail-closed` 客户范围双层 | 3–4 | | 系统配置框架(40 项 / 10 模块 / L1-L3 / 审计 / 专用控件) | 直接复用 | 声明式注册表,是「加配置不写迁移」的成熟范式 | 3–5 | | 模板/分类/标签/规格管理 | 改造复用 | 结构可留,需加平台维度与 Logo/促销/展示方式资产 | 8–10 | | PSD 解析与渲染引擎 | 改造复用 | 核心资产(1773 行合成器),但**只支持单一商品图入参** | 12–18 | | 抠图(白底判定 + ONNX + 降级告警) | 直接复用 | 失败会标 `low_quality_warning`,不静默交付 | 2–3 | | 批量任务/队列/进度/暂停恢复取消重试/收尾 | 直接复用 | BullMQ + DB 计数 + WS 推送 + 失败口径分轮次,工程细节成熟 | 6–8 | | 命名规则引擎(变量模板/客户级/批次快照) | 直接复用 | 变量域与 PRD「自定义字段组合」高度一致 | 3–4 | | 导出 / ZIP 打包 / 下载 | 直接复用 | CSV 导出 + BullMQ 异步打包 + 选集下载 | 3–4 | | 通知 / 站内信 / WebSocket | 改造复用 | 通知模板可配;缺独立通知中心页(仅文档) | 3–4 | | 任务事件 / 失败目录 / 时间线 / 任务日志 | 直接复用 | 事件目录 + 失败目录 + 90 天保留期,是统一平台的可观测底座 | 3–4 | | 文件存储 / 缩略图 / 清理 | 改造复用 | **纯本地磁盘、无 OSS**,需加存储适配层 | 4–6 | | 前端设计体系 / 布局 / 守卫 / 共享组件 | 改造复用 | token 单点 + 自动校验;但菜单与权限映射硬编码 | 5–7 | | 素材制作页(2625 行) | 服务端重写 | 需按「平台 → 选品 → 展示方式/Logo/促销」重构 | 10–15 | | 首页 Dashboard | 服务端重写 | 指标口径(单套成本/模板占比/提审通过率)全部变化 | 3–5 | | AI 生图 | 服务端重写 | **仅验证到 M2-0,无产品代码**;分支实现已声明作废 | 20–30 | | 数据字典 / 枚举 | 服务端重写 | 仅文档;分支实现未合并且已停用 | 3–4 | | 多平台 / 平台切换 | 新建 | 系统内**没有平台概念**(得物仅作示例客户名) | 5–8 | | 选品 / 原始物料获取 | 新建(另一项目迁移) | 该能力在 `dewu-material-Downloader`(Electron),本系统无爬取代码 | 15–25 | | 视频素材 AI 生产 | 新建 | 完全空白,无 ffmpeg/视频依赖 | 40+ | | 提审 | 新建 | 完全空白 | 8–12 | | 投放配置(DP/承接页) | 新建 | 完全空白 | 8–12 | > 粗估口径:按「熟悉本仓库的 1 名全栈 + 1 名前端」并行估算,仅作量级参考,**需与研发负责人校准**。可复用/改造部分合计约 **60–85 人日**;加上制作页与首页重写(13–20 人日),**图片产线地基落地约 75–105 人日**;再叠加 AI 生图(20–30)、多平台(5–8)、数据字典(3–4)、选品抓取(15–25)、提审(8–12)、投放配置(8–12)≈ 134–196 人日,计入约 20% 的联调/迁移余量后,统一平台 MVP(不含视频深水区)总量级约 **165–235 人日**。 --- ## 1. 项目定位与业务链路 ### 1.1 解决什么问题、给谁用 原 PRD 写得很直白:**信息流优化师在电商 APP 唤端项目中面临素材产能不足**,需要「设计师上传 PSD 模板 → 优化师一键替换产品图生成素材 → 支持批量」(`docs/PRD.md:5-14`)。用户角色只有三类:管理员 / 设计师 / 优化师(`docs/PRD.md:16-22`、`docs/用户使用手册.md:38-44`)。 因此本系统的真实定位是: - **面向「素材生产」而不是「素材投放」**:产出到「按命名规则落盘 + ZIP 打包下载」为止,不涉及账户搭建、DP、承接页、提审。 - **单客户 = 单个广告主/品牌方**(如「得物」「华泰证券」):`clients` 表是业务归属维度,**不是多租户**(同一套部署、同一个库,靠 `user_clients` + `created_by` 做数据范围收敛)。 - **设计师与优化师的协作边界被固化成「模板」**:模板承载图层契约,优化师只填字段和传商品图。 补充一个容易被忽略的事实:本系统同时服务**电商(得物)与券商(华泰证券等)两个行业**,AI 方向的全部验证文档都以券商素材为样本(免责声明、收益暗示、合规红线)。这与本次新 PRD 只聚焦「平台电商(得物/唯品会)」不同——**行业假设不一致,AI 侧结论不能整体平移**。 ### 1.2 主链路(代码实证) ``` ① 管理员:建客户 → 建用户并绑定客户/角色 →(可选)配置客户级命名规则 ② 设计师:上传 PSD → 系统解析图层(ag-psd)→ 逐图层绑定「图片替换/文本替换/剪贴蒙版」 → 预览图生成 → 归属客户/分类/标签/规格 ③ 优化师:素材制作页选模板(可多选)→ 上传商品图(≤100 张)→ 按文件名识别商品ID/名称/品牌 → 表格可编辑 → 选生产参数(抠图/尺寸限制/JPG/缩放/字号)→ 二次确认提交 ④ 系统:建批次(N 模板 × M 商品图 交叉)→ 写命名快照 + 预渲染文件名 → 入 BullMQ ⑤ Worker:白底图自动抠图 → MaterialComposer 合成 → 写 materials → 更新批次统计 → WS 推送 ⑥ 收尾:批次完成 → 站内信通知 → 自动/手动触发 ZIP 打包 → 下载交付 ⑦ 管理:任务列表 / 任务详情抽屉 / 时间线 / 任务日志(跨用户只读)/ CSV 导出 ``` 主链路的关键代码:`productionBatchController.ts:646`(建批次)、`workers.ts:70-412`(素材 Worker)、`materialComposer.ts:143`(合成入口)、`workers.ts:764`(ZIP Worker)。 ### 1.3 与 PRD 1.1 的问题对应关系(P-01~P-07) | 编号 | PRD 问题 | 本系统覆盖度 | 说明 | | --- | --- | --- | --- | | P-01 | 原始物料:详情页图片人工下载、分散存放 | ❌ **0** | 无选品/抓取/物料库;商品图靠本地上传。该能力在**另一个参考项目** `dewu-material-Downloader`(Electron 桌面端) | | P-02 | 图片制作:三规格 × 四层信息全靠人工排版 | 🟡 **部分覆盖** | 已覆盖「基础框架模板 + 商品展示(单图)+ 产品信息(文本层)」,**但缺 AI 模特/脚模、Logo 组合、促销信息叠加**;规格是通用 `(w,h)` 实体,非固定的三档 | | P-03 | 视频制作依赖实拍与个人经验 | ❌ **0** | 全仓库无视频/ffmpeg 依赖与代码 | | P-04 | 命名提审:手工改名、进度散落 | 🟡 **命名全覆盖、提审 0** | 命名引擎(变量模板 + 客户级 + 快照 + 重试同名)非常成熟;**无提审清单/状态机** | | P-05 | 账户搭建:DP、承接页逐条复制 | ❌ **0** | 无任何 DP/承接页/链接配置能力 | | P-06 | 多平台:资源与口径不统一 | ❌ **0** | 系统无「平台」维度;`得物` 只出现在通知模板的示例文案里(`utils/notificationTemplates.ts:97`) | | P-07 | 资产管理:模板、脚本散落 | 🟡 **模板资产强、其余缺** | 模板/分类/标签/规格/批次模板完整;**Logo(仅标签 logo 字段,未进合成)、展示方式、脚本无** | **覆盖率小结**:PRD 七类问题里,本系统实质回答的是 **P-02(约一半)+ P-04(一半)+ P-07(约一半)**,其余四类需要新建或从另一项目迁移。 --- ## 2. 功能模块清单(全量) > 逐项核对方式:以 `backend/src/app.ts:147-164` 的 17 个 `*Routes` 挂载 + `frontend/src/App.tsx:100-120` 的路由表为准,辅以 `routes/*.ts` 内逐条 `router.`。 > 接口规模:**约 113 个业务接口**(按 `routes/*.ts` 的 `router.get/post/put/delete` 计数)+ `/api/health` + `/api/queue-health` + `/uploads` 静态 + `/ws`。 | 模块 | 前端路由 / 页面 | 后端 API 前缀 | 核心功能点 | 实现状态 | 关键文件:行 | | --- | --- | --- | --- | --- | --- | | 登录与账号安全 | `/login`、`/first-login-password` | `/api/auth` | JWT 登录、IP+用户名双维限流、首次登录强制改密、口令有效期三态策略、改密后旧令牌立即失效 | 完整 | `routes/authRoutes.ts:14-28`、`middlewares/auth.ts:18-32`、`utils/passwordPolicy.ts` | | 用户管理 | `/system/users` | `/api/users` | CRUD、重置密码(一次性随机口令仅显示一次)、角色分配、口令审计、最近登录、密码状态 | 完整 | `routes/userRoutes.ts:16-33`、`controllers/userController.ts` | | 角色与权限 | `/system/roles` | `/api/roles` | 角色 CRUD、权限勾选、启用/禁用、`isAdmin` 超管;前端菜单+路由双守卫 | 完整 | `utils/permissions.ts:4-20`、`components/PermissionGuard.tsx:8-21`、`AdminLayout.tsx:85-98` | | 客户与数据范围 | `/system/clients` | `/api/clients` | 客户 CRUD、用户↔客户多对多绑定、`client_scope_mode` 受限模式、客户级命名规则 Tab | 完整 | `models/Client.ts:79`、`models/UserClient.ts`、`utils/clientScopeCore.ts:12-22` | | 模板分类 | `/templates/categories` | `/api/categories` | 树形分类(父子自关联)、客户归属、CRUD | 完整 | `routes/categoryRoutes.ts:19-24`、`models/TemplateCategory.ts:81` | | 模板标签 | `/templates/tags` | `/api/tags` | 标签 CRUD、颜色、客户归属、**logo 上传(后端有接口,前端未接线)** | BETA | `routes/tagRoutes.ts:20-25`、`controllers/tagController.ts:21-37` | | 规格管理 | `/templates/specifications` | `/api/specifications` | 规格 CRUD;**名称由 (w,h) 派生**(`横版 1280×720`),唯一性从名称迁到尺寸 | 完整 | `utils/specificationName.ts:46-57`、`migrations/20260924000001-specification-name-derivation.ts` | | 模板管理 | `/templates/list`、`/upload`、`/:id/edit` | `/api/templates` | PSD 上传/解析/预览、图层绑定、按客户字段池自动匹配、复制模板、批量删除/改分类/加标签、字体列表 | 完整 | `routes/templateRoutes.ts:17-50`、`controllers/templateController.ts:178` | | 素材制作(核心页) | `/material-maker` | `/api/materials/*`、`/api/batches/*` | 模板筛选/搜索/最近使用/批次模板、商品图批量上传与文件名识别、字段表格可编辑、生产参数、提交前二次确认、悬停预览 | 完整 | `pages/Production.tsx:207-364`、`productionBatchRoutes.ts:12-66` | | 生产任务列表 | `/tasks` | `/api/batches` | 卡片/表格双视图、状态页签与 facet 计数、详情抽屉、暂停/恢复/取消/重试/补跑失败项/重置卡死、选集下载、批量操作 | 完整 | `pages/Tasks.tsx:467-700`、`components/BatchDetailDrawer.tsx` | | 批次模板(组合) | 制作页内保存/选用 | `/api/batch-templates` | 把「多模板组合」存为可复用快捷模板,支持私有/共享、客户归属 | 完整 | `routes/batchTemplateRoutes.ts:12-24`、`controllers/batchTemplateController.ts:16` | | 任务日志(管理端) | `/system/task-logs` | `/api/admin/batches` | 跨用户只读看板、事件流/时间线、失败分类、CSV 导出、执行人筛选 | 完整 | `routes/taskLogRoutes.ts:28-49`、`components/TaskLogBoard.tsx` | | 命名规则 | 客户弹窗「命名规则」Tab、制作页识别列 | `/api/naming` | 商品图识别模板、字段/别名/必填/可编辑、素材命名模板、ZIP 命名模板、实时预览与校验 | 完整 | `utils/namingConfig.ts:240-328`、`components/NamingConfigEditor.tsx` | | 导出 / 打包 / 下载 | `/tasks` 内按钮 | `/api/batches/export`、`/:id/tasks/download`、`/:id/package-zip` | CSV 导出(BOM 防乱码、上限 1 万条)、选中子任务 ZIP、整批 ZIP(BullMQ 异步 + 状态机) | 完整 | `productionBatchController.ts:1249`、`:2005`、`:2269`、`workers.ts:764` | | 通知(站内信) | 顶栏铃铛下拉 | `/api/notifications` | 列表/未读数/标记已读/全读/删除;三类触发(批次完成/失败/ZIP 就绪);标题与内容模板可在系统配置里改 | 完整(**无独立页面**) | `routes/notificationRoutes.ts:8-20`、`AdminLayout.tsx:126-133`、`utils/notificationTemplates.ts:130` | | 通知中心独立页 | — | — | 需求文档中的独立页面:筛选、批量已读/清空、下载关联 | **仅文档** | `docs/通知中心页面需求.md:1-80` | | 首页 Dashboard | `/` | `/api/stats/dashboard` | 5 张指标卡(模板数量 / 素材生产量 / 生产任务量 / 模板使用率 / 失败率,统一「本周 vs 上周」环比)、今日任务、待我处理(失败/暂停/进行中)、最近任务、快捷入口 | 完整 | `pages/Home.tsx:60-140`、`:367-443`、`controllers/statsController.ts:52-80` | | 系统配置 | `/system/config` | `/api/system/configs` | 10 模块 40 项配置、L1/L2/L3 生效级别、类型与范围校验、审计留痕、两个专用控件(命名规则/通知模板)、运行环境只读面板 | 完整 | `utils/systemConfigRegistry.ts:192`、`controllers/systemConfigController.ts`、`pages/SystemConfig.tsx` | | 存储管理 / 清理 | 系统配置 → 存储 | `/api/system/storage` | 分类用量统计、**先预览后清理**、默认只删孤儿文件、保护模板资产、定时清理(默认关) | 完整 | `utils/storageCleanup.ts:18-40`、`utils/storageCleanupScheduler.ts` | | 文件服务 | `/uploads/*` | — | JWT 鉴权(header 或 `?token=`)、按需缩略图 `?w=320/640` 落盘缓存、失败回退原图 | 完整 | `app.ts:139-144`、`middlewares/uploadAuth.ts`、`middlewares/uploadThumbnail.ts:9` | | 实时进度 | `/ws?token=` | `WS /ws` | 批次整行快照推送、300ms 按批次节流、心跳、鉴权失败码 4401、前端降级轮询 | 完整 | `utils/realtime.ts:31-40`、`hooks/useRealtimeBatches.ts` | | 素材历史记录 | —(`history` 路由被注释) | `/api/materials` | 分页查询、详情、重生成(复用生产链路入队) | **BETA:后端就绪,前端未接线** | `App.tsx:118-120`、`materialRoutes.ts:28-34` | | 数据字典 / 枚举 | — | — | 「字典类型 → 字典项」两层、系统管理入口、code/label 分离、供 AI 提示词消费 | **仅文档**(分支 `backup/ai-asset-m1` 有 625 行后端 + 999 行前端实现,未合并) | `docs/数据字典与枚举管理评估.md:1-40`、分支 `controllers/dictionaryController.ts` | | 合规词库 / 免责声明校验 | — | — | 禁用词扫描、免责声明逐字比对、AI 成品复核闸门 | **仅文档/分支**(未合并) | 分支 `controllers/complianceController.ts`、`utils/disclaimerCheck.ts` | | AI 生图(M2 路线) | — | — | 9 要素表单 → 提示词合成 → Seedream 出图 → 程序保证免责声明/logo → VLM 文字逐字校验 → 挑图 | **仅验证(M2-0)**,无产品代码 | `tools/ai-image-validation/README.md:3`、`docs/AI生图-M2-0验证记录.md:8-11` | | AI 素材生成(M1 路线) | —(分支) | —(分支 `/api/ai/*`) | 图片模板上传 → VLM 反推 → 人工框选绑定 → 冻结契约 → SchemaRenderer 出图 + AI 底图 | **实验/已作废**:53 commits、约 3.1 万行新代码,但最新方案明确「整体作废,不参考、不复用」 | `git log backup/ai-asset-m1`、`docs/AI生图相关功能-实现方案.md:6` | | 视频素材 | — | — | — | **不存在** | — | | 提审 | — | — | — | **不存在** | — | | 投放配置(DP/承接页) | — | — | — | **不存在** | — | | 多平台切换 | — | — | — | **不存在** | — | | 队列/依赖健康检查 | — | `/api/health`、`/api/queue-health` | DB/Redis 真实探活(失败 503)、队列积压/Worker 状态/WS 在线数/清理状态 | 完整 | `app.ts:175-237` | > 说明:「历史上传记录」与「生产任务列表」是两件事。**生产记录(批次)已完整实现**(`/tasks`);**素材级历史(`materials` 表)只有 API、没有页面**,`history` 路由在 `App.tsx:118-120` 被注释为「等待迁移」。 --- ## 3. 技术栈与架构 ### 3.1 版本清单(以 `package.json` 实际声明为准) | 层 | 技术 | 版本 | 备注 | | --- | --- | --- | --- | | 后端运行时 | Node.js | 24(`.nvmrc`) | 镜像 `node:24-slim` | | Web 框架 | Express | `^5.2.1` | 5.x,路由级中间件 | | ORM / DB | Sequelize / pg | `^6.37.8` / `^8.20.0` | PostgreSQL 18(生产由 1Panel 管理) | | 迁移 | umzug + SequelizeStorage | `^3.8.3` | 账本表 `SequelizeMeta`(`utils/migrator.ts:26`) | | 队列 | BullMQ / ioredis | `^5.71.0` / `^5.10.1` | 2 个队列:`material-generation`、`zip-packaging`;项目级键前缀 `sucaigongfang` | | PSD 解析 | ag-psd | `^30.1.0` | `initializeCanvas` 接 `@napi-rs/canvas` | | 画布 / 图像 | @napi-rs/canvas / sharp | `^0.1.97` / **`0.32.6`(锁定)** | sharp 锁版本,PNG 量化行为依赖该版本 | | 字体 | fontkit | `^2.0.4` | 读字体名 + `GlobalFonts` 注册 | | 抠图 | @imgly/background-removal-node | `^1.4.5` | 内含 onnxruntime-node(medium 模型) | | 上传 | multer | `^2.1.1` | 按请求构造实例以支持热改限制(`utils/configurableUpload.ts`) | | 打包 | archiver | `^7.0.1` | ZIP level 9 | | 实时 | ws | `^8.21.3` | 复用同一 `http.Server` | | 鉴权 | jsonwebtoken / bcrypt | `^9.0.3` / `^6.0.0` | JWT 带口令版本号 `pwdv` | | 前端框架 | React / react-router-dom | `^19.2.4` / `^7.13.1` | 路由级 lazy 分包 | | UI | @arco-design/web-react | `^2.66.12` | 全站指定 Arco + 自定义 token 层 | | 样式 | Tailwind CSS / tw-animate-css | `^4.2.2` / `^1.4.0` | Tailwind 4 + `styles/` token 双轨 | | 状态 | zustand | `^5.0.12` | 只有 2 个 store(auth / appConfig) | | HTTP | axios | `^1.13.6` | 单一 `services/api.ts`(1223 行,113 个方法) | | 图表 | recharts | `^3.8.1` | 首页趋势 | | 图标 | lucide-react | `^1.7.0` | 全站图标 | | 构建 | Vite / TypeScript | `^8.0.1` / `~5.9.3` | 前端 `tsc -b && vite build` | 依赖证据:`backend/package.json:29-47`、`frontend/package.json:20-45`。 ### 3.2 架构分层与请求链路 ``` ┌──────────────────────────── 浏览器(React 19 SPA)────────────────────────────┐ │ pages(16) ─ components(24) ─ services/api.ts(113 方法) ─ zustand(auth/appConfig) │ │ styles/tokens.ts(字号/颜色) + theme.ts(Arco ConfigProvider) + Tailwind 4 │ └───────────────┬───────────────────────────────────────────────┬─────────────────┘ │ /api/* (nginx → backend:8300) │ /ws?token= /uploads?w= ┌───────────────▼───────────────────────────────────────────────▼─────────────────┐ │ Express 5 (单进程,API + Worker 同进程) │ │ 中间件链: 请求日志 → CORS 白名单 → body → normalizeQuery → /uploads(鉴权+缩略图+static) │ │ → 17 个 Router(登录校验在 router 内;10 个涉及客户数据的再挂 attachClientScope)│ │ 兜底: multer 错误 → JSON 400;uncaughtException → exit(1) 交给容器重启 │ │ 业务: controllers(17, 9.7k 行) → utils(55, 14.2k 行) → models(20, 2.7k 行) │ │ 异步: BullMQ(material 2 并发 / zip 1 并发,可页面热调 L3) ──► workers(43.8k 行) │ │ 实时: realtime.ts 进程内 pub(批内 300ms 节流)──► ws 推送 │ │ 观测: taskEvents(事件目录) / failureCatalog(失败目录) / taskMonitor(超时卡住巡检) │ └───────┬──────────────────────┬───────────────────────┬──────────────────────────┘ │ PostgreSQL(19 表) │ Redis(2 队列 + 项目前缀) │ 本地磁盘 backend/uploads(~732MB) ``` **三个关键架构决策(都可直接继承)**: 1. **API 与 Worker 同进程**:换来「实时推送零跨进程成本 + 部署极简」,代价是**无法按 CPU/内存独立扩缩容**,且 `uncaughtException` 会同时打断 API(`app.ts:64-78` 注释明确承认)。 2. **按批次节流推整行快照**(`realtime.ts:20-40`):不做增量、不做 diff,漏收一条下一条即纠正;300ms 合并 + 发送时重读库。 3. **配置生效级别 L1/L2/L3**(`systemConfigRegistry.ts:33-45`):L1 请求级热生效、L2 新任务生效(批次快照)、L3 需重建运行时(由 `app.ts:276-299` 注册 applier)。**这是统一平台最值得整套照搬的机制之一**。 ### 3.3 构建与部署 - **镜像**:后端多阶段 `node:24-slim` + `pnpm build`(`backend/Dockerfile`);前端 builder → `nginx:1.27-alpine`,构建期注入 `VITE_API_BASE_URL`,`BUILD_INFO.txt` 做制品溯源(`frontend/Dockerfile:20-66`)。 - **网络**:backend 用 `host` 网络(监听宿主 8300),frontend 用 bridge + 端口映射(3300),容器内 nginx 经 `host.docker.internal` 反代后端(`deploy/docker-compose.yml:1-40`)。 - **反代**:SPA history 回退、`/api` `/uploads` `/ws` 转发、WS 必须 `map $http_upgrade`、`client_max_body_size 200M`、安全响应头(`frontend/nginx.conf.template:20-80`)。 - **CI**:`deploy/ci-local.sh` 6 步硬卡(格式 → lint → 单测 → 前端静态校验 → tsc → 构建),构建镜像前必须全绿。 - **发布**:`build.sh`(本地构建推送,`linux/amd64` 固定)→ `deploy.sh`(服务器拉镜像,先备份 DB 再部署,健康校验)→ `rollback.sh`。DB 备份脚本 `backup-db.sh`。 - **宿主机依赖**:PostgreSQL 18 / Redis 8.6.1 / OpenResty / SSL 均由 1Panel 管理,本项目不负责(`deploy/docker-compose.yml:3-8`)。 --- ## 4. 数据模型与持久化 ### 4.1 模型逐表清单(19 张表) 建表顺序由 `utils/schemaFromModels.ts:30-56` 的 `MIGRATION_MODELS` 固化(必须先满足外键依赖)。所有表均 `underscore` 列名、`timestamps`。 | # | 表名 | 关键字段 | 关系 / 用途 | | --- | --- | --- | --- | | 1 | `roles` | name, description, status(enum enabled/disabled), permissions(JSON), isAdmin, deleted_at | 角色;`isAdmin=true` 直接映射全部权限 | | 2 | `users` | username, password(bcrypt), role(legacy: admin/designer/optimizer), role_id, nickname, last_login_at, must_change_password, password_changed_at, password_version, client_scope_mode(all/restricted), deleted_at | 账号;口令版本 + 客户范围模式都在行上 | | 3 | `clients` | name, remark, **naming_config(JSONB)**, deleted_at | 客户(=广告主/品牌),客户级配置的载体 | | 4 | `user_clients` | user_id, client_id | 用户↔客户多对多,仅 `restricted` 模式生效 | | 5 | `template_categories` | name, parent_id, customer_id | 树形分类(自关联) | | 6 | `template_specifications` | name, width, height, orientation, description | 规格;`(width,height)` 唯一,名称派生 | | 7 | `template_tags` | name, color, logo_path, customer_id | 标签(多值、带颜色、客户级) | | 8 | `templates` | name, client_id, orientation, category_id, specification_id, psd_file_path, preview_image_path, **layer_config(JSON)**, created_by | 模板主表;`layer_config` 是图层契约 | | 9 | `template_tag_relations` | template_id, tag_id | 模板↔标签中间表(另有 `tagsFilter` 别名专用于筛选,`associations.ts:67-74`) | | 10 | `materials` | template_id, product_image_path, output_image_path, product_id, material_number, output_filename, extra_fields(JSONB), created_by, batch_id, batch_task_id, status, error_message, retry_count, low_quality_warning | 单张产出物;`low_quality_warning` 记录「抠图降级/压缩不足」 | | 11 | `production_batches` | batch_number, task_type(single/batch), status, priority, client_id, created_by, template_ids(INT[]), total/success/failed_count, estimated/actual_duration, zip_file_path, zip_status, started_at, completed_at, **auto_cutout/size_limit/force_jpg/jpg_quality/png_colours/scale_mode/font_size**, **naming_snapshot(JSONB)** | 批次;**生产参数与命名规则在创建时快照**(L2 语义) | | 12 | `batch_templates` | name, description, template_ids(INT[]), client_id, created_by, is_shared, deleted_at | 模板组合快捷方式 | | 13 | `batch_tasks` | batch_id, template_id, product_image_path, product_id, product_name, brand_name, extra_fields(JSONB), **planned_filename**, status(pending/processing/completed/failed), error_code, error_message, retry_count, started_at, completed_at | 子任务;`planned_filename` 建单时预渲染(保证重试同名) | | 14 | `notifications` | user_id, type, title, content, batch_id, is_read | 站内信 | | 15 | `sequence_numbers` | date, current_seq | 每日批次号自增(`YYMMDD` + 3 位) | | 16 | `system_configs` | config_key, config_value(JSONB), value_type, updated_by | 只存「用户改过的值」,默认值在注册表 | | 17 | `system_config_audits` | config_key, old_value, new_value, operator | 配置变更审计(**完整存明文,没有脱敏**) | | 18 | `user_audits` | action, target_username, operator_name, detail | 口令/账号审计(**刻意不建外键**,审计要比被审计者活得久) | | 19 | `task_events` | batch_id, batch_number, batch_task_id, level(batch/task), event_type, status_from/to, failure_code/message, detail, metadata(JSONB), actor/creator/client 快照 | 任务事件流;快照冗余 + 90 天保留期 | 关系总图由 `models/associations.ts:17-236` 统一定义;删除策略:`batch_tasks` CASCADE 于批次,`notifications` CASCADE 于用户、SET NULL 于批次。 ### 4.2 迁移机制 - **umzug 迁移**,不是 `sequelize.sync`:`DBSync.sync()` 启动即 `runMigrations()`(`utils/dbSync.ts:24-40`),账本 `SequelizeMeta`,迁移文件名去扩展名做身份(避免 ts/js 双执行,`migrator.ts:56-63`)。 - **11 个迁移 / 778 行**(`backend/src/migrations/`),全部幂等、带 `down`。初始迁移复用模型定义生成 DDL(`schemaFromModels.createSchemaFromModels`),避免模型与 DDL 两份漂移。 - **为什么不 alter**:实测开发库累积到「1112 个唯一约束 / 1154 个索引 / 18MB」,每次启动 ACCESS EXCLUSIVE 锁(`utils/migrator.ts:8-23`)。 - **初始化脚本**:`scripts/init-admin.ts`(管理员引导,口令来自 `ADMIN_INITIAL_PASSWORD`,无内置口令)、`scripts/migrate.ts`(migrate/status/baseline/rollback)、`scripts/backfill-password-flags.ts`、`scripts/clean-data.ts`、`scripts/clear-queue.ts`、`scripts/redis-namespace.ts`。 - **枚举/字典**:**没有独立字典表**。规格是专表;状态/类型用 TS 字面量联合类型 + 目录文件(`failureCatalog.ts`、`taskEventCatalog.ts`);字典化只做到文档层(见 §2 数据字典行)。 ### 4.3 文件存储 | 目录 | 用途 | 是否受保护 | 证据 | | --- | --- | --- | --- | | `uploads/psd` | 模板 PSD 源文件 | ✅ 永不清理 | `storageCleanup.ts:65` | | `uploads/preview` | PSD 解析后的预览 PNG(无损,平均 829KB,最大 2.37MB) | ✅ | `imageThumbnail.ts:22-32` | | `uploads/product/` | 商品原图 + 抠图中间产物 | ❌ 可清理 | `workers.ts:203` | | `uploads/output/` | 生成素材 | ❌ 可清理 | `workers.ts:234` | | `uploads/zip/` | ZIP 交付包 | ❌ 可清理(有保留天数) | `workers.ts:830-840` | | `uploads/tag-logos` | 标签 logo | ✅ | `tagController.ts:24` | | `uploads/temp` | 上传临时区 | ❌ 可清理 | `storageCleanup.ts:36-40` | | `uploads/thumb/` | 按需缩略图缓存(WebP q80) | ✅(随源删除 + 孤儿清扫) | `imageThumbnail.ts:40-60` | - **对外 URL 规则**:`/uploads/<相对路径>`,JWT 鉴权(`Authorization` 头或 `` 场景 `?token=`),追加 `?w=320|640` 返回 WebP 缩略图,失败静默回退原图(`middlewares/uploadThumbnail.ts:9-30`)。 - **容量与清理**:代码注释记录实测 732MB(psd 417M / output 146M / product 62M / zip 59M / preview 47M);本机副本 854MB / 775 文件。清理策略为「先预览、只删文件、绝不碰模板资产、默认只删孤儿」(`storageCleanup.ts:16-40`),可配保留天数 + 定时清理(默认关)。 - **路径安全**:所有客户端传入的相对路径必须过 `resolveUploadPath`(`..`/绝对路径/NUL 全拒绝),历史上曾有任意文件读取漏洞(`utils/uploadPath.ts:1-20`)。 ### 4.4 Redis 用途与任务状态机 - **Redis 只做 BullMQ**:2 个队列(`material-generation` / `zip-packaging`)、项目级键前缀 `sucaigongfang`、可配 db(`REDIS_DB` 真正生效)。**限流用进程内存**(非 Redis),因此水平扩容会失效(`middlewares/rateLimit.ts:11-18` 自述待改)。 - **批次状态机**(`models/ProductionBatch.ts:12`): `pending → processing → completed | failed`,旁支 `paused`(可 resume)与 `cancelled`(不可被收尾覆盖)。 - **结束态判定唯一来源** `utils/batchStatus.ts`:`completed|failed` 才算结束;`failed_count>0` 即 `failed`;`cancelled` 不允许被收尾改写(`batchStatus.ts:12-80`)。 - **子任务状态**:`pending → processing → completed|failed`;BullMQ `attempts=3` 时**中间轮次保持 processing**,只有最终失败才计入 `failed_count`(`workers.ts:324-401`,这是踩过「失败数按尝试次数累加 → 进度 300%」坑后的修正)。 - **ZIP 状态**:`none → generating → ready|failed`(`ProductionBatch.ts:24`)。 --- ## 5. 关键机制 ### 5.1 PSD 模板:上传 → 解析 → 图层约定 → 渲染合成 **① 解析**(`utils/psdParser.ts`) - `readPsd(buffer)` + `initializeCanvas(@napi-rs/canvas)`;递归 `children` 提取图层(`psdParser.ts:104-161`)。 - 只识别 **3 类前缀**:`image_replacement_`(产品图替换)、`text_replacement_`(文本替换)、`clipping_mask_`(剪贴蒙版容器)(`psdParser.ts:123-147`)。 - 预览图:`psd.canvas → sharp().png()` 原尺寸无损落盘(`psdParser.ts:170-236`)。 **② 图层契约**(`models/Template.ts:10-34`) ```ts LayerConfig { imageReplacements: [{ layerName, type, description, linkedContentLayer?, linkedClippingMask?, // 剪贴蒙版双向关联 scaleMode?: 'fill'|'contain'|'stretch' }] textReplacements: [{ layerName, field(deprecated), textTemplate?, hideWhenEmpty?, description, font?, fontSize? }] } ``` **③ 文本图层 ↔ 客户字段匹配**(`utils/layerFieldMatcher.ts:1-20`,四层优先级) 1. `placeholder`:图层名直接写模板,如 `text_replacement_标题-{品牌名称}{商品名称}`(零配置,推荐给设计师); 2. `key`:去掉前缀等于字段名(`text_replacement_商品名称`); 3. `alias`:等于字段别名(`spmc`/`title`/`sku`/`pp`,不区分大小写); 4. `fuzzy`:含「标题/名称/title」等关键词兜底; 5. 未命中返回 null,**不猜测**(避免把静态文案层错绑成商品名)。 **④ 渲染合成**(`utils/materialComposer.ts`,1773 行,本仓库最核心资产) - 入口 `compose(psdPath, layerConfig, productImagePath, outputPath, fields, forceJpg, sizeLimit, cutoutFailed, renderOptions)`(`materialComposer.ts:143`)。 - 流程:`readPsd` 全量图层 → 先渲染「非替换图层」到临时画布(用于取真实背景色)→ **以 PSD 合成 canvas 为底** → 处理替换图层(剪贴蒙版/真实背景/叠加/仅替换 四条分支)→ 输出编码。 - 支持:图层不透明度、**15 种混合模式**(multiply/screen/overlay/…/luminosity,`materialComposer.ts:427-477`)、隐藏图层跳过、剪贴蒙版 alpha 与形状蒙版、scaleMode 三档(fill/contain/stretch)。 - 文本渲染:`buildCanvasFont` 按 CSS 规则构造字体简写(**踩过引号不成对导致 `@napi-rs/canvas` Rust 层 panic → SIGABRT → 容器反复重启**的坑,`materialComposer.ts:100-125`);默认字体栈把项目内置中文字体放在最前,避免 node:24-slim 无中日韩字形时渲染成「豆腐块而任务仍报成功」。 - 输出约束:JPG 用**二分搜索质量**逼近大小上限;PNG 因 sharp 0.32.6 的 `quality` 无效,改为**调色板 colours 逐级减半**(`materialComposer.ts:258-357`);压不下去时返回 `lowQualityWarning` 而不是假报成功。 - **已知限制(对 PRD P-02 致命)**:所有 `imageReplacements` 图层共用**同一个 `productImageBuffer`**(`materialComposer.ts:222-236`、`516-620`),即「同一张商品图贴到多个位置」。**不支持**按图层注入不同资产(Logo/促销图/AI 模特/脚模),`type` 字段除 `clipping_mask` 外被忽略。 ### 5.2 图片生成流水线(当前唯一真正可跑的产线) ``` 上传商品图(≤100) → 按客户命名模板识别文件名(latin1→utf8 乱码修复) → 前端可编辑表格(商品图/商品ID/商品名称/品牌) → 校验必填(商品ID) → POST /api/batches(事务内建批次+子任务;命名快照;预渲染 planned_filename) → 事务外入队(失败不回滚,可用「重试」补救) → Worker:白底判定(ImageTypeDetector) → 是白底则 imgly 抠图 → 抠图失败/产物缺失 ⇒ 回退原图 + cutoutFailed=true(写 low_quality_warning) → MaterialComposer.compose → 写 materials → 更新批次计数 → publishBatchUpdate(WS) → checkBatchCompletion → 有失败项=skipped failed / 全成功=completed → 站内信(成功/失败模板可配)+ 自动 ZIP 打包 → 下载 ``` 关键点: - **交叉批量**:`totalTasks = templateIds.length * products.length`,双层循环(`productionBatchController.ts:839-840`、`:888-890`、`:948-950`)。 - **抠图是「条件触发 + 可降级 + 有痕迹」**:仅白底图抠(`workers.ts:193-230`),失败标记低质量而不是静默成功——这是全项目「不静默交付」原则的典型。 - **并发与内存**:素材 Worker 默认 **2 并发**(可页面 L3 热改 1–4),因为 API 与 Worker 同进程,抠图峰值可能吃掉数 GB(`workers.ts:52-66`)。 - **任务监控**:超时任务、ZIP 卡住由 `taskMonitor.ts` 巡检(默认 30 分钟 / 10 分钟,可配),恢复卡死批次。 - **重试语义**:BullMQ 自动重试 3 次 + 手动「重试失败项/继续执行」,`planned_filename` 保证重试同名不产生重复文件。 ### 5.3 AI 生图:M0 / M1 / M2 三条线的真实结论 > **一句话:AI 能力目前全部停留在「验证工作台 + 文档」,`main` 分支没有任何 AI 产品代码;另有一条已声明作废的 M1 实现分支。** **证据基线**:`grep -rniE "seedream|volc|方舟|ai.?image|aigc" backend/src frontend/src` **零命中**;`docs/AI生图相关功能-实现方案.md:4` 明确写「基线:main 分支现状(没有任何 AI 代码)+ 从零新写」。 #### M0 · VLM 反推 / 图层分离可行性(`tools/vlm-lab/`,10,469 行,已入库) - 模型:A=`doubao-seed-2-1-pro-260915`、B=`qwen3.8-max-0902`;34 条原始记录。 - 结论(`docs/AI素材生成-M0验证记录.md:10-30`):坐标精度 IoU 几何 0.968/0.982、文字 0.819/0.833(达标);坐标格式**可被提示词控制**(11/11 遵从归一化);`json_schema` 严格模式 100% 合法;**图层分离真实跑通 96s 返回 12 图层**;免责声明可逐字锁定。 - 未达标:**换文案鲁棒性(溢出率 16.7%,未达 ≤5%)**、单张耗时(86–110s,关思考后 31.3s 达标)。对策是字段加 `maxChars`,不是放宽阈值。 - 状态:**验证完成,未产品化**。 #### M1 · 图片模板(非 PSD)契约 + 冻结 + Schema 渲染(分支 `backup/ai-asset-m1-20260923-1451`) - 规模:**53 个提交**,相对 main 净增约 4.8 万行;其中新增文件 **后端 ~19,455 行 / 前端 ~11,928 行**(`aiTemplateController` 1341、`schemaRenderer` 2748、`layerMerge` 1200、`imageTemplateRenderer` 774、`aiProvider` 547…),分支整体约 10 万行。 - 目标(`docs/AI素材生成-M1正式开发方案与计划.md:16-25`):设计师传普通图片 → VLM 反推 → 人工框选确认 → 冻结契约 → 优化师填文案出图(≤5 分钟 + ≤3 分钟)。 - 已实现多条产品链路(合规闸门、警示语逐字比对、擦除遮罩、自由改图、品牌资产逐像素贴回等,见 `git log main..branch`)。 - **但已被最新方案整体否决**:`docs/AI生图相关功能-实现方案.md:6` 写明「M1 的文档与代码**整体作废,不参考、不复用**」,原因是需求方改口径为「生成图即成品,不套模板、不做契约/冻结/图层绑定」。 - 状态:**实验分支,不可作为交付物**(但不排除局部算法/经验可借鉴,如擦除遮罩、文字校验、合规词库)。 #### M2 · Seedream 表单式生图(`tools/ai-image-validation/`,2,122 行,已入库) - 模型:`doubao-seedream-5-0-pro-260628` / `doubao-seedream-5-0-flash-260915`(`lib/config.mjs:27-31`);读图校验用 `doubao-seed-2-1-pro` 或 `qwen3.8-max` 交叉验证。 - 实测(34 张真实生成,34/34 成功;`docs/AI生图-M2-0验证记录.md:46-66`): | 验证项 | 结论 | | --- | --- | | 中文文字逐字正确率 | ✅ 凡提示词写明文案的全部逐字正确(含中英数混排、6 卡片 9 处文案) | | `background=transparent` | ✅ 成立(四角 alpha=0,全透明 70–78%),但输出有 20–28% 半透明像素,贴回**不能硬阈值抠图** | | 多图参考融合(≤10 张) | ✅ 真融合(背景取风格图、主体取主体图) | | flash vs pro | ✅ 文字/透明底打平,输出 token 相同(16428/张),**flash 快约 3 倍**(19–25s vs 55–76s)→ 建议默认 flash | | 文字校验闸门 | ⚠️ 机制有效(5 个真错字样本全拦住、0 漏检;读图失败返回 `unchecked` 而非 `passed`),但「漏检率为 0」**不应作为上线门槛** | | 账号级限流 | ⚠️ **真实存在**(撞过 `429 RPM limit exceeded`),产品化必须自己排队(工作台用 12s 串行节流) | - **产品化差距**:M2-A 之后才写 `backend/` 代码(`tools/ai-image-validation/README.md:3-5`),当前**零集成**:没有 `ai` 配置模块、没有 AI 数据表、没有 AI 队列、没有前端页面。 - **成本**:**待确认**。M2-0 明确写「flash 单张价格待补,需把价格做成可配置项用于成本预估」(`docs/AI生图-M2-0验证记录.md:134-135`)。可确定的是:flash 单张 19–25s、输出 16,428 token/张;文字校验是**每张一次额外 VLM 调用**,会让耗时与费用接近翻倍(`docs/AI生图相关功能-实现方案.md:522`)。 - **密钥约束**:AI API Key **不能放系统配置**——`ConfigValueType` 没有 `secret` 类型,且 `SystemConfigAudit` 完整存 `oldValue/newValue` 不脱敏,Key 会长期明文留痕(`docs/AI素材生成-与现有系统整合方案.md:122-155`)。必须走 `.env` 或先补 secret 机制。 **AI 成熟度判定**:`M0 验证完成` → `M1 实现完成但作废` → `M2 验证完成(M2-0),产品代码未开始`。**统一平台若排 AI 生图,必须按「从零新建」估算,不能按「接入现有能力」估算。** ### 5.4 命名规范引擎(PRD P-04 的直接答案) - **变量体系**(`backend/src/types/naming.ts:19-40`):系统变量 11 个(用户昵称/客户名称/模板名称/横竖版/任务编号/批次号/序号/素材序号/日期/时间/扩展名)+ 客户自定义字段(可映射 `productId/productName/brandName` 三个语义字段,其余落 `extra_fields` JSONB)。 - **默认模板**(`utils/namingConfig.ts:28-72`): - 商品图识别:`['交易-sp{商品ID}-{商品名称}', '交易-sp{商品ID}']` - 素材命名:`交易-sp{商品ID}-{横竖版}-{品牌名称}-{用户昵称}-{任务编号}-{序号}.{扩展名}` - ZIP:`{客户名称}图片素材-{任务编号}` - **三层兜底**:客户配置 → 客户配置不命中回落到旧 `FilenameParser` 宽松解析 → 仍失败用 `renderFallbacks`(商品ID/品牌默认 `unknown`)(`namingConfig.ts:352-403`)。 - **校验很实**:字段名不得与系统变量重名、别名不得跨字段重复、识别模板必须能编译且字段已定义、命名模板不得引用未知变量(`namingConfig.ts:240-328`)。前端 `NamingConfigEditor.tsx`(765 行)实时预览。 - **批次快照**(`types/naming.ts:104-119`):建单时固化 `materialTemplate/zipTemplate/renderFallbacks`,保证「客户事后改规则不影响在途/历史批次,重试产出同名文件」。 - **文件名安全**:非法字符清洗 `sanitizeFilename` + UTF-8 字节截断 `truncateFilename`(`namingTemplate.ts:177`、`:218`、`:277`)。**注意:main 分支没有「提交前重名门禁」**——若命名模板不含商品维度,同批次会产出同名文件;ZIP 下载路径里有注释承认「会互相覆盖」(`productionBatchController.ts:2096`),而「撞名返回 400」的重名门禁只存在于 AI 分支(`32f1a09`)。整合时需补这条校验。 ### 5.5 客户级配置与数据范围 - **客户级配置范式**:`clients.naming_config` JSONB,`null` = 用系统默认(`models/Client.ts:61-65`)。AI 侧方案明确要求「新增客户级配置照这个模式,别发明新机制」(`docs/AI素材生成-与现有系统整合方案.md:19`)。 - **数据范围**:`users.client_scope_mode` + `user_clients`。语义是 **fail-closed**:`restricted + 无绑定 = 什么都看不到`,绝不放大成「全部客户」(`utils/clientScopeCore.ts:12-22`)。 - **落地很扎实**:涉及客户数据的 10 个 Router 都挂了 `attachClientScope`;模板清单、建批次、上传商品图、命名预览、批次模板都做 `checkClientReadable/Writable`;甚至专门修过「生产入口未消费客户范围 → 可用别家模板出图并套出命名规则」的安全 bug(commit `b7e2c68`、`100544e`)。 ### 5.6 通知、站内信与 WebSocket - **通知模板可配**:5 类模板(批次全部成功 / 部分失败 / 全部失败 / ZIP 就绪(自动清理)/ ZIP 就绪(保留)),支持变量占位(批次号、客户名称、数量、失败原因、过期日期),长度上限标题 60/内容 200,`uiControl: 'notification-templates'` 挂专用编辑器(`utils/notificationTemplates.ts:130-180`、`:411`)。 - **触发点**:`workers.ts` 批次完成与失败、ZIP 打包完成;开关可配(`notification.on_batch_completed` 等)。 - **前端形态**:顶栏铃铛 **hover 下拉**(最近 10 条 + 未读数 + 标记/删除/清空),**没有独立通知中心页**(`AdminLayout.tsx:126-133`、`:400-420`)。 - **WS**:`/ws?token=`(浏览器 WS 不能带 header),推整行快照,300ms 按批合并,30s 心跳,鉴权失败 `4401` 让前端停止重连;前端 `useRealtimeBatches` 在无连接时降级轮询(`utils/realtime.ts:20-70`)。 ### 5.7 系统配置框架(统一平台应整套继承) - 40 个配置项 / 10 模块:basic(2)、production(9)、upload(3)、naming(1)、queue(8)、notification(4)、monitor(3)、security(5)、log(1)、storage(4)(`utils/systemConfigRegistry.ts:192-760`)。 - 每个配置项声明:`module/label/type/default/level/description/min/max/options/nullable/isPublic/warning/uiControl/uiControlMeta/validate`。 - **默认值只能定义在注册表**,DB 只存改过的值;`default` 支持函数形式从 env 派生(升级不重置现网调优)(`systemConfigRegistry.ts:33-100`)。 - **明确禁止**把进程级基础设施(JWT_SECRET/DB_*/REDIS_*/PORT)放进注册表——改完不生效只会制造错觉(`systemConfigRegistry.ts:1-20`)。 - 审计:`system_config_audits` 存 old/new,**不脱敏**(因此不适合放凭据)。 - 这套机制直接对应统一平台所需的「平台级参数、AI 模型/并发/上限、上传限制、队列并发、通知开关、安全策略」等集中治理。 --- ## 6. 代码规模与质量 ### 6.1 LOC / 文件数(口径命令) ```bash cd 参考项目/sucaigongfang # 后端源代码(ts/js,不含 node_modules/dist/uploads) find backend/src -type f \( -name '*.ts' -o -name '*.js' \) -print0 | xargs -0 wc -l | tail -1 # → 30835 # 前端源代码(ts/tsx/css) find frontend/src -type f \( -name '*.ts' -o -name '*.tsx' -o -name '*.css' \) -print0 | xargs -0 wc -l | tail -1 # → 32797 # 后端测试 find backend/tests -type f -name '*.ts' -print0 | xargs -0 wc -l | tail -1 # → 2078 # 文件数 find backend/src -type f -name '*.ts' | wc -l # 137 find frontend/src -type f \( -name '*.ts' -o -name '*.tsx' \) | wc -l # 74 ``` | 模块 | 文件数 | LOC | 占比 | 说明 | | --- | --- | --- | --- | --- | | backend/utils | 55 | 14,169 | 46.0% | 渲染/队列/命名/配置/清理/日志,绝对核心 | | backend/controllers | 17 | 9,746 | 31.6% | `productionBatchController` 2,404 行、`templateController` 1,500+ 行 | | backend/models | 20 | 2,721 | 8.8% | 19 表 + associations | | backend/scripts | 8 | 1,348 | 4.4% | 运维脚本 | | backend/migrations | 11 | 778 | 2.5% | umzug | | backend/middlewares | 6 | 766 | 2.5% | auth/clientScope/upload/rateLimit | | backend/routes | 17 | 657 | 2.1% | 薄层 | | backend/app.ts + types | 3 | 650 | 2.1% | 入口 + 类型 | | **后端合计** | **137** | **30,835** | 100% | | | frontend/pages | 16 | 16,705 | 50.9% | `Production` 2,625、`SystemConfig` 1,860、`TemplateList` 1,735、`Tasks` 1,610 | | frontend/components | 24 | 8,303 | 25.3% | `BatchDetailDrawer` 1,125、`AdminLayout` 874、`TaskLogBoard` 779 | | frontend/utils | 20 | 2,528 | 7.7% | 纯函数多,易复用 | | frontend/types | 1 | 1,648 | 5.0% | 单文件类型字典 | | frontend/styles | 7 | 1,561 | 4.8% | token/theme/arco-overrides | | frontend/services | 1 | 1,223 | 3.7% | 113 个 API 方法 | | frontend/hooks + stores + 入口 | 11 | 829 | 2.5% | 4 hooks + 2 store + App/main/index.css/App.css/arcoStyles.ts | | **前端合计** | **74(+css)** | **32,797** | 100% | | | **总计** | **211+** | **63,632** | — | 约 6.4 万行(与任务书「约 62k 行 TS」量级一致) | 补充:AI 验证工作台 `tools/vlm-lab` 10,469 行、`tools/ai-image-validation` 2,122 行(**非产品代码**);分支 `backup/ai-asset-m1` 整体约 10 万行。 ### 6.2 TypeScript 严格度 - 后端 `tsconfig.json`:`strict: true`、`target ES6`、`skipLibCheck: true`(**无 `noUnusedLocals`/`noUncheckedIndexedAccess`**)。 - 前端 `tsconfig.app.json`:`strict` + `noUnusedLocals` + `noUnusedParameters` + `verbatimModuleSyntax` + `noFallthroughCasesInSwitch` + `erasableSyntaxOnly`(比后端更严)。 - 全量 `tsc --noEmit` 纳入 CI(前后端 + 测试独立 tsconfig),且后端生产镜像是 `tsc` 全量编译,类型错误会直接卡住镜像构建(`ci-local.sh:28-32`)。 ### 6.3 测试 - `node --test`(Node 内置 test runner),**175 个用例 / 14 个文件 / 2,078 行**。 - 覆盖的都是「口径类纯函数」:通知模板(41)、统计趋势(26)、客户范围(23+4+2)、口令有效性与策略(14+11)、规格名称派生(13)、存储清理计划(10)、缩略图路径(10)、批次状态(8)、失败目录(7)、事件目录(3)、命名变量别名(3)。 - **缺口**:无 DB 集成测试、无 API 契约测试、无渲染快照测试、无 E2E(Playwright 只在另一项目里)。PSD 渲染这种最容易静默出错的模块,**没有自动化回归**。 ### 6.4 Lint / CI - 后端 `.eslintrc.js`(@typescript-eslint 8)+ prettier 3;前端 `.eslintrc.cjs` + react-hooks/react-refresh。 - `deploy/ci-local.sh` 六步:格式 → lint → 单测 → **前端设计 token 静态校验**(`scripts/verify-design-tokens.mjs`,穷举扫描 Arco CSS 变量三元组/完整色值混用)→ 失效筛选判定(`verify-invalid-filters.mjs`)→ tsc → 构建。 - 设计 token 校验被硬卡的原因写得很实在:Arco 变量格式用错**不报错、只静默失效**,2026-09 因此连续出过两次线上视觉事故(`ci-local.sh:18-24`)。 - 版本管理:commitizen(`git cz`),语义化提交(feat/fix/docs/refactor/style/chore/test/ci)。 ### 6.5 技术债与已知问题(须带入整合评估) | # | 问题 | 影响 | 证据 | | --- | --- | --- | --- | | 1 | **`docs/` 整体 gitignore,110 个文件 0 入库** | AI 验证结论、命名方案、部署手册都只存在本机;换人/换机即丢失上下文 | `.gitignore:122`、`tools/ai-image-validation/README.md:5-8` | | 2 | API 与 Worker 同进程 | 无法独立扩缩容;Worker OOM/崩溃会连带 API;`uncaughtException` 直接退出 | `app.ts:64-78`、`app.ts:402` | | 3 | 文件存储纯本地磁盘 | 多副本/多机部署不可行;无 OSS/S3 适配层 | `utils/uploadPath.ts:20`、`docs/AI多行业素材生成方案.md:87` | | 4 | 登录限流用进程内存 | 水平扩容后限流失效(代码自述) | `middlewares/rateLimit.ts:11-18` | | 5 | 无 `secret` 配置类型、审计不脱敏 | API Key 等凭据不能进系统配置 | `docs/AI素材生成-与现有系统整合方案.md:122-155` | | 6 | 渲染引擎只支持单张商品图 | 无法做 Logo/促销/AI 模特多资产叠加 | `materialComposer.ts:222-236` | | 7 | 大量 `console.log` 混在渲染器里 | 生产日志噪音大、性能损耗 | `materialComposer.ts:288-357` | | 8 | 文档漂移:`database/README.md` 写「15 张表」实际 19;`deploy.sh:16-20` 仍写「启动 DBSync alter」实际已是 umzug | 运维按文档操作会误判 | `database/README.md:11`、`deploy/deploy.sh:16-20`、`utils/dbSync.ts:9-40` | | 9 | `estimatedDuration` 后端从未写入,前端曾据此展示「预估剩余时间」 | 已修(前端改为不显示),但字段仍是死字段 | `pages/Home.tsx:306-310` | | 10 | 标签 logo 上传后端有、前端未接线 | 资产能力半成品 | `tagController.ts:21-37`、`frontend/src` 无 `logoPath` 消费 | | 11 | `history` 路由被注释 | 素材历史页缺失 | `App.tsx:118-120` | | 12 | sharp 锁 `0.32.6` | 升级需回归 PNG 量化行为 | `backend/package.json:45`、`materialComposer.ts:317-329` | | 13 | Windows 下抠图不可用(动态 require 降级) | 开发环境与生产行为不一致 | `utils/backgroundRemover.ts:5-30` | | 14 | 前端菜单/路由/权限映射三处硬编码 | 加一个模块要改 4 处,易漏 | `AdminLayout.tsx:85-98`、`:763-841`、`App.tsx:100-120` | | 15 | **权限模型只有「页面级」,没有「动作级」**;任务日志因此刻意不提供暂停/取消/重试 | 统一平台的提审、投放配置、AI 产出复核都需要动作级/数据级权限,现模型不够用 | `routes/taskLogRoutes.ts:18-24`、`utils/permissions.ts:4-20` | | 16 | 素材历史(`materials`)与生产批次两套记录并存,前者无页面 | 后续若做「素材资产/提审」会有两套口径 | `routes/materialRoutes.ts:28-34`、`App.tsx:118-120` | --- ## 7. 逐模块复用评估(核心交付) ### 7.1 逐模块评估表 > 复用级别:**A=几乎直接复用**;**B=改造可控**;**C=需重写**;**D=废弃**。 > 人日为「改造成统一平台形态」的粗估,不含产品/测试/联调,需与研发负责人校准。 | 模块 | 现有实现 | 统一平台中的目标形态 | 复用级别 | 改造要点 | 粗估人日 | 风险 | | --- | --- | --- | --- | --- | --- | --- | | 工程地基(Express/Sequelize/PG/umzug/日志/健康检查) | 17 Router、19 模型、11 迁移、结构化错误兜底 | 统一后端基座 | **A** | 拆 `platform` 维度;健康检查增加 OSS/第三方 AI 探活 | 3–5 | 低 | | 认证 / 口令安全 / 限流 / 审计 | JWT+pwdv、强制改密、三态有效期、一次性口令、IP+用户名限流 | 同一套 | **A** | 限流迁 Redis(为多副本) | 2–3 | 低 | | RBAC + 客户数据范围 | 权限 key 目录 + `fail-closed` 范围 + 全链路校验 | 平台/客户双层范围 | **A** | 权限 key 按新模块扩充;客户范围升级为「平台×客户」 | 3–4 | 权限遗漏=静默越权,需补回归测试 | | 系统配置框架 | 40 项/10 模块/L1-L3/审计/专用控件 | 加 `platform`、`ai`、`selection`、`video` 模块 | **A** | 加 `secret` 类型 + 审计脱敏(AI Key 前置条件) | 3–5 | 低 | | 模板/分类/标签/规格管理 | 完整 CRUD + 批量 + 复制 + 自动匹配 | 平台级模板资产库 | **B** | 加 `platform_id`/`project`;标签 logo 接入渲染;规格扩三档 | 8–10 | 模板迁移与兼容 | | PSD 解析与渲染引擎 | 1773 行合成器 + 字体兜底 + 质量压缩 | 多资产合成(模板/商品/AI模特/信息/Logo) | **B** | **核心改造:按图层注入不同资产**;Logo 组合与拼接字符;促销图层;三尺寸派生 | 12–18 | 改造面大、无自动化回归、字体版权 | | 抠图 | imgly + ONNX medium + 白底判定 + 降级告警 | 同能力 | **A** | 抽成独立推理服务(可选);非白底/透明底策略 | 2–3 | 单机 ONNX 内存峰值 | | 批量任务/队列/进度/收尾/重试 | BullMQ 2 队列 + DB 计数 + WS + 监控 + 失败口径 | 多产线统一任务中心 | **A** | 队列按产线拆分;Worker 独立进程化(可选) | 6–8 | 同进程 OOM | | 命名规则引擎 | 变量模板 + 客户级 + 快照 + 校验 + 商品图识别 | 提审命名规范引擎 | **A** | 变量域扩 `SPUID/平台/活动/日期` 等;与提审状态联动 | 3–4 | 字段域扩展后校验规则要同步 | | 导出 / ZIP / 下载 | CSV + 选集 ZIP + 整批 ZIP 异步 | 按路径导出/打包 | **A** | 导出字段调整;路径规则(平台/项目专用路径) | 3–4 | 低 | | 通知 / 站内信 / WS | 可配模板 + 3 类触发 + 铃铛下拉 + WS | 通知中心(含提审/投放事件) | **B** | 补独立通知中心页;补事件类型;WS 跨进程改 Redis pub/sub | 3–4 | 低 | | 任务事件 / 失败目录 / 时间线 / 任务日志 | 事件目录 + 失败目录 + 90 天保留 + 管理端看板 | 统一可观测中心 | **A** | 新增事件类型;权限粒度可能需细分 | 3–4 | 低 | | 文件存储 / 缩略图 / 清理 | 本地磁盘 + 按需 WebP + 先预览后清理 | 对象存储 + CDN | **B** | 抽 `StorageAdapter`(local/OSS),URL 规则改造,清理策略按存储对象 | 4–6 | **迁移期双写/回填** | | 前端设计体系/布局/守卫/共享组件 | token 单点 + 自动校验 + AssetThumb/StatCard/SectionCard 等 | 统一设计系统 | **B** | 菜单/路由/权限映射改为配置驱动;主题换肤 | 5–7 | 菜单硬编码三处耦合 | | 素材制作页(2625 行) | 选模板/传图/识别/填字段/参数/二次确认 | 「平台 → 选品 → 品类 → 尺寸 → 模板 → 展示方式 → Logo/促销 → 预览 → 导出」 | **C** | 按新信息架构重写,复用其上传识别与字段表格逻辑 | 10–15 | 新交互未定稿即返工 | | 首页 Dashboard | 5 卡环比 + 今日任务 + 待我处理 | 新 KPI(单套成本/模板占比/提审通过率) | **C** | 指标口径重定义 + 后端聚合 | 3–5 | 指标口径待业务确认 | | AI 生图 | **无可运行产品代码**(仅 M2-0 验证) | 9 要素表单→提示词→生成→校验→挑图→入生产 | **C** | 从零:模型网关、配额排队、成本预估、文字校验闸门、素材落库、UI | 20–30 | 成本/限流/合规/一致性 | | AI 素材生成(M1 分支) | 53 commits / ~3.1 万行新增 | — | **D** | 官方已声明作废;仅可借鉴擦除遮罩/文字校验/合规词库经验 | 0(不计) | 误用会引入大量死代码 | | 数据字典 / 枚举 | 仅文档 + 未合并分支 | 字典类型→字典项(code/label/description) | **C** | 重写(表+API+页面+权限),供 AI 提示词与筛选消费 | 3–4 | 低 | | 多平台 / 平台切换 | 无 | 得物/唯品会切换,资源路径按平台隔离 | **C/新建** | 平台表、平台上下文、菜单/模板/命名/ZIP 全链路带平台 | 5–8 | 与客户维度正交,需先定数据模型 | | 选品 / 原始物料获取 | 无(在 Electron 桌面端) | 商品检索(SPUID/粗词)→ 圈选 → 详情页图片抓取归档 | **新建** | 迁移 `dewu-material-Downloader` 的抓取能力到服务端 + 物料库 | 15–25 | 平台反爬/登录态/合规 | | 视频素材 AI 生产 | 无 | 脚本(AI/人工)→ 画面 → 组合,16:9 / 9:16 | **新建** | 完全新产线(模型选型、异步长任务、素材管理) | 40+ | 成本、时长、质量不可控 | | 提审 | 无 | 待审清单 + 状态跟踪 + 与素材/商品关联 | **新建** | 新表+状态机+清单页+通知事件 | 8–12 | 平台提审口径未定义 | | 投放配置(DP/承接页) | 无 | 输入点(SPUID/官网链接)→ 自动生成链接 → 一键复制/批量替换 | **新建** | 新表+生成规则+批量操作+校验 | 8–12 | 规则来源与合规未确认 | ### 7.2 能否直接作为统一 WEB 平台的技术底座? **结论:能作为「图片素材产线 + 平台工程地基」的底座,但不能直接当作整个统一平台的底座。** **可以成立的四个理由** 1. **工程底座是成熟的**:认证/权限/客户范围/系统配置/迁移/队列/事件/失败目录/通知/打包/部署/CI,几乎覆盖统一平台 70% 的「非业务」工作量,且质量高于平均水平(大量注释是「为什么这么写 + 踩过什么坑」,对后来者价值极高)。 2. **图片产线的主干已经跑通**:PSD 模板 → 图层契约 → 批量交叉生产 → 命名 → 打包 → 交付,正是 PRD P-02 的骨架。 3. **命名引擎就是 P-04 的一半**,且实现质量明显高于「按规范重写」的预期(客户级可配 + 快照 + 兜底 + 校验)。 4. **可观测性天然具备**:任务事件时间线 + 失败分类目录 + 管理端任务日志,统一平台最容易忽略、又最难后补的东西这里已经有了。 **必须补齐的 8 块(按优先级)** | # | 缺口 | 现状 | 建议 | | --- | --- | --- | --- | | 1 | **多平台/项目抽象** | 完全无「平台」概念 | 先定数据模型:`platform`(得物/唯品会)+ 资源路径规则 + 平台上下文贯穿模板/命名/ZIP/导出 | | 2 | **选品 / 原始物料获取** | 在另一 Electron 项目 | 服务端化抓取(登录态/反爬/限速),建物料库与 SPU 关联 | | 3 | **四层信息中的三块**(展示方式/Logo/促销) | 只有单品图 + 文本 | 改造渲染引擎支持「按图层注入不同资产」;Logo 组合与拼接字符;促销模板叠加 | | 4 | **视频素材产线** | 空白 | 独立产线,勿与图片产线耦合;先做脚本/画面/组合三段 MVP | | 5 | **提审** | 空白 | 新状态机 + 清单 + 通知 + 与素材/商品稳定关联 | | 6 | **投放配置** | 空白 | DP/承接页生成器 + 批量替换 + 一键复制 | | 7 | **对象存储 / 队列水平扩展** | 本地磁盘 + 同进程 Worker + 内存限流 | 抽存储适配层;Worker 独立进程 + Redis 限流;否则多副本不可行 | | 8 | **权限体系细化** | 菜单级 key + 客户范围 | 提审/投放/AI 需要更细的能力点 + 数据范围(平台×客户) | **一个必须提前说明的取舍**:若把「AI 生图」和「视频」放进同一期,本系统的「单进程 + 本地磁盘 + 同步重 CPU 渲染」架构会先撞墙——**图片渲染(CPU/内存密集)与 AI 调用(长耗时、外部限流)应该分属不同队列与不同 Worker 进程**,这在原系统里是有意合并的(`app.ts:65-66`),整合时必须拆。 --- ## 8. 可复用资产清单 ### 8.1 后端(可直接搬运/改造) | 资产 | 路径 | 价值 | | --- | --- | --- | | PSD 渲染合成引擎 | `backend/src/utils/materialComposer.ts`(1773 行) | 剪贴蒙版/混合模式/缩放模式/字体兜底/质量压缩,全项目最贵资产 | | 字体管理 | `backend/src/utils/fontManager.ts` + `backend/fonts/方正兰亭中黑简体.TTF` | 字体扫描双目录 + 别名兜底(防豆腐块事故);**字体商用授权待确认** | | PSD 解析 | `backend/src/utils/psdParser.ts` | 图层前缀识别 + 预览图生成 | | 图层字段匹配 | `backend/src/utils/layerFieldMatcher.ts` | placeholder/key/alias/fuzzy 四级匹配,纯函数可单测 | | 抠图 | `backend/src/utils/backgroundRemover.ts` + `imageTypeDetector.ts` | 白底判定 + ONNX 抠图 + 降级告警 | | 命名引擎 | `backend/src/utils/namingTemplate.ts`、`namingConfig.ts`、`namingRuleResolver.ts`、`filenameParser.ts`、`types/naming.ts` | 变量模板/识别/校验/快照,纯函数多 | | 队列与 Worker | `backend/src/utils/queue.ts`、`workers.ts`(1074 行)、`batchTasks.ts`、`batchStatus.ts`、`taskMonitor.ts` | 入队/重试/收尾/超时/卡死恢复 | | 任务事件体系 | `taskEvents.ts`、`taskEventCatalog.ts`、`failureCatalog.ts`、`taskEventQuery.ts` | 事件目录 + 失败目录 + 保留期,可观测底座 | | 系统配置框架 | `systemConfigRegistry.ts`(40 项)、`systemConfigService.ts`、`models/SystemConfig(Audit).ts` | 声明式配置 + L1/L2/L3 + 审计 | | 权限与范围 | `utils/permissions.ts`、`clientScope.ts`、`clientScopeCore.ts`、`middlewares/auth.ts`、`middlewares/clientScope.ts` | RBAC + fail-closed 数据范围 | | 账号安全 | `utils/passwordPolicy.ts`、`passwordExpiry*.ts`、`middlewares/rateLimit.ts`、`utils/jwt.ts` | 口令策略/有效期/令牌失效/限流 | | 文件服务 | `utils/uploadPath.ts`、`imageThumbnail.ts`、`thumbnailPath.ts`、`storageCleanup.ts`、`storageCleanupScheduler.ts`、`middlewares/upload(Thumbnail|Auth).ts` | 路径安全 + 按需缩略图 + 预览式清理 | | 通知模板 | `utils/notificationTemplates.ts` | 变量化模板 + 校验 + 渲染 | | 实时推送 | `utils/realtime.ts` | 整行快照 + 节流 + 心跳 + 401 停止重连 | | 迁移机制 | `utils/migrator.ts`、`dbSync.ts`、`schemaFromModels.ts`、`backend/src/migrations/*`(11 个) | 模型即 DDL 来源 + 幂等迁移 | | 运维脚本 | `backend/src/scripts/*`(init-admin/migrate/backfill/clean/clear-queue) | 部署与数据治理 | | 数据模型 | `backend/src/models/*`(19 表)+ `associations.ts` | 用户/客户/模板/批次/任务/事件/配置全量 | | API 契约 | `backend/src/routes/*`(113 接口)+ `frontend/src/services/api.ts`(113 方法) | 前后端一一对应的契约样板 | | 测试 | `backend/tests/*`(175 用例) | 纯函数测试范式,可直接扩展 | ### 8.2 前端(可直接复用) | 资产 | 路径 | 价值 | | --- | --- | --- | | 设计 token / 主题 | `frontend/src/styles/tokens.ts`、`theme.ts`、`arco-overrides.css` | 字号 7 档/颜色/圆角/控件描边,全站唯一来源 | | 设计规范 + 自动校验 | `frontend/前端设计规范.md`、`frontend/scripts/verify-design-tokens.mjs` | 规范落到可执行脚本(CI 硬卡) | | 布局与守卫 | `components/AdminLayout.tsx`、`AuthGuard.tsx`、`PermissionGuard.tsx` | 侧栏/面包屑/通知/用户菜单/双守卫 | | 复用组件 | `AssetThumb`(统一缩略图)、`StatCard`、`SectionCard`、`HelpTip`、`BatchEventTimeline`、`TaskLogTimeline`、`TemplateHoverPreview`、`useHoverPreview` | 直接可用 | | 业务重组件 | `BatchDetailDrawer`(1125 行)、`TaskLogBoard`、`LayerBindingEditor`、`NamingConfigEditor`、`NotificationTemplatesEditor`、`ImageLayerTable`、`TemplateChecklist` | 改造后价值高 | | 工具函数 | `utils/naming.ts`、`assetUrl.ts`、`download.ts`、`clipboard.ts`、`batchDisplay.ts`、`taskEvents.ts`、`specification.ts`、`realtime.ts` | 纯函数,易搬 | | 页面参考 | `pages/Production.tsx`、`Tasks.tsx`、`TemplateList.tsx`、`SystemConfig.tsx`、`UploadTemplate.tsx`/`EditTemplate.tsx` | 作为重构蓝本而非直接复制 | | 发布说明(12 篇) | `frontend/发布说明-*.md` | 记录每次改动与踩坑,交接价值高 | ### 8.3 文档(**注意:全部未入库**) | 文档 | 价值 | | --- | --- | | `docs/AI素材生成-M0验证记录.md`(924 行) | VLM 反推/图层分离的实测数据与 Go/No-Go 判定 | | `docs/AI生图-M2-0验证记录.md`(335 行) | Seedream 五项门槛实测、flash/pro 对比、限流与成本注意点 | | `docs/AI生图相关功能-实现方案.md`(757 行) | 最新 AI 口径(表单化/不套模板)+ 9 要素表单 + 官方参数硬边界 | | `docs/AI素材生成-与现有系统整合方案.md`(459 行) | 代码勘察结论、API Key 不能进配置、整合顺序 | | `docs/AI多行业素材生成方案.md`(959 行) | 契约化方案(已部分作废,但行业分析/现有能力边界表仍有效) | | `docs/命名规范梳理与客户级配置方案.md`(874 行) | 命名现状/硬编码落点/改造影响面 | | `docs/数据字典与枚举管理评估.md`(478 行) | 字典建模结论(code/label 分离、单值/多值取舍) | | `docs/系统配置页面规划文档-v2.md`(799 行) | L1/L2/L3 分级的来源与「不要放进页面」清单 | | `docs/通知中心页面需求.md`(92 行) | 通知中心独立页的交互稿(未实现) | | `docs/用户使用手册.md`(543 行) | 三个角色的真实操作路径 | | `deploy/部署上线手册.md` | 容器化部署/迁移/回滚的标准流程 | | `tools/vlm-lab/`、`tools/ai-image-validation/` | 可复跑的 AI 验证工作台(**注意 config.json 含真实 Key,永不入库**) | --- ## 9. 风险与待确认问题 ### 9.1 六个最大风险 | # | 风险 | 说明 | 影响 | 缓解建议 | | --- | --- | --- | --- | --- | | R1 | **单机架构撞上「多产线 + AI」** | API 与 Worker 同进程、抠图 ONNX 单机内存峰值、本地磁盘存储、内存限流(`app.ts:65-66`、`rateLimit.ts:11-18`) | 无法水平扩容;AI 长任务会拖垮图片产线的 API 响应;OOM 导致整站不可用 | 整合前先拆三层:API / 图片渲染 Worker / AI 任务 Worker;存储抽象层 + 对象存储;限流迁 Redis | | R2 | **AI 生图从零开始,成本与合规双未知** | 无产品代码;flash 单张 19–25s、16,428 token;文字校验接近翻倍;账号级 IPM 限流真实存在;单张单价未获取(`docs/AI生图-M2-0验证记录.md:62-65`、`:134-135`) | 排期与成本预估都会失真;上线后易被限流/账单打爆 | 先做 1 人日连通性与配额压测;把单价与日调用上限做成配置;默认 flash;强制人工复核闸门 | | R3 | **PSD 渲染引擎改造的正确性风险** | 1773 行、无自动化回归、只支持单商品图;字体/混合模式/剪贴蒙版细节多(`materialComposer.ts`) | 一次改造就可能让存量模板产出错图,且**错误是静默的**(图能出、内容错) | 建立「存量模板黄金样本 + 像素级回归」再动引擎;灰度客户;保留旧渲染器开关 | | R4 | **PRD 的四层信息与现引擎不匹配** | Logo 组合/拼接字符、促销模板、AI 模特/脚模都要「按图层注入不同资产」,现引擎共用一个 `productImageBuffer`(`materialComposer.ts:222-236`) | P-02 的验收标准(四层信息)达不到 | 把「多资产图层」列为引擎改造第一优先级;先与产品确认四层的技术边界 | | R5 | **文档与知识全部未入库** | `docs/` 110 文件 0 入库;AI 验证结论只在本机(`.gitignore:122`) | 人员变动/机器损坏即丢失决策上下文;「为什么这么设计」无法追溯 | 整合启动第一件事:文档入库(或迁到统一知识库),先抢救 AI 验证结论与配置分级结论 | | R6 | **平台/客户维度正交 + 存量数据迁移** | 现在只有「客户」,PRD 要「平台(得物/唯品会)」;模板/命名/ZIP/导出都要带平台;现有 854MB 文件与 19 表数据要迁 | 模型改错代价最大(迁移难回滚);历史产出可能无法归属到平台 | 先出「平台 × 客户 × 项目」数据模型评审;迁移脚本先 dry-run + 备份;不动 19 表而是加列/加表 | **其余风险(次级)**:中文/字体版权(`方正兰亭中黑简体.TTF` 商用授权待确认);PSD 兼容性(`ag-psd` 对智能对象/文字特效/图层样式的支持边界未系统测试);许可证(依赖多为 MIT/Apache,但 `sharp 0.32.6`/`onnxruntime` 需过安全扫描);AI 输出内容合规(券商场景已证明需要人工复核,电商场景的商标/肖像/功效宣称同样有风险)。 ### 9.2 待确认清单 | 编号 | 问题 | 为什么影响整合 | 建议向谁确认 | | --- | --- | --- | --- | | Q-01 | 统一平台的「平台」是数据维度还是独立部署?(得物/唯品会是否共享同一套模板与命名规则) | 决定是否需要给 19 张表加 `platform_id`,以及资源路径规则 | 产品负责人 + 运营 | | Q-02 | 图片产线是否必须保留「PSD 模板」为唯一模板载体?是否允许图片模板(非 PSD)并存? | 决定是否复活 M1 分支的 SchemaRenderer,还是只改造 MaterialComposer | 产品 + 设计负责人 | | Q-03 | AI 生图的定位:替代 PSD 产线,还是「无模板场景」的补充产线? | 决定 AI 是 P0 还是 P2,以及是否与模板体系解耦 | 产品负责人 | | Q-04 | 三规格(800×800 / 1280×720 / 1080×1920)是固定枚举还是可扩展规格? | 现有规格是通用 `(w,h)` 实体;固定则逻辑更简单 | 产品 + 设计 | | Q-05 | Logo 的「拼接方式与拼接字符」具体是什么交互?字符是否需要上传字体/图形? | 决定渲染引擎是否需要「图形组合器」,工作量差异大 | 设计负责人 + 唯品会对接人 | | Q-06 | 促销信息(折扣/活动标识)是模板叠加还是独立图层组? | 决定促销图层的模型与编辑器 | 设计 + 运营 | | Q-07 | 选品主键:得物用 SPUID、唯品会用什么?是否统一? | 决定物料库与模板变量域 | 运营 + 平台对接人 | | Q-08 | 详情页图片抓取是否有合规/授权依据?是否需要登录态? | 决定选品模块可行性与架构(是否要浏览器自动化) | 法务 + 运营 | | Q-09 | 提审是「平台后台人工提审」还是「系统内审批流」?需要跟踪到哪一级状态? | 决定提审模块是轻量清单还是完整工作流 | 运营 + 产品 | | Q-10 | DP / 承接页的生成规则由谁提供?是否有稳定模板与校验规则? | 决定投放配置模块是「规则引擎」还是「固定拼接」 | 运营 + 平台对接人 | | Q-11 | AI 生图的单张成本上限与日预算?可接受的单张耗时? | 决定模型档位(flash/pro)、是否做文字校验、是否批量 | 财务 + 产品 | | Q-12 | AI 生图是否需要「文字逐字校验 + 人工复核」双闸门? | 决定额外 VLM 调用成本(接近翻倍)与流程设计 | 合规 + 产品 | | Q-13 | 视频素材的模型选型、时长上限、单条预算? | 视频是最大不确定性,需前置验证(M0 式) | 产品 + 财务 | | Q-14 | 视频/图片产出是否需要对象存储与 CDN?部署形态是单机还是多副本? | 决定是否必须先做存储适配层与 Worker 拆分 | 运维 + 技术负责人 | | Q-15 | 字体商用授权:`方正兰亭中黑简体` 是否已购买商用授权?新增字体如何管理? | 字体是合规红线,且换字体会改变渲染结果 | 法务 + 设计 | | Q-16 | 存量数据(19 表 + 854MB 文件)是否需要迁移?迁移到哪个平台维度? | 决定迁移工作量与回滚方案 | 产品 + 运维 | | Q-17 | 「历史选品」「历史脚本」「模板复用次数」等 P1 指标是否需要埋点体系? | 现有统计只有批次/素材维度,无选品/脚本 | 产品 + 数据 | | Q-18 | 统一平台是否需要多租户(不同客户数据硬隔离)?还是继续「单部署 + 客户范围」? | 决定权限与存储架构(这是最大的架构分叉) | 技术负责人 + 业务负责人 | --- ## 10. 证据索引 > 格式:结论 → `文件:行`。均为本次只读勘察直接可见。 | # | 结论 | 证据 | | --- | --- | --- | | 1 | 17 个业务 Router 挂载 + 静态文件 + 健康检查 | `backend/src/app.ts:147-164`、`:139-144`、`:175-201` | | 2 | API 与 BullMQ Worker 同进程 | `backend/src/app.ts:65-66`、`:402` | | 3 | WebSocket 复用同一 http.Server | `backend/src/app.ts:429-432` | | 4 | 17 个控制器 / 113 个接口 | `backend/src/routes/*.ts`(逐文件 `router.` 计数) | | 5 | 19 张表与建表顺序 | `backend/src/utils/schemaFromModels.ts:30-56`、`models/*.ts` 的 `tableName` | | 6 | umzug 迁移取代 `alter:true` 及原因 | `backend/src/utils/migrator.ts:8-30`、`utils/dbSync.ts:9-40` | | 7 | 批次/子任务/结束态状态机与失败口径 | `models/ProductionBatch.ts:12-51`、`models/BatchTask.ts:19-34`、`utils/batchStatus.ts:12-80` | | 8 | 生产参数与命名规则在建批次时快照(L2) | `models/ProductionBatch.ts:37-48`、`productionBatchController.ts:680-712`、`:797-806`、`:873-877` | | 9 | 交叉批量 N×M 与命名预渲染 | `productionBatchController.ts:839-840`、`:888-908`、`:948-950` | | 10 | 素材 Worker 主流程(抠图→合成→落库→计数→WS) | `utils/workers.ts:70-412`、`:193-230`、`:271-312` | | 11 | 自动重试与「最终失败才计数」的语义 | `utils/workers.ts:324-401` | | 12 | ZIP 打包 Worker | `utils/workers.ts:764-860` | | 13 | PSD 只识别 3 类图层前缀 | `utils/psdParser.ts:123-147` | | 14 | 图层契约结构(图片/文本/剪贴蒙版/缩放模式) | `models/Template.ts:10-34` | | 15 | 文本图层↔字段四级匹配(placeholder/key/alias/fuzzy) | `utils/layerFieldMatcher.ts:1-20` | | 16 | 合成器入口与签名 | `utils/materialComposer.ts:143-154` | | 17 | 以 PSD 合成画布为底 + 替换图层四分支 | `utils/materialComposer.ts:212-236`、`:516-620` | | 18 | 15 种混合模式支持 | `utils/materialComposer.ts:427-477` | | 19 | 字体简写引号不成对导致 SIGABRT 的坑 | `utils/materialComposer.ts:100-125` | | 20 | 中文字体兜底栈(防豆腐块静默交付) | `utils/materialComposer.ts:27-38`、`utils/fontManager.ts:1-40` | | 21 | JPG 二分质量 / PNG 调色板减半压缩 | `utils/materialComposer.ts:258-357` | | 22 | **所有图片替换图层共用同一张商品图** | `utils/materialComposer.ts:222-236`、`:585-620`、`:750-752` | | 23 | 默认命名模板与字段定义 | `utils/namingConfig.ts:28-72` | | 24 | 系统变量清单(11 个) | `backend/src/types/naming.ts:19-40` | | 25 | 命名配置校验(重名/别名/未知变量) | `utils/namingConfig.ts:240-328` | | 26 | 批次级命名快照结构 | `backend/src/types/naming.ts:104-119` | | 27 | 商品图识别三层兜底(客户→legacy→fallback) | `utils/namingConfig.ts:352-403` | | 28 | 系统配置注册表与 L1/L2/L3 | `utils/systemConfigRegistry.ts:1-100`、`:192-760`(40 个 key) | | 29 | 权限 key 目录(11 个)与历史角色兜底 | `utils/permissions.ts:4-45` | | 30 | 客户范围 fail-closed 语义 | `utils/clientScopeCore.ts:12-22`、`:40-60` | | 31 | 登录限流为进程内存(扩容失效) | `middlewares/rateLimit.ts:11-30` | | 32 | 口令策略/强制改密/令牌版本 | `docs/PRD.md:46-56`、`middlewares/auth.ts:1-80`、`models/User.ts:1-60` | | 33 | 存储目录保护清单与清理原则 | `utils/storageCleanup.ts:16-65` | | 34 | 缩略图按需生成 + 失败回退原图 | `utils/imageThumbnail.ts:22-60`、`middlewares/uploadThumbnail.ts:9-30` | | 35 | 上传路径越界漏洞与修复 | `utils/uploadPath.ts:1-40` | | 36 | 通知模板可配 + 变量 | `utils/notificationTemplates.ts:38-180`、`:411-455` | | 37 | WS 整行快照 + 300ms 节流 + 心跳 | `utils/realtime.ts:20-70` | | 38 | 任务事件目录与 90 天保留 | `utils/taskEventCatalog.ts:1-40`、`utils/taskEvents.ts:15-45` | | 39 | 失败原因目录(code/audience/rawDetail) | `utils/failureCatalog.ts:1-50` | | 40 | 任务监控(超时/卡死/巡检可配) | `utils/taskMonitor.ts:9-40` | | 41 | 规格名称由尺寸派生、唯一性迁到尺寸 | `utils/specificationName.ts:46-57`、`migrations/20260924000001-specification-name-derivation.ts:1-40` | | 42 | CI 六步硬卡与设计 token 校验动机 | `deploy/ci-local.sh:24-45`、`frontend/前端设计规范.md:1-12` | | 43 | 容器化部署与网络模型 | `deploy/docker-compose.yml:1-40`、`frontend/Dockerfile:1-66`、`frontend/nginx.conf.template:20-80` | | 44 | 健康检查真实探活 DB/Redis | `backend/src/app.ts:175-201` | | 45 | `docs/` 全目录被 gitignore(110 文件 0 入库) | `.gitignore:122`、`git ls-files docs` = 0、`find docs -type f` = 110 | | 46 | AI 生图「main 无任何 AI 代码 + 从零新写」 | `docs/AI生图相关功能-实现方案.md:4-11`、`grep -rniE "seedream|volc|aigc" backend/src frontend/src` = 0 | | 47 | M1 分支被整体作废 | `docs/AI生图相关功能-实现方案.md:6`、`git log main..backup/ai-asset-m1`(53 commits) | | 48 | M2-0 五项验证结论(34 张全成功、flash 快 3 倍、透明底成立) | `docs/AI生图-M2-0验证记录.md:8-11`、`:46-66`、`:120-135` | | 49 | AI 账号级 IPM 限流真实存在 | `docs/AI生图-M2-0验证记录.md:62-65`、`tools/ai-image-validation/lib/config.mjs:59` | | 50 | AI API Key 不能进系统配置(无 secret 类型 + 审计不脱敏) | `docs/AI素材生成-与现有系统整合方案.md:122-155` | | 51 | AI 验证工作台不是产品代码 | `tools/ai-image-validation/README.md:3-8`、`tools/ai-image-validation/lib/config.mjs:1-12` | | 52 | M0 反推/图层分离结论与未达标项 | `docs/AI素材生成-M0验证记录.md:10-35` | | 53 | M1 验收定义与量化门槛 | `docs/AI素材生成-M1正式开发方案与计划.md:16-25`、`:1275-1298` | | 54 | 现系统无视频/提审/投放/平台/抓取/OSS | 以下 grep 均 **0 命中**:`grep -rniE "ffmpeg\|\bvideo\b" backend/src frontend/src`、`grep -rniE "\boss\b\|s3\|minio\|对象存储" backend/src`、`grep -rniE "crawl\|scrape\|puppeteer\|playwright" backend/src frontend/src`、`grep -rniE "提审\|审核" backend/src frontend/src`、`grep -rniE "平台切换" backend/src frontend/src`(「得物」仅在通知模板示例中) | | 55 | 文档漂移:database README 写 15 表、deploy 注释写 alter | `database/README.md:11`、`deploy/deploy.sh:16-20` | | 56 | `estimatedDuration` 从未写入 | `frontend/src/pages/Home.tsx:306-310` | | 57 | 素材历史页被注释、历史 API 仍在 | `frontend/src/App.tsx:118-120`、`backend/src/routes/materialRoutes.ts:28-34` | | 58 | 标签 logo 后端有、前端未消费 | `controllers/tagController.ts:21-37`、`grep -rn "logoPath" frontend/src` 仅 `types/index.ts` | | 59 | 端口/部署实测环境 | `deploy/docker-compose.yml:1-30`、`生产环境部署/PRODUCTION.md:1-8`(旧文档已自标失效) | | 60 | 提交活跃度与时间分布 | `git log --format='%ad' --date=format:'%Y-%m' \| sort \| uniq -c`(3 月 85 / 4 月 33 / 9 月 131) | --- ### 附:本报告未展开的部分 - `dewu-material-Downloader`(另一参考项目,Electron 桌面端)只做了边界确认:它承担「得物素材下载」能力,**未做深读**,选品/抓取的服务端化评估需以其专项报告为准。 - `docs/API文档/` 下 4 份火山方舟 PDF(约 4.9–7MB/份)为二进制,未解析;模型参数边界以 `docs/AI生图相关功能-实现方案.md:15-70` 的摘录为准。 - `frontend/发布说明-*.md`(12 篇)仅做存在性登记,未逐篇展开。 - 未运行任何测试/构建,所有质量结论均来自静态代码与脚本内容;「实际能否一次跑通」属**待确认**。