平台电商素材生产平台 · 技术栈与架构定稿
文档类型:技术栈与架构设计(定稿)
版本:V1.0 | 状态:已定稿(评审通过后进入 M0) | 日期:2026-10-30
适用范围:统一 WEB 平台(选品 → 原始物料 → 图片/视频生产 → 命名 → 提审 → 投放配置)
输入依据:docs/PRD/平台电商素材生产平台_PRD_V0.1.md、两份参考项目调研报告、docs/评估/平台电商素材生产平台_整合可行性评估_V0.1.md
环境基线(已在本机核实):macOS arm64、Node v24.15.0、pnpm 11.4.0、PostgreSQL 18.4(Docker,5432)、Redis 7.4.9(Docker,6379)
密级:内部;本文不含平台签名盐、账号、密钥等敏感值。
目录
- 定稿结论(一页纸)
- 决策依据与约束
- 技术栈总表(版本锁定)
- 架构总览
- Monorepo 结构与包职责
- 前端架构
- 后端架构
- 数据层(PostgreSQL 18 + Prisma 7)
- Redis、队列与实时
- 文件与对象存储
- 平台适配与 Playwright
- 图片产线
- AI 网关
- 认证、权限、凭据与安全
- 工程规范与质量门禁
- 开发环境与命令
- 构建与部署
- 版本治理
- 与评估文档的差异
- M0 必须验证的风险
- 明确不采用的技术与理由
- 附录 A:
.npmrc 与根配置
- 附录 B:依赖清单
- 附录 C:待确认项
0 定稿结论(一页纸)
| 决策 |
定稿 |
关键理由 |
| 运行时 |
Node.js 24.15.0(精确版本) |
与开发机一致;Prisma 7 引擎要求 ^24.11;Vite 8 要求 >=22.12 |
| 包管理 |
pnpm 11.4.0(唯一) |
与开发机一致;workspace 严格依赖;不使用 npm |
| 语言 |
TypeScript 5.9.3(strict) |
typescript-eslint 8.72 要求 <6.1,暂不上 TS 7 |
| 前端 |
React 19.3.1 + Semi Design 2.104.0 + Tailwind CSS 4.3.3 + Vite 8.3.5 |
Semi 支持 React 19(dewu 已验证);Tailwind 4 由 scgf 验证;Vite 8 满足 Tailwind/Semi 插件 |
| 后端 |
Express 5.3.0 + Prisma 7.10.1 + PostgreSQL 18 + Redis 7 + BullMQ 6.5.0 |
Express 5 可迁移 scgf 中间件;Prisma 7 是当前稳定线(8 仍为 RC);PG/Redis 已在本机运行 |
| 数据访问 |
Prisma 7.10.1 + @prisma/adapter-pg(ESM) |
类型安全、迁移一体化;19 表模型需重写(+8~12 人日,见 §18) |
| 队列/实时 |
BullMQ 5 类队列 + SSE(不用 WebSocket) |
任务进度是单向流;SSE 对 nginx/公网更友好 |
| 图像 |
sharp 0.32.6(锁定) + ag-psd 30.1.0 + @napi-rs/canvas 0.1.97 + fontkit 2.0.4 + ONNX 抠图 |
与 scgf 图像引擎行为一致,降低存量模板回归风险 |
| 平台自动化 |
Playwright 1.64.1(独立 platform-worker) |
送审必须页面自动化;独立进程隔离;账号级串行锁 |
| 部署 |
单机 Docker Compose(api + 4 worker + web/nginx) |
首期够用;代码保持无本地状态,后续可平滑扩副本 |
| 对象存储 |
开发本地磁盘 / 生产 OSS(S3 适配层) |
开发机不新增容器;生产支持多副本与签名 URL |
| 数据库/缓存部署 |
复用开发机已有 Docker(PG 18 / Redis 7),平台不负责安装 |
用户明确要求;.env 直连 localhost |
与评估文档的差异(详见 §18):ORM 由评估时的 Sequelize 改为 Prisma,工作量 +8~12 人日(全量 383569 → **391581 人日**);UI 由评估的 Tailwind 3 改为 Tailwind 4(+1~3 人日验证,在不确定性内);其余方案不变。
1 决策依据与约束
1.1 硬约束(用户已确认)
| 约束 |
值 |
来源 |
| Node.js |
24.15.0 |
与开发机一致 |
| 包管理 |
pnpm,非必要不用 npm |
用户明确要求 |
| PostgreSQL |
18(开发机 Docker postgres:18-alpine,实际 18.4,5432) |
用户明确要求 |
| Redis |
7(开发机 Docker redis:7-alpine,实际 7.4.9,6379) |
用户明确要求 |
| 部署 |
内部平台,但公网可访问 + 登录鉴权 |
用户确认 |
| 对象存储 |
开发本地磁盘,生产 OSS |
用户确认 |
| 数据库/缓存 |
开发前直接开启已有容器,平台不做安装/编排 |
用户明确要求 |
| 形态 |
统一 WEB 平台,桌面端停止演进 |
用户确认 |
| 工程 |
新建统一工程 + 迁移两个参考项目资产 |
用户确认 |
| UI |
Semi Design 最新版(支持 React 19)+ Tailwind CSS 4 |
用户确认 |
| ORM |
Prisma |
用户确认 |
1.2 参考项目继承关系
| 资产 |
来源 |
在本工程中的处理 |
| 认证/口令安全/RBAC/客户范围/系统配置/任务事件/通知/导出 |
scgf backend/src |
迁移业务逻辑;数据访问层改为 Prisma,模型重写 |
| PSD 解析/合成/抠图/字体/命名引擎 |
scgf utils/* |
代码级迁移;渲染行为锁定(sharp/ag-psd/canvas 版本不变) |
| 星光签名/字段映射/送审命名/混剪规则/DPLink/审核 CSV |
dewu scripts/lib/*、electron/platform/* |
抽为 packages/domain / packages/platform-dewu;纯逻辑几乎原样迁移 |
| 送审批次策略(串行/幂等/回查/审计) |
dewu electron/submit/* |
迁移逻辑,存储改 DB + Redis |
| 只读接口层、商品详情 SSR 直取 |
dewu electron/platform/*、shop/detail.ts |
服务端化,去掉 Electron |
| Electron 外壳/更新/签名/窗口/本地文件 |
dewu electron/* |
废弃 |
| 页面信息架构(选品/物料/审核/送审/投放) |
dewu src/pages/* |
交互参考;组件按 Semi 2.104 重写 |
| 页面信息架构(模板/生产/任务/配置) |
scgf frontend/src/pages/* |
交互参考;按新 IA 重写 |
1.3 本机实测结论(定稿依据)
| 项 |
实测 |
结论 |
| Node |
v24.15.0 |
与要求一致;Prisma 7 要求 ^24.11,满足 |
| pnpm |
11.4.0 |
锁定;packageManager 写 11.4.0 |
| PG |
PostgreSQL 18.4(alpine,5432) |
直连;使用 PG18 原生 uuidv7() |
| Redis |
redis_version:7.4.9(6379) |
BullMQ 6 支持 Redis 7 |
| 图像原生依赖 |
sharp 0.32.6、ag-psd 30.1.0、@napi-rs/canvas 0.1.97、fontkit 2.0.4、@imgly/background-removal-node 1.4.5 均可在 Node 24.15 arm64 加载(libvips 8.14.5) |
锁定该组合作为图像基线 |
| pnpm registry 最新版本 |
Semi 2.104.0、Tailwind 4.3.3、Vite 8.3.5、Prisma 7.10.1、Express 5.3.0、BullMQ 6.5.0、Playwright 1.64.1 |
见 §2 版本总表 |
2 技术栈总表(版本锁定)
标记 🔒 的为精确锁定(升级需回归/评审);其余使用 caret 范围 + 提交 lockfile。
版本以 2026-10-30 查询/实测为准;pnpm-lock.yaml 是唯一事实来源。
2.1 运行时与工具链
| 类别 |
技术 |
版本 |
说明 |
| 运行时 |
Node.js |
24.15.0 🔒 |
.nvmrc、engines、Docker 镜像三处一致 |
| 包管理 |
pnpm |
11.4.0 🔒 |
packageManager;corepack;禁止 npm |
| 语言 |
TypeScript |
5.9.3 🔒 |
strict;typescript-eslint 8.72 要求 <6.1 |
| 开发执行 |
tsx |
4.23.15 |
后端/脚本热执行 |
| 构建 |
Vite |
8.3.5 🔒 |
前端;Node >=22.12 |
| Lint |
ESLint |
10.13.0 🔒 |
flat config |
| Lint |
typescript-eslint |
8.72.1 🔒 |
支持 ESLint 8/9/10、TS <6.1 |
| 格式 |
Prettier |
3.9.10 |
与 ESLint 分离 |
| 单测 |
Vitest |
5.0.5 🔒 |
支持 Vite 8 |
| 覆盖率 |
@vitest/coverage-v8 |
5.0.5 |
— |
| E2E |
Playwright |
1.64.1 🔒 |
同时用于平台自动化;浏览器版本随包锁定 |
2.2 前端
| 类别 |
技术 |
版本 |
说明 |
| 框架 |
React / react-dom |
19.3.1 🔒 |
Semi 2.104 支持 React 19 |
| 路由 |
react-router-dom |
7.18.5 |
Data Router;路由级 lazy |
| 组件库 |
@douyinfe/semi-ui |
2.104.0 🔒 |
最新稳定;zh_CN、暗色、Table/Form/Upload/Tree |
| 组件库构建 |
@douyinfe/semi-vite-plugin |
2.104.0 🔒 |
主题/CSS Layer;peer vite >=3 |
| 图标 |
@remixicon/react |
4.9.0 |
与 Semi 搭配 |
| CSS |
Tailwind CSS |
4.3.3 🔒 |
@tailwindcss/vite 4.3.3;@theme token |
| 构建插件 |
@vitejs/plugin-react |
6.1.2 🔒 |
peer Vite ^8;React 19 |
| 状态 |
zustand |
5.0.16 |
会话/UI 状态 |
| 服务端状态 |
@tanstack/react-query |
5.105.0 |
列表/轮询/缓存 |
| HTTP |
axios |
1.21.0 |
拦截器、刷新令牌、上传进度 |
| 表单 |
react-hook-form |
7.91.0 |
复杂表单 |
| 校验 |
@hookform/resolvers + zod |
6.0.2 / 4.6.7 |
与后端共享 schema |
| 图表 |
recharts |
3.10.1 |
Dashboard |
| 日期 |
dayjs |
1.11.23 |
zh-cn |
| Markdown |
marked |
18.1.2 |
发版说明/富文本白名单渲染 |
| 测试 |
@testing-library/react |
16.3.3 |
React 19;配合 jsdom 30.1.3 |
2.3 后端
| 类别 |
技术 |
版本 |
说明 |
| Web 框架 |
Express |
5.3.0 🔒 |
迁移 scgf 中间件;不选 Nest/Fastify |
| ORM |
Prisma |
7.10.1 🔒 |
稳定线;8 仍为 RC,不采用 |
| ORM 客户端 |
@prisma/client |
7.10.1 🔒 |
ESM;prisma-client 生成器 |
| PG 驱动适配 |
@prisma/adapter-pg |
7.10.1 🔒 |
连接 PG18 |
| PG 驱动 |
pg |
8.23.1 |
适配器/运维脚本/健康检查 |
| 数据库 |
PostgreSQL |
18(18.4)🔒 |
已有 Docker;PG18 uuidv7() |
| 缓存/队列 |
Redis |
7(7.4.9)🔒 |
已有 Docker |
| Redis 客户端 |
ioredis |
6.0.0 🔒 |
BullMQ 6 推荐 |
| 队列 |
BullMQ |
6.5.0 🔒 |
5 类队列;Redis 7 兼容 |
| 鉴权 |
jsonwebtoken |
9.0.3 |
access/refresh + pwdv |
| 密码 |
bcrypt |
6.0.0 |
复用 scgf 策略 |
| 上传 |
multer |
2.5.0 |
内存/临时盘;大文件直传 OSS |
| 校验 |
zod |
4.6.7 |
DTO/环境变量/表单共享 |
| 日志 |
pino + pino-http |
10.4.0 / 11.0.0 |
结构化 JSON |
| 安全头 |
helmet |
8.3.0 |
公网暴露必配 |
| CORS |
cors |
2.8.6 |
白名单 |
| 压缩 |
compression |
1.8.2 |
静态/JSON |
| 限流 |
express-rate-limit + rate-limit-redis |
8.7.2 / 6.0.2 |
Redis 共享限流 |
| 打包 |
archiver |
8.0.0 |
ZIP level 9 |
| 表格 |
exceljs |
4.4.0 |
CSV/XLSX 导入导出 |
| ID |
uuid |
14.0.3 |
仅应用侧特殊场景;主键用 PG uuidv7() |
| 环境变量 |
dotenv |
18.0.9 |
dev 加载;prod 由编排注入 |
| 指标 |
prom-client |
15.1.3 |
/metrics |
| 对象存储 |
@aws-sdk/client-s3 + presigner |
3.1160.0 |
S3/MinIO |
| 阿里云 OSS |
ali-oss |
6.23.0 |
生产 OSS 驱动 |
| MFA(管理员) |
otplib |
13.5.0 |
TOTP;公网暴露必配 |
2.4 图像 / PSD / 平台 / AI
| 类别 |
技术 |
版本 |
说明 |
| 图像处理 |
sharp |
0.32.6 🔒 |
与 scgf 引擎行为一致;不可随意升级 |
| PSD 解析 |
ag-psd |
30.1.0 🔒 |
图层读取 |
| Canvas |
@napi-rs/canvas |
0.1.97 🔒 |
合成画布 |
| 字体 |
fontkit |
2.0.4 🔒 |
字体名/排版 |
| 抠图 |
@imgly/background-removal-node |
1.4.5 🔒 |
ONNX medium;独立 Worker |
| 浏览器自动化 |
Playwright |
1.64.1 🔒 |
platform-worker;Chromium 版本随包 |
| AI Provider |
火山方舟 Seedream(图片)/ 视频模型待选 |
通过 REST 适配 |
不绑定 SDK;密钥只走环境变量 |
| AI 校验 |
VLM(文字逐字/合规) |
通过 REST 适配 |
可插拔 provider |
2.5 为什么是这些版本(关键兼容性证据)
| 组合 |
证据 |
| Node 24.15 + Prisma 7.10.1 |
Prisma engines:^22.18 || ^24.11 || >=26;@prisma/client:^20.19 || ^22.12 || >=24.0 |
| Vite 8.3.5 + Node 24.15 |
Vite engines:^20.19.0 || >=22.12.0 |
| Vite 8 + Tailwind 4 |
@tailwindcss/vite peer:^5.2 || ^6 || ^7 || ^8 |
| Vite 8 + Semi 插件 |
semi-vite-plugin peer:vite >=3 |
| Vite 8 + plugin-react 6.1.2 |
peer vite ^8;其 oxc/babel peer 均为 optional |
| React 19.3 + Semi 2.104 |
Semi peer react >=16;dewu 已在 React 19.3 + Semi 2.103 生产验证 |
| TS 5.9.3 + typescript-eslint 8.72 |
typescript-eslint peer typescript >=4.8.4 <6.1.0(故不选 TS 7.0.2) |
| ESLint 10 + typescript-eslint 8.72 |
peer 支持 ^10.0.0 |
| BullMQ 6.5 + Redis 7.4 |
BullMQ peer ioredis >=5、Node >=14.18;Redis 7 兼容 |
| 图像栈 + Node 24 arm64 |
本机实测 5 个原生包全部加载成功(libvips 8.14.5) |
3 架构总览
3.1 运行拓扑(开发环境)
开发机不新增 PG/Redis 容器:平台进程直接连已有 Docker;文件落本地目录。
3.2 运行拓扑(生产)
关键设计:
- API 无状态(会话在 JWT/Redis;文件在对象存储)→ 可水平扩副本。
- Worker 按负载类型拆进程:图像/抠图吃 CPU+内存,Playwright 吃内存+浏览器,AI 吃网络等待,导出吃 IO。
- 平台账号写操作在
worker-platform 内按账号串行,API 不直接开浏览器。
3.3 请求与任务链路
4 Monorepo 结构与包职责
4.1 目录
4.1.1 模块规范
- 全仓库 ESM:各包
package.json 设 "type": "module",tsconfig 用 module: NodeNext + verbatimModuleSyntax。
- Prisma 7 使用
prisma-client 生成器产出 ESM client,统一从 @lingxi/db 导入。
- 后端构建用
tsc(ESM 输出),开发用 tsx;不使用 CommonJS require。
- 禁止跨包相对路径引用;统一
@lingxi/* workspace 协议。
4.2 包依赖规则
| 规则 |
说明 |
apps/* 不得被其他包依赖 |
应用是组合根 |
packages/domain 只依赖 shared |
纯逻辑、可单测、不碰 DB/HTTP |
packages/platform-* 依赖 domain + shared |
平台协议/页面自动化;不反向依赖 API |
packages/db 只暴露 Prisma client 与 Repository |
禁止业务直接 import Prisma client 到处用 |
packages/ui 只依赖 React/Semi/Tailwind |
不含业务请求 |
跨包引用走 @lingxi/* workspace 协议 |
pnpm 严格链接,禁止相对路径穿透 |
4.3 根 scripts(只用 pnpm)
5 前端架构(React 19 + Semi 2.104 + Tailwind 4)
5.1 应用结构
- 路由:
react-router-dom 7 Data Router;按 features 懒加载;路由守卫统一在 app/routes.tsx。
- 权限:后端
/auth/me 返回 permissions[];菜单/按钮用 <Can permission="...">;前端不作为安全边界,后端每个接口独立校验。
- 状态:服务端状态全部走 TanStack Query;仅会话/主题/布局用 zustand。禁止把列表数据塞 zustand。
- 请求:单一
shared/api/client.ts(axios);自动附加 access token;401 触发一次 refresh;失败统一 toast + 错误码映射。
- 表单:react-hook-form + zod(schema 来自
@lingxi/shared),Semi Form 作为展示层。
5.2 Semi 2.104 + Tailwind 4 集成(必须按此顺序,否则静默失效)
硬性约定:
@import 顺序即层叠顺序:Tailwind base → Semi → 主题 token → 页面样式;页面级样式不得写未分层的 * { margin:0 }。
- 品牌色/圆角/字号只允许在
tokens.css 的 @theme 与 semi-theme.css 的 --semi-color-* 定义,页面禁止硬编码色值(CI 用脚本校验)。
- 不使用
.semi-* 内部类做覆盖;只通过 Semi 语义 token 与 ConfigProvider。
- 组件统一从
@lingxi/ui 导出(薄封装 Semi),页面不直接 import Semi 的内部组件,便于后续换肤/升级。
- M0 必须先做构建 Spike:Semi 2.104 + Tailwind 4.3 + Vite 8 同时引入,验证 modal/drawer/table 样式与
@layer 顺序;失败则回退方案见 §19 R1。
5.3 页面与路由(首期)
| 路由 |
页面 |
首期 |
/ |
工作台/Dashboard |
是 |
/selection |
选品(检索/圈选/历史) |
是 |
/materials |
原始物料 |
是 |
/image/templates |
模板资产 |
是 |
/image/produce |
图片制作(三规格/四层合成/批量) |
是 |
/review |
提审与审核 |
是(MVP-2) |
/delivery |
投放配置/DPLink |
是(MVP-2) |
/assets |
资产中心 |
M4 |
/system/* |
用户/角色/客户/配置/任务日志 |
是 |
/video/* |
视频产线 |
M5 |
5.4 构建与性能
- Vite 8:
build.target = es2022;路由级 code-split;Semi 按需(插件已处理)。
- 图片列表用缩略图 URL(
?w=320);大图预览走原图/签名 URL。
- 表格默认分页 20/50;超过 200 行启用虚拟滚动(Semi Table 支持)。
- 首屏预算:JS ≤ 350KB gzip(不含图片);超过则拆包。
6 后端架构(Express 5)
6.1 分层
- Controller 不写 SQL/Prisma;Service 不直接读
req。
- 事务:Service 层用
prisma.$transaction;跨任务/文件操作先落库再入队。
- 错误:统一
AppError(code, message, httpStatus, details);错误码在 packages/shared/errors.ts;响应不泄露堆栈。
- 请求 ID:
x-request-id 透传 pino;审计与日志可关联。
6.2 API 规范
| 项 |
约定 |
| 前缀 |
/api/v1 |
| 成功 |
{ "data": ..., "requestId": "..." } |
| 失败 |
{ "error": { "code": "AUTH_401", "message": "...", "details": ... }, "requestId": "..." } |
| 分页 |
?page=1&pageSize=20;返回 { items, total, page, pageSize };大列表用 cursor |
| 幂等 |
写操作支持 Idempotency-Key;任务创建以 (batchId, templateId, productId) 唯一约束兜底 |
| 时间 |
ISO 8601 UTC;DB timestamptz |
| 文件 |
上传走 POST /files/presign(生产)或 POST /files/upload(开发本地) |
| 实时 |
GET /api/v1/events?topics=batch:{id}(SSE) |
| 文档 |
zod → OpenAPI(dev 环境 /api/docs);不维护手写文档 |
6.3 中间件顺序(固定)
helmet 在公网必须开启;cors 只允许 WEB_ORIGIN。
- SSE 路由放在
compression 之后但显式跳过压缩,并设置 Cache-Control: no-cache、X-Accel-Buffering: no。
- 上传路由使用 multer 单独限制大小与 MIME,不适用全局 body 限制。
6.4 健康检查与可观测
| 端点 |
用途 |
GET /health/live |
进程存活(不查依赖) |
GET /health/ready |
DB/Redis/对象存储探活;失败 503 |
GET /metrics |
Prometheus(内部网段/鉴权) |
- 日志:pino JSON;字段
requestId/userId/platformId/taskId;禁止打印 token/密码/签名盐。
- 指标:HTTP P95、队列积压、任务成功率/时长、图像处理耗时、AI 调用成本。
7 数据层(PostgreSQL 18 + Prisma 7.10.1)
7.1 Prisma 约定
- 生成器:Prisma 7 的
prisma-client(ESM),输出到 packages/db/src/generated/prisma;应用统一从 @lingxi/db 导入。
- 连接:
@prisma/adapter-pg(driver adapter)+ pg;连接池 max=10(API)/max=3(Worker),按实例数核算 PG max_connections。
- 迁移:本地
prisma migrate dev;CI/生产 prisma migrate deploy;禁止 db push 到生产。
- 命名:
@@map("snake_case")、@map("snake_case");Prisma 字段 camelCase,DB 列 snake_case。
- 主键:
String @id @default(dbgenerated("uuidv7()")) @db.Uuid(PG18 原生 uuidv7(),时间有序、索引友好)。
- 时间:
DateTime @db.Timestamptz(6);统一 UTC。
- JSON:
Json(映射 JSONB);仅用于配置/快照/扩展字段,不用于高频过滤。
- 软删除:
deletedAt DateTime?;查询统一走 Repository 的 notDeleted。
- 枚举:稳定集合用 Prisma enum;运营可维护的字典用表(如失败原因、类目)。
- 索引:
platform_id/client_id/status/created_at 组合;任务表 (status, created_at);事件表 (task_id, created_at)。
7.2 初始 schema 范围(与评估 §5.6 对齐)
| 域 |
表(Prisma model) |
| 组织 |
Platform、Project、Client、User、Role、UserClient |
| 凭据 |
PlatformAccount(能力:soft/hard/review;密文;归属) |
| 商品/选品 |
ProductSnapshot、SelectionBatch、SelectionItem |
| 物料 |
RawAsset |
| 模板/资产 |
Template、TemplateCategory、TemplateTag、TemplateTagRelation、TemplateSpec、Asset |
| 生产 |
ProductionBatch、ProductionTask、Material、NamingRule |
| 提审 |
ReviewTicket、ReviewItem、SubmitLedger |
| 投放 |
DeliveryConfig、DeliveryLink |
| 任务/通知 |
TaskEvent、FailureCatalog、Notification、NotificationTemplate |
| 配置/审计 |
SystemConfig、SystemConfigAudit、UserAudit、OperationAudit |
共约 33 张表;与评估文档的数据模型一致,仅访问层从 Sequelize 改为 Prisma。
7.3 迁移与种子
- 初始化:
pnpm db:create(Node 脚本连 postgres 库,CREATE DATABASE lingxi,不存在才建)→ pnpm db:migrate → pnpm db:seed。
- 种子:管理员账号(口令随机、仅显示一次)、基础角色/权限、得物平台、默认规格(800×800 / 1280×720 / 1080×1920)、字典。根
package.json 需声明 "prisma": { "seed": "tsx prisma/seed.ts" }。
- 回滚:Prisma 迁移是前向的;破坏性变更采用 expand/contract(先加列/双写,再切读,最后删列),保留 down 说明在迁移目录 README。
- 备份:生产每日
pg_dump + WAL;迁移前强制快照。
7.4 数据隔离
- 单租户(内部平台);数据范围:
platformId + clientId。
- 所有列表查询必须带范围条件(Repository 强制参数);
fail-closed:无范围 = 查不到,而不是全量。
- 审计表不做外键级联,保留历史。
8 Redis、队列与实时
8.1 Redis 使用边界
| 用途 |
Key 前缀 |
说明 |
| BullMQ |
lingxi:bull:* |
5 类队列 |
| 缓存 |
lingxi:cache:* |
平台枚举/标签树/配置;TTL |
| 限流 |
lingxi:rl:* |
登录、API、AI 调用 |
| 锁 |
lingxi:lock:account:{id} 等 |
SET NX PX + Lua 释放 |
| Pub/Sub |
lingxi:events |
SSE 广播 |
| 会话(可选) |
lingxi:sess:* |
刷新令牌黑名单/设备 |
- Redis 7 已有;不执行
FLUSHALL;统一前缀,避免与其他项目冲突。
- 禁用
KEYS;批量用 SCAN。
- 本地开发使用 db 0;测试用 db 15(
REDIS_URL=.../15)。
8.2 队列拓扑
| 队列 |
生产者 |
消费者 |
并发建议 |
说明 |
image-render |
api |
worker-image |
2~4 |
PSD/合成/抠图;吃内存 |
platform-task |
api |
worker-platform |
每账号 1 |
登录/送审/审核/投放;串行 |
ai-image |
api |
worker-ai |
2~4 |
外部 API;限流/配额 |
ai-video |
api |
worker-ai |
1~2 |
长任务;M5 |
export |
api |
worker-export |
2 |
ZIP/CSV;IO 密集 |
约定:jobId = DB task id;attempts=3(平台写操作只 1 次 + 回查);指数退避;removeOnComplete 保留 1000/1 天;失败进 DLQ(*-failed)。DB 是事实来源;每 60s reconciler 扫描 processing 超时任务重新入队或标记失败。
8.3 SSE(任务进度)
- 端点:
GET /api/v1/events?topics=batch:{id},platform:{code};text/event-stream。
- 鉴权:登录 cookie/JWT(EventSource 不能带 header → 用短时 ticket:
POST /events/ticket 换 60s ticket)。
- 服务端:worker 更新任务 →
publish(lingxi:events, {topic, seq, payload}) → 每个 api 实例订阅并 fan-out 给本实例 SSE 连接。
- 事件:
{ type: 'batch.progress' | 'task.status' | 'notification', topic, seq, ts, data }。
- 心跳 15s;断线重连用
Last-Event-ID(seq 游标,保留最近 5 分钟环形缓冲)。
- nginx:单独 location,
proxy_buffering off; proxy_read_timeout 3600s;。
8.4 分布式锁与幂等
- 账号锁:
SET lock:account:{id} <token> NX PX 900000;释放用 Lua 比对 token;超时自动释放并告警。
- 任务幂等:DB 唯一约束 +
jobId;重复投递由 BullMQ 去重。
- 提交幂等:
SubmitLedger(md5)先查后提;平台返回重复码时按已提交处理。
- 限流:登录 5 次/分钟/IP+用户名;普通 API 300 次/分钟/用户;AI 按日配额。
9 文件与对象存储
9.1 StorageAdapter
| 驱动 |
使用场景 |
实现 |
local |
开发/单机 |
./var/uploads;/files/:key 鉴权后流式返回;禁止路径穿越 |
s3 |
MinIO/兼容 S3 |
@aws-sdk/client-s3 + presigner |
oss |
阿里云 OSS 生产 |
ali-oss(STS/签名 URL) |
- 配置:
STORAGE_DRIVER=local|s3|oss;启动时 zod 校验必填项。
- Key 规则:
{platformCode}/{clientId}/{domain}/{yyyy}/{mm}/{uuid}.{ext};禁止使用客户端文件名作为 key。
- 缩略图:图片上传后由
worker-image 生成 _thumb.webp(?w=320/640);清理任务按引用计数/保留期。
- 大文件(视频 140–190MB):生产走浏览器/客户端直传 OSS(STS 临时凭证),API 只登记元数据;Playwright 送审时 worker 从对象存储下载到本地临时目录再
setInputFiles。
- 生命周期:原始物料默认长期保留;导出包 7~30 天;临时文件 24 小时。
10 平台适配与 Playwright(worker-platform)
10.1 适配器接口
- 得物:读链路走签名 HTTP(
packages/platform-dewu/http.ts);写链路默认页面自动化(page.ts),直连写接口作为 POC 备选。
- 唯品会:M3 开始,先摸登录/接口/提审流程,再实现同一接口。
- 不另开浏览器 context:同一账号复用持久化 context;token 由凭据服务单写(API 只读,禁止自行重登)。
10.2 Worker 运行约束
| 项 |
约定 |
| 浏览器 |
Chromium 版本随 Playwright 1.64.1 锁定;pnpm playwright:install 安装 |
| 账号并发 |
同一 platformAccountId 同时只允许 1 个写任务(Redis 锁) |
| 只读并发 |
全局上限 4;按平台限速 |
| 失败证据 |
截图 + DOM 快照 + reasonCode 存对象存储,DB 记录 key |
| 选择器 |
全部走配置(selectors.dewu.json),禁止散落硬编码;关键元素缺失触发健康告警 |
| 上传 |
大文件先落本地临时目录,setInputFiles 后清理;超时 15 分钟 |
| 重试 |
网络/5xx 重试 1 次;提交超时只回查不重试 |
11 图片产线(worker-image)
- 输入:模板(PSD)+ 商品图 + 展示方式资产 + Logo + 促销模板 + 字段;输出:三规格图片 + 命名 + 元数据。
- 流程:
readPsd → 资产快照 → 图层合成(保持 PSD z 序,按 assetType 取资产)→ 抠图(仅白底)→ 压缩(JPG 二分质量 / PNG 调色板)→ 写对象存储 → Material 落库。
- 版本:sharp 0.32.6 / ag-psd 30.1.0 / canvas 0.1.97 / fontkit 2.0.4 锁定;升级必须过黄金样本。
- 回归:
test/golden/ 存 ≥5 个真实模板 + 期望图;像素 diff 阈值(默认 mean diff < 0.5%,单像素阈值 8/255);CI 跑 pnpm test:golden。
- 字体:内置字体清单与授权登记;换字体必须跑回归;缺字形时显式失败(不许"豆腐块但任务成功")。
- 并发:worker-image 独立进程,默认 2 并发;内存上限监控,OOM 重启由容器策略兜底。
12 AI 网关(worker-ai)
12.1 接口
- Provider:
ark(火山方舟 Seedream,默认);可插拔其他厂商。
- 模型分层:成品默认
pro,草稿/批量初筛用 flash;模型名/单价/限额全部配置化。
- 队列:
ai-image / ai-video;按账号级并发与 RPM 限流;429 退避。
- 成本:每次调用写
AiUsage(provider/model/tokens/单价/金额/任务);日/月预算告警;超限降级或拒绝。
- 合规:文字逐字校验 + 人工复核闸门 + 品牌 Logo 程序贴回;生成内容留痕(prompt/输出/审核人)。
- 密钥:只读环境变量/KMS;禁止进 DB/系统配置/日志。
13 认证、权限、凭据与安全
13.1 认证
| 项 |
约定 |
| Access Token |
JWT,15 分钟,前端仅内存保存(不写 localStorage) |
| Refresh Token |
httpOnly + Secure + SameSite=Strict Cookie,7 天,旋转 + 复用检测 |
| 口令 |
bcrypt 12;策略复用 scgf(8–20 位、字母+数字、弱口令库、不得含用户名) |
| 强制改密 |
首次登录/管理员重置;服务端 403 PASSWORD_CHANGE_REQUIRED |
| 失效 |
JWT 携带 pwdv;改密/重置递增,旧令牌立即失效 |
| MFA |
管理员与高权限角色强制 TOTP;公网暴露的前置 |
| 限流 |
登录 5 次/分钟/IP+用户名;失败锁定 15 分钟 |
13.2 权限
- RBAC:
admin / designer / optimizer / operator;权限 key(如 template.write、review.submit、delivery.manage)。
- 数据范围:
platformId × clientId;Repository 强制注入;越权返回 404(不泄露存在性)。
- 平台账号:
PlatformAccount.capabilities(soft/hard/review);使用账号需授权;敏感操作二次确认 + 审计。
13.3 凭据加密
- 算法:AES-256-GCM;密钥
PLATFORM_CRED_KEY(32 字节 base64,环境变量/KMS 注入);AAD 绑定 accountId。
- 存储:
PlatformAccount.credentialCipher(密文)+ keyVersion;轮换时后台重加密。
- 禁止:写入日志、审计 old/new 明文、系统配置表、前端响应。
13.4 公网暴露面
| 控制 |
要求 |
| HTTPS |
TLS 1.2+;HSTS |
| WAF |
云 WAF/高防(建议);至少 nginx + 限流 |
| 安全头 |
helmet:CSP、X-Frame-Options、nosniff、Referrer-Policy |
| CORS |
仅 WEB_ORIGIN |
| MFA |
管理员强制;运营可配置 |
| 依赖/镜像 |
CI pnpm audit --prod;镜像扫描 |
| 渗透测试 |
上线前 1 次;每年 1 次 |
| 备份 |
PG 每日 + WAL;对象存储版本化 |
| 审计 |
登录、权限变更、凭据使用、提审/投放、导出 |
14 工程规范与质量门禁
14.1 TypeScript / Lint
strict: true、noUncheckedIndexedAccess、exactOptionalPropertyTypes(新代码);禁止 any(unknown + 收窄)。
- ESLint 10 flat config:
@typescript-eslint、react-hooks、import 排序;pnpm lint 零 warning。
- Prettier 3.9:单引号、无分号、printWidth 100;
pnpm format:check 入 CI。
- commitlint + cz-git(沿用 scgf 规范):
feat/fix/docs/refactor/test/chore。
14.2 测试分层
| 层 |
工具 |
范围 |
门禁 |
| 单元 |
Vitest 5 |
domain/命名/校验/成本 |
覆盖率 ≥ 70%(domain ≥ 85%) |
| 集成 |
Vitest + PG/Redis 测试库 |
Repository/事务/队列/SSE |
每次 PR |
| 契约 |
Vitest + 抓包 fixture |
星光只读接口/字段映射 |
每次 PR |
| 黄金图 |
Vitest + pixelmatch |
PSD 合成/压缩 |
每次改图像代码 |
| E2E |
Playwright 1.64 |
登录→选品→出图→提审 |
每日/M1 后 |
- 测试库:
lingxi_test(PG)+ Redis db 15;每个测试文件事务回滚或 truncate。
- 不 mock 数据库(集成层);外部平台用 fixture/mock server。
14.3 CI(pnpm only)
- 服务:
postgres:18-alpine、redis:7-alpine(GitHub Actions services 或 GitLab services)。
- 缓存:pnpm store;Node 24.15.0 由
.nvmrc/action 指定。
- 任何步骤禁止
npm install;如遇工具要求 npm,用 pnpm dlx 替代 npx。
15 开发环境与命令(本机)
15.1 前置(已具备)
| 项 |
本机 |
说明 |
| Node |
24.15.0 |
.nvmrc 固定 |
| pnpm |
11.4.0 |
corepack enable 后由 packageManager 锁定 |
| PG |
18.4 @ 127.0.0.1:5432 |
Docker 已有;平台不启动/不安装 |
| Redis |
7.4.9 @ 127.0.0.1:6379 |
Docker 已有;平台不启动/不安装 |
15.2 首次启动
15.3 .env.example
15.4 端口与目录
| 服务 |
端口 |
说明 |
| web |
5173 |
Vite dev;/api 代理到 8300 |
| api |
8300 |
避开常见 3000,降低冲突 |
| PG |
5432 |
已有容器 |
| Redis |
6379 |
已有容器 |
| 本地文件 |
./var/uploads |
已 gitignore |
| Playwright |
./.cache/ms-playwright |
已 gitignore |
15.5 pnpm 构建脚本白名单
pnpm 11 默认拦截依赖的安装脚本;以下必须放行(与 scgf pnpm-workspace.yaml 一致):
若 pnpm 11 提示 allowBuilds 未知字段,改用 onlyBuiltDependencies(同上清单)。CI 以 pnpm install --frozen-lockfile 成功为准。
16 构建与部署
16.1 镜像
| 镜像 |
基础 |
内容 |
lingxi/web |
nginx:1.29-alpine |
构建后的 SPA + nginx 配置 |
lingxi/api |
node:24.15.0-bookworm-slim |
api 构建产物 + prisma 运行时 |
lingxi/worker-* |
同上 |
各自 worker 产物 |
- 构建阶段:
corepack enable → pnpm install --frozen-lockfile → pnpm --filter <pkg> build;运行阶段只拷贝 dist + node_modules(pnpm deploy/--prod)。
- 非 root 用户运行;
NODE_ENV=production。
- Node 镜像若暂无
24.15.0-bookworm-slim tag:锁定 node:24-bookworm-slim 的 digest,并在构建阶段执行 node -v 断言 v24.15.0,不通过则失败。
16.2 Compose(生产/联调)
- 发布顺序:备份 →
prisma migrate deploy(一次性 job)→ api → workers → web → 健康检查。
- 迁移采用 expand/contract,保证旧版本可回滚;镜像保留上一版。
- 备份:每日
pg_dump + WAL 归档;对象存储版本化;恢复演练每季度。
16.3 nginx 关键配置
| 路径 |
配置 |
/ |
SPA,try_files $uri $uri/ /index.html |
/api/ |
反代 api:8300;client_max_body_size 300m;超时 300s |
/api/v1/events |
proxy_buffering off; proxy_read_timeout 3600s; |
/files/ |
反代 api(本地驱动)或直连对象存储(生产用签名 URL 跳过) |
17 版本治理
| 类别 |
策略 |
| Node/pnpm |
精确锁定;.nvmrc + engines + 镜像三处一致 |
| Playwright |
精确锁定;浏览器 revision 随包;升级需回归送审选择器 |
| 图像栈 |
精确锁定;升级需黄金样本 + 像素 diff 通过 |
| Prisma |
精确锁定 minor;升级需跑迁移与集成测试 |
| UI 库 |
caret + lockfile;每月升级窗口 |
| 依赖 |
pnpm-lock.yaml 提交;pnpm audit --prod 入 CI |
| 废弃 |
不保留 Electron/桌面端构建链 |
18 与评估文档的差异(需同步)
| 项 |
评估(ORM/UI 定稿前) |
本定稿 |
影响 |
| ORM |
Sequelize 6(复用 scgf) |
Prisma 7.10.1(模型/迁移重写) |
+8~12 人日;总量 383569 → **391581**;首期 280414 → **288426** |
| UI CSS |
Tailwind 3.4(dewu 验证) |
Tailwind 4.3.3 + Semi 2.104 |
+1~3 人日集成验证(在 ±25% 内) |
| 运行时 |
Node 24 LTS |
Node 24.15.0 精确 |
无 |
| 数据/缓存 |
PG/Redis 由部署提供 |
复用开发机已有 Docker,不负责安装 |
省去部署工作 |
| 部署 |
单机 Compose |
同 |
公网安全加固 4~8 人日已列 |
| 对象存储 |
S3 适配 |
local(dev)/OSS(prod) |
同 |
评估文档已同步更新为 391581 人日 / 首期 288426 人日(rev.2);本定稿以 Prisma + Tailwind 4 为准。
19 M0 必须验证的风险(Go/No-Go)
| 编号 |
风险 |
验证方式 |
通过标准 |
回退方案 |
| R1 |
Semi 2.104 + Tailwind 4.3 + Vite 8 样式层叠冲突 |
建一个含 Table/Modal/Drawer/Form 的 Spike 页 |
组件样式正确、暗色/中文正常、@layer 顺序可控 |
回退 Tailwind 3.4.19 + Semi 2.104(dewu 已验证) |
| R2 |
Prisma 7.10.1 + PG18 + Node 24.15 |
建 5 张表、迁移、seed、事务、uuidv7 默认值、adapter-pg 连接 |
迁移/回滚/连接池/类型生成全通过;select uuidv7() 返回 v7(本机已验证 PG18 支持) |
回退 Prisma 7.9.x 或评估 Drizzle(需重新评审) |
| R3 |
sharp 0.32.6 / ag-psd 30.1 / canvas 0.1.97 在 Node 24 arm64 + Linux 镜像 |
本机 + Docker 各跑一次合成 |
输出一致、无崩溃 |
逐项升级并重跑黄金样本 |
| R4 |
Playwright 1.64 在 macOS/Linux 行为差异 |
本机跑登录/送审空跑 |
选择器/上传可用 |
锁定 1.60(dewu 已验证) |
| R5 |
Node 24.15.0 官方镜像 tag |
docker build 构建 API 镜像 |
node -v = 24.15.0 |
用 node:24-bookworm-slim + digest + 版本断言 |
| R6 |
OSS SDK 选择(ali-oss vs S3 兼容) |
用测试 bucket 跑 put/sign/get |
签名 URL 可下载、分片上传可用 |
统一改用 S3 兼容网关 |
| R7 |
Prisma 迁移 19 表的一次性搬迁 |
从 scgf 导出数据导入新库 |
行数/文件/命名对账通过 |
分批迁移 + 只读并行 |
20 明确不采用的技术与理由(ADR)
| 不采用 |
理由 |
| Sequelize 6 |
用户选择 Prisma;类型/迁移体验更好;代价是模型重写 +8~12 人日 |
| Tailwind 3 |
用户选择 Tailwind 4;需 M0 验证 Semi 共存 |
| Arco Design / shadcn/ui |
用户选择 Semi;Arco 与 scgf 页面绑定,shadcn 需自建后台组件层 |
| WebSocket |
任务进度单向,SSE 足够;少一套鉴权/心跳/广播实现 |
| Next.js |
内部 SPA,不需要 SSR/SEO;Vite SPA 更简单 |
| NestJS / Fastify |
Express 5 可迁移 scgf 中间件;换框架收益低于成本 |
| MongoDB |
主数据强关系 + 事务 + 报表;PG 合适 |
| Redis 8 / PG 17 |
用户环境已固定 Redis 7 / PG 18 |
| K8s(首期) |
单机 Compose 足够;代码保持无状态,后续可迁移 |
| MinIO(开发必选) |
用户未提供;开发用 local 驱动,生产用 OSS |
| npm |
用户要求统一 pnpm |
| Electron |
桌面端停止演进 |
21 附录 A:.npmrc 与根配置
auto-install-peers=true 用于兼容 Semi/Tailwind 生态的 peer 依赖;锁定 lockfile 后再收紧。
22 附录 B:依赖清单(按包)
完整版本以 pnpm-lock.yaml 为准;下表为定稿基线。
| 包 |
dependencies 摘要 |
apps/web |
react 19.3.1、react-dom 19.3.1、react-router-dom 7.18.5、@douyinfe/semi-ui 2.104.0、@remixicon/react 4.9.0、zustand 5.0.16、@tanstack/react-query 5.105.0、axios 1.21.0、react-hook-form 7.91.0、@hookform/resolvers 6.0.2、zod 4.6.7、recharts 3.10.1、dayjs 1.11.23、marked 18.1.2 |
apps/api |
express 5.3.0、@lingxi/db、@lingxi/shared、@lingxi/domain、@lingxi/storage、@lingxi/ai、bullmq 6.5.0、ioredis 6.0.0、jsonwebtoken 9.0.3、bcrypt 6.0.0、multer 2.5.0、zod 4.6.7、pino 10.4.0、pino-http 11.0.0、helmet 8.3.0、cors 2.8.6、compression 1.8.2、express-rate-limit 8.7.2、rate-limit-redis 6.0.2、archiver 8.0.0、exceljs 4.4.0、uuid 14.0.3、dotenv 18.0.9、prom-client 15.1.3、otplib 13.5.0 |
packages/db |
@prisma/client 7.10.1、@prisma/adapter-pg 7.10.1、pg 8.23.1 |
packages/domain |
zod 4.6.7、dayjs 1.11.23(纯逻辑,不依赖 DB/HTTP) |
packages/platform-dewu |
原生 fetch(Node 24 内置)、playwright 1.64.1、zod 4.6.7 |
packages/storage |
@aws-sdk/client-s3 3.1160.0、@aws-sdk/s3-request-presigner 3.1160.0、ali-oss 6.23.0 |
packages/ai |
原生 fetch、zod 4.6.7 |
apps/worker-image |
sharp 0.32.6、ag-psd 30.1.0、@napi-rs/canvas 0.1.97、fontkit 2.0.4、@imgly/background-removal-node 1.4.5、@lingxi/domain、@lingxi/storage |
apps/worker-platform |
playwright 1.64.1、@lingxi/platform-dewu、@lingxi/platform-vipshop、@lingxi/db |
apps/worker-ai |
@lingxi/ai、@lingxi/storage |
apps/worker-export |
archiver 8.0.0、exceljs 4.4.0、@lingxi/storage |
| 根 devDependencies |
typescript 5.9.3、tsx 4.23.15、vite 8.3.5、@vitejs/plugin-react 6.1.2、tailwindcss 4.3.3、@tailwindcss/vite 4.3.3、@douyinfe/semi-vite-plugin 2.104.0、eslint 10.13.0、typescript-eslint 8.72.1、prettier 3.9.10、vitest 5.0.5、@vitest/coverage-v8 5.0.5、@testing-library/react 16.3.3、jsdom 30.1.3、playwright 1.64.1、pino-pretty 13.2.0、prisma 7.10.1 |
23 附录 C:待确认项
| 编号 |
问题 |
影响 |
建议 |
| C-01 |
生产 OSS 的 region/bucket/账号是否已就绪? |
决定 oss 驱动配置与直传凭证 |
M0 前确认 |
| C-02 |
是否有云 WAF/高防?还是仅 nginx 限流? |
公网安全方案 |
M0 前确认 |
| C-03 |
CI 平台是 GitHub Actions 还是内网 GitLab? |
CI 配置模板 |
M0 前确认 |
| C-04 |
生产是否为单台 Linux + Docker? |
Compose/备份/监控方案 |
M0 前确认 |
| C-05 |
管理员 MFA 是否首期强制? |
登录流程与 otplib 依赖 |
建议首期强制 |
| C-06 |
Tailwind 4 + Semi 的 Spike 谁负责/何时完成? |
前端开工前置 |
M0 第 1 周 |
定稿基于 2026-10-30 的开发机实测、pnpm registry 版本与两个参考项目源码;任何版本调整必须更新本文档与 lockfile。