- 新增双项目整合可行性评估 V0.1 rev.2:覆盖度矩阵、方案对比、目标架构、工作量/TCO、风险与决策清单 - 新增 dewu/sucaigongfang 两份调研报告(内部资料,平台敏感值已脱敏) - 新增技术栈与架构定稿 V1.0:Node 24.15.0 / pnpm 11.4.0 / Prisma 7.10.1 / PostgreSQL 18 / Redis 7 - 更新 README 文档索引与仓库结构
1015 lines
52 KiB
Markdown
1015 lines
52 KiB
Markdown
# 平台电商素材生产平台 · 技术栈与架构定稿
|
||
|
||
> **文档类型**:技术栈与架构设计(定稿)
|
||
> **版本**: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)
|
||
> **密级**:内部;本文不含平台签名盐、账号、密钥等敏感值。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
0. 定稿结论(一页纸)
|
||
1. 决策依据与约束
|
||
2. 技术栈总表(版本锁定)
|
||
3. 架构总览
|
||
4. Monorepo 结构与包职责
|
||
5. 前端架构
|
||
6. 后端架构
|
||
7. 数据层(PostgreSQL 18 + Prisma 7)
|
||
8. Redis、队列与实时
|
||
9. 文件与对象存储
|
||
10. 平台适配与 Playwright
|
||
11. 图片产线
|
||
12. AI 网关
|
||
13. 认证、权限、凭据与安全
|
||
14. 工程规范与质量门禁
|
||
15. 开发环境与命令
|
||
16. 构建与部署
|
||
17. 版本治理
|
||
18. 与评估文档的差异
|
||
19. M0 必须验证的风险
|
||
20. 明确不采用的技术与理由
|
||
21. 附录 A:`.npmrc` 与根配置
|
||
22. 附录 B:依赖清单
|
||
23. 附录 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 人日**(全量 383~569 → **391~581 人日**);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;文件落本地目录。
|
||
|
||
```
|
||
┌───────────────────────────── macOS 开发机 ─────────────────────────────┐
|
||
│ │
|
||
│ 浏览器 ──http://localhost:5173──▶ Vite dev server(apps/web) │
|
||
│ │ /api 代理 │
|
||
│ ▼ │
|
||
│ apps/api(Express 5,:8300) │
|
||
│ │ │ │
|
||
│ │ ├──▶ PostgreSQL 18.4 :5432 │
|
||
│ │ │ (Docker 已有) │
|
||
│ │ ├──▶ Redis 7.4.9 :6379 │
|
||
│ │ │ (Docker 已有) │
|
||
│ │ └──▶ 本地磁盘 ./var/uploads │
|
||
│ │ │
|
||
│ 独立进程(pnpm dev:workers): ▼ │
|
||
│ worker-image(PSD/合成/抠图) worker-platform(Playwright 有头/无头) │
|
||
│ worker-ai(生图/视频) worker-export(ZIP/CSV/打包) │
|
||
│ │ │
|
||
│ └──▶ BullMQ 队列(Redis) │
|
||
└────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 3.2 运行拓扑(生产)
|
||
|
||
```
|
||
公网(HTTPS)
|
||
│
|
||
┌────────▼────────┐
|
||
│ WAF / 云防护 │ (可选但建议)
|
||
└────────┬────────┘
|
||
┌────────▼────────┐
|
||
│ nginx(web 镜像)│ SPA + /api 反代 + SSE + /files
|
||
└────────┬────────┘
|
||
│
|
||
┌────────────────────┼────────────────────┬────────────────────┐
|
||
▼ ▼ ▼ ▼
|
||
api ×1~N worker-image ×1~N worker-platform ×1 worker-export/ai
|
||
│ │ │ │
|
||
└──────────┬─────────┴──────────┬─────────┴──────────┬─────────┘
|
||
▼ ▼ ▼
|
||
PostgreSQL 18 Redis 7 对象存储(OSS/S3)
|
||
(托管/独立容器) (队列/锁/限流) (物料/成片/导出包)
|
||
```
|
||
|
||
**关键设计**:
|
||
- API 无状态(会话在 JWT/Redis;文件在对象存储)→ 可水平扩副本。
|
||
- Worker 按负载类型拆进程:图像/抠图吃 CPU+内存,Playwright 吃内存+浏览器,AI 吃网络等待,导出吃 IO。
|
||
- 平台账号写操作在 `worker-platform` 内**按账号串行**,API 不直接开浏览器。
|
||
|
||
### 3.3 请求与任务链路
|
||
|
||
```
|
||
同步请求:浏览器 → nginx → api → Prisma(PG) → 返回
|
||
异步任务:api 建任务行(PG)→ 入 BullMQ(jobId = taskId)→ worker 执行
|
||
→ 更新任务行 + 写 task_event → Redis Pub/Sub → api SSE → 浏览器
|
||
文件:上传 → api 签发上传凭证(或本地直存)→ 对象存储/本地盘 → 记录 storage_key
|
||
下载 → api 校验权限 → 302 签名 URL / 流式代理
|
||
```
|
||
|
||
---
|
||
|
||
## 4 Monorepo 结构与包职责
|
||
|
||
### 4.1 目录
|
||
|
||
```
|
||
lingxi/
|
||
├── package.json # 根:scripts、engines、packageManager
|
||
├── pnpm-workspace.yaml
|
||
├── pnpm-lock.yaml # 唯一事实来源
|
||
├── .nvmrc # 24.15.0
|
||
├── .npmrc # engine-strict=true 等
|
||
├── tsconfig.base.json
|
||
├── prisma/
|
||
│ ├── schema.prisma # 唯一 schema
|
||
│ ├── migrations/ # prisma migrate 生成
|
||
│ └── seed.ts
|
||
├── apps/
|
||
│ ├── web/ # React 19 + Semi + Tailwind 4(Vite 8)
|
||
│ ├── api/ # Express 5(BFF + 业务 API)
|
||
│ ├── worker-image/ # sharp/ag-psd/canvas/抠图
|
||
│ ├── worker-platform/ # Playwright:登录/送审/审核/投放
|
||
│ ├── worker-ai/ # 生图/视频
|
||
│ └── worker-export/ # ZIP/CSV/XLSX
|
||
├── packages/
|
||
│ ├── shared/ # zod schema、DTO、错误码、枚举、类型
|
||
│ ├── domain/ # 命名引擎、送审规则、DPLink、审核映射(纯逻辑)
|
||
│ ├── platform-dewu/ # 星光签名/只读接口/页面自动化
|
||
│ ├── platform-vipshop/ # 唯品会适配(M3 起)
|
||
│ ├── db/ # Prisma client 单例、事务工具、Repository
|
||
│ ├── storage/ # StorageAdapter(local / s3 / oss)
|
||
│ ├── ai/ # Provider 接口 + Ark 实现 + 成本核算
|
||
│ └── ui/ # 设计 token + Semi 封装 + 通用组件
|
||
├── infra/
|
||
│ ├── docker/ # 各镜像 Dockerfile
|
||
│ ├── nginx/ # SPA + API + SSE + /files
|
||
│ ├── compose/ # dev/prod compose
|
||
│ └── scripts/ # db-create、备份、健康检查
|
||
├── test/
|
||
│ ├── integration/ # PG/Redis 集成测试
|
||
│ ├── golden/ # PSD 黄金样本 + 像素 diff
|
||
│ └── e2e/ # Playwright
|
||
└── docs/
|
||
```
|
||
|
||
### 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)
|
||
|
||
```json
|
||
{
|
||
"packageManager": "pnpm@11.4.0",
|
||
"engines": { "node": "24.15.0", "pnpm": "11.4.0" },
|
||
"scripts": {
|
||
"dev": "pnpm --parallel -r --filter @lingxi/web --filter @lingxi/api run dev",
|
||
"dev:workers": "pnpm --parallel -r --filter './apps/worker-*' run dev",
|
||
"build": "pnpm -r build",
|
||
"lint": "eslint .",
|
||
"format": "prettier --write .",
|
||
"typecheck": "pnpm -r typecheck",
|
||
"test": "vitest run",
|
||
"test:integration": "vitest run -c vitest.integration.config.ts",
|
||
"test:e2e": "playwright test",
|
||
"db:create": "tsx infra/scripts/db-create.ts",
|
||
"db:migrate": "prisma migrate dev",
|
||
"db:deploy": "prisma migrate deploy",
|
||
"db:seed": "prisma db seed",
|
||
"db:studio": "prisma studio",
|
||
"playwright:install": "playwright install chromium"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5 前端架构(React 19 + Semi 2.104 + Tailwind 4)
|
||
|
||
### 5.1 应用结构
|
||
|
||
```
|
||
apps/web/src/
|
||
├── app/ # 入口、路由、Provider、错误边界
|
||
├── layouts/ # 侧边栏/顶栏/权限布局
|
||
├── features/
|
||
│ ├── platform/ # 平台切换
|
||
│ ├── selection/ # 选品/圈选/历史
|
||
│ ├── materials/ # 原始物料
|
||
│ ├── image/ # 模板/制作/批量/预览
|
||
│ ├── video/ # 脚本/画面/成片
|
||
│ ├── review/ # 提审/审核
|
||
│ ├── delivery/ # 投放配置/DPLink
|
||
│ ├── assets/ # 模板/Logo/展示/促销/脚本资产
|
||
│ └── system/ # 用户/角色/客户/配置/任务日志
|
||
├── shared/ # api client、hooks、utils、权限、i18n
|
||
└── styles/ # tailwind.css、semi-theme.css、tokens.css
|
||
```
|
||
|
||
- 路由:`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 集成(必须按此顺序,否则静默失效)
|
||
|
||
```css
|
||
/* apps/web/src/styles/index.css —— 顺序 = 层叠优先级(M0 Spike 最终确认) */
|
||
@layer theme, base, semi, components, utilities;
|
||
|
||
@import "tailwindcss";
|
||
@import "@douyinfe/semi-ui/dist/css/semi.min.css" layer(semi);
|
||
@import "./semi-theme.css" layer(semi); /* Semi CSS 变量覆盖 */
|
||
@import "./tokens.css"; /* 品牌 token(@theme) */
|
||
```
|
||
|
||
**硬性约定**:
|
||
1. `@import` 顺序即层叠顺序:Tailwind base → Semi → 主题 token → 页面样式;页面级样式不得写未分层的 `* { margin:0 }`。
|
||
2. 品牌色/圆角/字号只允许在 `tokens.css` 的 `@theme` 与 `semi-theme.css` 的 `--semi-color-*` 定义,页面禁止硬编码色值(CI 用脚本校验)。
|
||
3. 不使用 `.semi-*` 内部类做覆盖;只通过 Semi 语义 token 与 `ConfigProvider`。
|
||
4. 组件统一从 `@lingxi/ui` 导出(薄封装 Semi),页面不直接 import Semi 的内部组件,便于后续换肤/升级。
|
||
5. **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 分层
|
||
|
||
```
|
||
apps/api/src/
|
||
├── server.ts # 启动、优雅退出
|
||
├── app.ts # Express 装配(middleware 顺序)
|
||
├── routes/ # 路由声明(薄)
|
||
├── controllers/ # 参数校验 + 调 service
|
||
├── services/ # 业务编排(事务边界)
|
||
├── repositories/ # Prisma 查询封装(可测试)
|
||
├── middlewares/ # auth、scope、rateLimit、error、requestId
|
||
├── events/ # SSE、任务事件发布
|
||
└── jobs/ # 入队(只投递,不执行)
|
||
```
|
||
|
||
- **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 中间件顺序(固定)
|
||
|
||
```
|
||
requestId → pino-http → helmet → cors 白名单 → compression(跳过 SSE)
|
||
→ body parser(json 2mb / urlencoded)→ cookie parser
|
||
→ 全局限流(Redis)→ 路由
|
||
→ 404 → error handler(统一信封)
|
||
```
|
||
|
||
- `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
|
||
|
||
```ts
|
||
export interface StorageAdapter {
|
||
put(key: string, body: Buffer | Readable, opts: { contentType: string; cacheControl?: string }): Promise<{ key: string; size: number; md5: string }>;
|
||
get(key: string): Promise<Readable>;
|
||
delete(key: string): Promise<void>;
|
||
exists(key: string): Promise<boolean>;
|
||
signDownload(key: string, ttlSeconds: number): Promise<string>;
|
||
copy(srcKey: string, dstKey: string): Promise<void>;
|
||
}
|
||
```
|
||
|
||
| 驱动 | 使用场景 | 实现 |
|
||
|---|---|---|
|
||
| `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 适配器接口
|
||
|
||
```ts
|
||
export interface PlatformAdapter {
|
||
code: 'dewu' | 'vipshop';
|
||
capabilities: { read: boolean; write: boolean; submit: boolean; dplink: boolean };
|
||
login(ctx: AccountContext): Promise<LoginResult>;
|
||
searchProducts(q: SearchQuery): Promise<ProductBrief[]>;
|
||
getProduct(spuId: string): Promise<ProductSnapshot>;
|
||
listRawAssets(spuId: string): Promise<RawAssetRef[]>;
|
||
listMaterials(q: MaterialQuery): Promise<MaterialPage>;
|
||
submitReview(input: SubmitInput): Promise<SubmitResult>;
|
||
queryReview(input: ReviewQuery): Promise<ReviewPage>;
|
||
buildDeliveryLink(input: DeliveryLinkInput): Promise<DeliveryLink>;
|
||
}
|
||
```
|
||
|
||
- 得物:读链路走签名 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 接口
|
||
|
||
```ts
|
||
export interface AiImageProvider {
|
||
generate(input: ImageGenInput): Promise<ImageGenResult>; // 返回 storageKey/成本/耗时
|
||
checkText(input: TextCheckInput): Promise<TextCheckResult>; // VLM 逐字校验
|
||
}
|
||
```
|
||
|
||
- 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)
|
||
|
||
```
|
||
pnpm install --frozen-lockfile
|
||
pnpm lint
|
||
pnpm typecheck
|
||
pnpm test
|
||
pnpm db:deploy(测试库)
|
||
pnpm test:integration
|
||
pnpm test:golden
|
||
pnpm build
|
||
pnpm test:e2e(可选,需浏览器)
|
||
```
|
||
|
||
- 服务:`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 首次启动
|
||
|
||
```bash
|
||
corepack enable
|
||
pnpm install
|
||
cp .env.example .env # 填 PG 密码/JWT 密钥/凭据密钥
|
||
pnpm db:create # 连接 postgres 库,创建 lingxi(不存在才建)
|
||
pnpm db:migrate # prisma migrate dev
|
||
pnpm db:seed # 管理员/角色/平台/规格
|
||
pnpm playwright:install # 安装 Chromium(平台 Worker 需要)
|
||
pnpm dev # web:5173 + api:8300
|
||
# 另开终端:
|
||
pnpm dev:workers # 4 类 Worker
|
||
```
|
||
|
||
### 15.3 `.env.example`
|
||
|
||
```dotenv
|
||
NODE_ENV=development
|
||
PORT=8300
|
||
WEB_ORIGIN=http://localhost:5173
|
||
|
||
DATABASE_URL=postgresql://postgres:CHANGE_ME@127.0.0.1:5432/lingxi?schema=public
|
||
REDIS_URL=redis://127.0.0.1:6379/0
|
||
REDIS_PREFIX=lingxi
|
||
|
||
JWT_ACCESS_SECRET=CHANGE_ME_32+_CHARS
|
||
JWT_REFRESH_SECRET=CHANGE_ME_32+_CHARS
|
||
PLATFORM_CRED_KEY=base64:CHANGE_ME_32_BYTES
|
||
|
||
STORAGE_DRIVER=local
|
||
STORAGE_LOCAL_ROOT=./var/uploads
|
||
# STORAGE_DRIVER=s3
|
||
# S3_ENDPOINT=http://127.0.0.1:9000
|
||
# S3_REGION=us-east-1
|
||
# S3_BUCKET=lingxi
|
||
# S3_ACCESS_KEY_ID=
|
||
# S3_SECRET_ACCESS_KEY=
|
||
# STORAGE_DRIVER=oss
|
||
# OSS_REGION=oss-cn-shanghai
|
||
# OSS_BUCKET=
|
||
# OSS_ACCESS_KEY_ID=
|
||
# OSS_ACCESS_KEY_SECRET=
|
||
|
||
PLAYWRIGHT_BROWSERS_PATH=./.cache/ms-playwright
|
||
AI_PROVIDER=ark
|
||
ARK_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
|
||
ARK_API_KEY=
|
||
LOG_LEVEL=info
|
||
```
|
||
|
||
### 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` 一致):
|
||
|
||
```yaml
|
||
# pnpm-workspace.yaml
|
||
packages:
|
||
- apps/*
|
||
- packages/*
|
||
allowBuilds:
|
||
bcrypt: true
|
||
sharp: true
|
||
onnxruntime-node: true
|
||
msgpackr-extract: true
|
||
```
|
||
|
||
> 若 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(生产/联调)
|
||
|
||
```yaml
|
||
services:
|
||
web: { image: lingxi/web, ports: ["80:80", "443:443"] }
|
||
api: { image: lingxi/api, env_file: .env }
|
||
worker-image: { image: lingxi/worker-image, env_file: .env }
|
||
worker-platform: { image: lingxi/worker-platform, env_file: .env }
|
||
worker-ai: { image: lingxi/worker-ai, env_file: .env }
|
||
worker-export: { image: lingxi/worker-export, env_file: .env }
|
||
# PG/Redis 使用外部实例(已有/托管),不写入 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 人日**;总量 383~569 → **391~581**;首期 280~414 → **288~426** |
|
||
| 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)** | 同 |
|
||
|
||
> 评估文档已同步更新为 **391~581 人日 / 首期 288~426 人日**(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` 与根配置
|
||
|
||
```ini
|
||
engine-strict=true
|
||
auto-install-peers=true
|
||
strict-peer-dependencies=false
|
||
resolution-mode=highest
|
||
```
|
||
|
||
> `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。*
|