docs(specs): add AGENTS.md and engineering guidelines
项目尚无统一工程规范,AI 助手与研发各自发挥导致返工风险。建立 总纲 + 细则两层结构:AGENTS.md 只写硬约束与索引(14 条红线、技术栈 基线、仓库结构、命令契约),细则拆为 10 份文档覆盖 Git、代码风格、 格式化、API 与路由、数据库、测试与 CI、Review、日志、密钥、资产命名。 影响面:仅文档。约束力从本轮起对新建代码生效;与现状冲突处以文档为准。 回滚方式:git revert 本提交。
This commit is contained in:
1 parent
d062d79465
commit
00ae12435e
12 files changed
+1562
No files matched your search
@@ -0,0 +1,215 @@
|
|||||||
|
# AGENTS.md · 灵犀项目整体规范
|
||||||
|
|
||||||
|
> 本文件是本仓库的**唯一总纲**,面向所有研发与 AI 编程助手(Claude Code / Cursor / Codex / Copilot 等)。
|
||||||
|
> 只写**硬约束与索引**,细则一律见 [`docs/工程规范/`](docs/工程规范/README.md),本文不重复展开。
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 适用对象 | 前端 / 后端 / 测试 / 运维 / AI 工程 / 产品(技术边界) |
|
||||||
|
| 适用产物 | 代码、配置、脚本、迁移、文档、Prompts |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 事实基线 | PRD `V1.6`、技术架构 `V0.4` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0 效力与冲突处理
|
||||||
|
|
||||||
|
1. **优先级(高 → 低)**:现行 PRD → 技术架构 → 工程规范 → 本文示例 → 个人偏好。
|
||||||
|
2. 规范与实现冲突时:**先改文档,再改代码**;禁止"代码先行、文档后补"。
|
||||||
|
3. 本文与细则冲突:以细则为准;细则未覆盖:以技术架构为准。
|
||||||
|
4. 文档未定义且影响面大于单个模块的决策,**必须提 ADR**(`docs/adr/ADR-xxx-标题.md`)评审后再写代码。
|
||||||
|
5. 无法确认的业务口径:标 `【待确认 Q-xx】` 并停下,**禁止猜测**(Q 编号见 PRD 第 10 章)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 项目边界(一句话)
|
||||||
|
|
||||||
|
灵犀 = 信息流广告素材生产平台:**行业包 → 要素卡 → 脚本 → 素材(图片确定性合成 / 视频逐镜生成)→ 快筛 → 导出即归档**。
|
||||||
|
内核不分行业,行业差异用「行业包 + schema」表达,**禁止写成代码分支**。
|
||||||
|
|
||||||
|
**本期不做**(越界即评审驳回):通用视频剪辑器、投放执行、效果归因、客户自助登录、计费结算、图片线 AI 生图(M5 前)、通用低代码/审批引擎、模型训练微调。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 文档基线(唯一事实来源)
|
||||||
|
|
||||||
|
| 文档 | 路径 | 版本 | 作用 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| PRD | `docs/PRD/灵犀-信息流广告素材一站式生产平台PRD.md` | V1.6 | 需求、业务规则(R-01~R-30)唯一来源 |
|
||||||
|
| 技术架构 | `docs/技术架构/灵犀_信息流素材生产平台_技术架构_V0.1.md` | V0.4 | 技术选型、模块、数据、接口唯一来源 |
|
||||||
|
| 立项建议 | `docs/立项建议/灵犀_信息流素材生产平台_立项建议书_V0.1.md` | V0.1 | 论证背景,**不约束实现** |
|
||||||
|
| 工程规范 | `docs/工程规范/` | — | 落地细则(本文件的下游) |
|
||||||
|
| 参考项目 | `项目参考/`、`sucaigongfang` | — | 仅查阅,**不入库、禁止复制业务代码** |
|
||||||
|
|
||||||
|
- 新增技术选型 / 架构变更,必须先更新技术架构并评审,**禁止在代码里"先试再说"**。
|
||||||
|
- 引用业务规则时写编号(如 `// R-04 客户数据隔离`),便于反查。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 硬红线(违反即打回,不接受"下个版本再改")
|
||||||
|
|
||||||
|
1. **包管理只用 pnpm 10**,Node 锁定 `24.15.0`(`.nvmrc`);禁 npm / yarn / bun,禁 `npm link`,内部包用 `workspace:*`。
|
||||||
|
2. **全栈 TypeScript strict**;禁 `any`(用 `unknown` + 收窄),禁用 `as` 绕过类型检查。
|
||||||
|
3. **UI 只用 `@douyinfe/semi-ui-19`**;图标只用 Remix Icon 且只能从 `packages/ui/icons.ts` 按语义名导入。禁混装其他组件库/图标库,禁业务代码内联 SVG(品牌 Logo 除外)。
|
||||||
|
4. **模块化单体**:模块间只走 Service 接口或领域事件;**禁止跨模块 `prisma.*`**;`identity` / `client` 是被依赖方,不得反向依赖。
|
||||||
|
5. 任何数据访问必须携带 **`ScopeContext`**,查询必须带 `customer_id`;**禁止"先查全量再过滤"**。
|
||||||
|
6. **出网 AI 调用唯一入口 `platform.ai`**;业务模块不得直连任何模型 SDK。
|
||||||
|
7. **图片线只做确定性合成**(PSD 套版 + 抠图 + 合成);**禁止**在图片线接入文生图 / 图生图(PRD R-29)。
|
||||||
|
8. **迁移一律 expand → migrate → contract**;禁止同一次发布内"删字段 + 改代码"。
|
||||||
|
9. **密钥 / Token 不入库、不写日志、不进前端 bundle**,只从环境变量读。
|
||||||
|
10. **审计表 append-only(仅 INSERT)**;导出全留痕(PRD R-05)。
|
||||||
|
11. **阈值 / 上限类数字只存系统配置一处**(PRD R-26);禁前后端硬编码。
|
||||||
|
12. **低质量产出必须显式打标**(`low_quality` + 原因码),不得按成功静默交付(PRD R-30)。
|
||||||
|
13. 任务提交必须带 **`Idempotency-Key`**;产物一律短时效签名 URL,**不塞进 JSON**。
|
||||||
|
14. **被引用的资产只能停用、不能删除**(PRD R-02);已入库资产修改须**新版本 + 重新审核**(R-03)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 技术栈基线(版本以技术架构 §2.10 为准)
|
||||||
|
|
||||||
|
| 层 | 选型 |
|
||||||
|
| --- | --- |
|
||||||
|
| 运行时 | Node.js `24.15.0` · pnpm `10.x` · TypeScript `5.7+` |
|
||||||
|
| 前端 | React `19` · Vite `6` · React Router `7` · TanStack Query `5` · Zustand `5` · Tiptap · dnd-kit · ECharts `5` · HLS.js(P2) |
|
||||||
|
| UI | Semi Design `2.x`(`@douyinfe/semi-ui-19`)· Remix Icon `4.x` |
|
||||||
|
| 后端 | NestJS `11` + Fastify · Prisma `6` · Zod(静态 DTO)+ Ajv(动态 schema) |
|
||||||
|
| 数据 | PostgreSQL `18` · Redis `7.4` + BullMQ `5` · MinIO(S3 协议) |
|
||||||
|
| 媒体 | ffmpeg `7` · sharp `0.33+` · `@napi-rs/canvas` · `ag-psd` · ONNX 抠图 |
|
||||||
|
| 质量 | Vitest · Testcontainers · Playwright · k6 · oasdiff |
|
||||||
|
| 观测 | Pino · OpenTelemetry |
|
||||||
|
|
||||||
|
> 版本升级单独 PR + 修订记录,**禁止顺手升级**。新增运行时依赖需在 PR 说明必要性(影响面 + 替代方案)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 仓库结构(目标态;当前仅 `docs/`)
|
||||||
|
|
||||||
|
```
|
||||||
|
apps/api NestJS HTTP + SSE + AI 回调
|
||||||
|
apps/worker 同一镜像,按 QUEUE 环境变量启动
|
||||||
|
apps/web React SPA
|
||||||
|
packages/shared 领域事件、错误码、权限点常量、Zod schema、ULID、命名工具
|
||||||
|
packages/form-engine 受控 schema 表单渲染器(14 类字段白名单)
|
||||||
|
packages/render PSD 解析 / 抠图 / 合成 / 编码
|
||||||
|
packages/ai-sdk Provider 适配器、路由、限流、记账
|
||||||
|
packages/ui Semi 二次封装 + Remix 图标统一出口 + locale
|
||||||
|
prisma/ schema.prisma 与 migrations/
|
||||||
|
infra/ compose、Nginx、监控、备份
|
||||||
|
docs/ PRD / 技术架构 / 立项建议 / 工程规范
|
||||||
|
```
|
||||||
|
|
||||||
|
- **依赖方向**:`apps → packages`;`packages` 单向;`shared` 不依赖任何包;**禁止循环依赖**。
|
||||||
|
- 工作区包 scope 统一 `@lingxi/*`(`@lingxi/shared`、`@lingxi/ui`、`@lingxi/render`…),保持私有、不发布公网。
|
||||||
|
- 后端 8 模块:`identity` `client` `catalog` `industry` `script` `media` `delivery` `platform`。
|
||||||
|
- 新目录 / 新包必须先评审,**禁止在 `apps/web` 内私建"共享"目录**充当包。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 命令契约(建仓后必须与下表一致)
|
||||||
|
|
||||||
|
| 场景 | 命令 |
|
||||||
|
| --- | --- |
|
||||||
|
| 依赖启动 | `pnpm compose:up` / `pnpm compose:down` |
|
||||||
|
| 开发 | `pnpm dev` |
|
||||||
|
| 构建 | `pnpm build` |
|
||||||
|
| 质量 | `pnpm lint` · `pnpm lint:fix` · `pnpm format` · `pnpm format:check` · `pnpm typecheck` |
|
||||||
|
| 测试 | `pnpm test` · `pnpm test:int` · `pnpm test:e2e` |
|
||||||
|
| 数据库 | `pnpm db:migrate` · `pnpm db:reset`(禁止 `docker compose down -v`) |
|
||||||
|
| 契约 | `pnpm openapi:diff` |
|
||||||
|
|
||||||
|
> 文档、脚本、CI 中出现的命令必须与上表一致,禁止自造别名。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 变更流程(提测 / 合并前必须全绿)
|
||||||
|
|
||||||
|
1. 从 `main` 拉短分支 → 小步提交(Conventional Commits)。
|
||||||
|
2. 本地门禁:`pnpm lint && pnpm typecheck && pnpm test`。
|
||||||
|
3. PR 描述必含:**背景 / 改动 / 影响面 / 回滚方式 / 验证证据**;迁移、权限、AI、导出类变更须**双人 Review**。
|
||||||
|
4. CI 全绿 + Review 通过 → **Squash Merge**;禁止 force push `main`。
|
||||||
|
5. 发布:staging 验证 → 生产灰度;**迁移先行、代码后行**。
|
||||||
|
|
||||||
|
细则:[Git 规范](docs/工程规范/01-Git规范.md) · [Code Review 与 PR](docs/工程规范/07-CodeReview与PR规范.md) · [测试与 CI](docs/工程规范/06-测试与CI规范.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8 文档规范(摘要)
|
||||||
|
|
||||||
|
- 文档与代码**同 PR** 更新;只改文档也要走 PR。
|
||||||
|
- 新业务规则先进 PRD 并编号(`R-xx`),再进代码。
|
||||||
|
- 文档版本 `V<主>.<次>`:主版本 = 业务口径不兼容;次版本 = 增量补充。
|
||||||
|
- 索引三处必须同步:根 `README.md`、`docs/工程规范/README.md`、本文件 §9。
|
||||||
|
- 命名沿用 `灵犀_<主题>_<类型>_V<版本>.md`;**新建文档**必须遵循,历史文件保持原名并登记。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9 细则索引
|
||||||
|
|
||||||
|
| 主题 | 文档 | 一句话 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Git / 分支 / 提交 | [01-Git规范](docs/工程规范/01-Git规范.md) | 主干 + 短分支,Conventional Commits,Squash 合并 |
|
||||||
|
| 代码风格 | [02-代码风格](docs/工程规范/02-代码风格.md) | 命名、类型、错误处理、异步、React 约定 |
|
||||||
|
| 格式化与 Lint | [03-格式化与Lint](docs/工程规范/03-格式化与Lint.md) | Prettier + ESLint 9 flat,钩子与 CI 门禁 |
|
||||||
|
| API 与路由 | [04-API与路由规范](docs/工程规范/04-API与路由规范.md) | REST /api/v1、错误码、鉴权链、任务协议 |
|
||||||
|
| 数据库 | [05-数据库规范](docs/工程规范/05-数据库规范.md) | ULID、customer_id、游标分页、expand/contract |
|
||||||
|
| 测试与 CI | [06-测试与CI规范](docs/工程规范/06-测试与CI规范.md) | 分层测试、权限三态、覆盖率只升不降 |
|
||||||
|
| Code Review | [07-CodeReview与PR规范](docs/工程规范/07-CodeReview与PR规范.md) | PR 模板、双人场景、DoD、合并后观察 |
|
||||||
|
| 日志与可观测 | [08-日志与可观测性规范](docs/工程规范/08-日志与可观测性规范.md) | Pino 字段、trace_id、审计与日志分离、告警 |
|
||||||
|
| 环境变量与密钥 | [09-环境变量与密钥规范](docs/工程规范/09-环境变量与密钥规范.md) | `.env.example` 入库、启动校验、轮换与泄露处置 |
|
||||||
|
| 资产命名与图层 | [10-资产命名与PSD图层规范](docs/工程规范/10-资产命名与PSD图层规范.md) | 命名占位符、PSD 图层前缀、规格矩阵、低质量标记 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10 禁止自行决定(须产品 / 技术负责人确认)
|
||||||
|
|
||||||
|
- 新增第三方运行时依赖、新增外部 AI 供应商、改动 `ai_routes` 路由表。
|
||||||
|
- 字段类型白名单之外的新字段类型;权限点新增 / 删除 / 改名。
|
||||||
|
- 数据保留期、AI 预算阈值、爆量判定阈值。
|
||||||
|
- 公开分享 / 免登录审核链接开关(PRD R-28)。
|
||||||
|
- 跨客户资产复用授权(R-02 之外的显式授权流程)。
|
||||||
|
- 任何会让客户数据跨客户可见的实现方式。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11 AI 助手协作约定
|
||||||
|
|
||||||
|
1. 读上下文顺序:本文件 → 相关细则 → 技术架构对应章节 → PRD 对应章节,**禁止凭记忆写版本号 / 包名 / 表名 / 路由**。
|
||||||
|
2. 产出代码必须能过 `pnpm lint`、`pnpm typecheck`、`pnpm test`;不得新增未声明依赖。
|
||||||
|
3. 禁止修改已合并的 `prisma/migrations/`;禁止 `--no-verify` 提交。
|
||||||
|
4. 涉及业务口径不确定时,输出 `【待确认 Q-xx】` 并说明影响面,**不要编造**。
|
||||||
|
5. 改动跨 >3 个模块或触及 §10 清单时,先给出方案与影响面,等确认后再动手。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12 待建设规范(建议,尚未落地)
|
||||||
|
|
||||||
|
当前已覆盖 Git / 代码 / 格式 / 接口 / 数据库 / 测试 / Review / 日志 / 密钥 / 资产命名。以下规范建议在对应里程碑前补齐,**补齐前不得以"没有规范"为由自行发挥**:
|
||||||
|
|
||||||
|
| 规范 | 为什么需要 | 建议时点 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Prompt 工程规范** | 提示词模板是最容易被低估的资产;需约定模板结构、变量槽、版本与评测集 | M1 前 |
|
||||||
|
| **AI 成本与预算规范** | 预占 / 结算 / 账本 / 闸门已定,但预算口径与对账流程未文档化 | M2 前 |
|
||||||
|
| **安全与合规专项** | 广告法极限词、肖像权授权、AI 生成内容标识(R-09 形式仍待确认) | M1 前 |
|
||||||
|
| **运维 Runbook** | 部署、备份恢复、故障处理、告警处置(技术架构待办已列) | T1 前 |
|
||||||
|
| **前端交互与 UX 规范** | 快筛快捷键、空态 / 加载态 / 错误态、表格与表单交互统一 | M1 中 |
|
||||||
|
| **性能预算规范** | 首屏 ≤ 2s、接口 P95 ≤ 500ms 需拆到包体积 / 请求数 / 渲染预算 | M1 后 |
|
||||||
|
| **ADR 目录** | `docs/adr/` 尚未创建;首次架构决策时建立并纳入索引 | 首次 ADR 时 |
|
||||||
|
| **文档写作规范** | 术语统一、图表规范、评审流程 | M1 前 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附:待同步事项(2026-11-02)
|
||||||
|
|
||||||
|
- [x] 根 `README.md` 已与 PRD V1.6 对齐(三主线 + 一底座、文档索引、命令契约)。
|
||||||
|
- [ ] `docs/技术架构/`、`docs/立项建议/`、PRD V1.6、`docs/工程规范/` 尚未纳入 git,应合并为一个 `docs:` 提交入库。
|
||||||
|
- [ ] PRD 文件名 `灵犀-信息流广告素材一站式生产平台PRD.md` 不符合 §8 命名;因已被外部引用,暂保留原名,重命名时同步更新全部引用。
|
||||||
|
- [ ] 技术架构文档**文件名**为 `_V0.1.md` 但**内部版本**为 V0.4,正文修订记录中 V0.4 / V0.3 顺序倒置,建议下一版修正。
|
||||||
|
|
||||||
|
## 附:待确认口径(工程侧发现,需产品 / 架构确认)
|
||||||
|
|
||||||
|
| 编号 | 待确认项 | 本文现行约定 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `C-01` | JSON body 字段命名:架构文档示例为 camelCase,而 query 用 `next_cursor` 等 snake_case | body 用 camelCase,query 用 snake_case(见 [04](docs/工程规范/04-API与路由规范.md) §2) |
|
||||||
|
| `C-02` | 列表响应是否统一信封 | 列表 `{ items, next_cursor }`,单资源不套 `data` |
|
||||||
|
| `C-03` | 工作区包 scope 命名 | 暂定 `@lingxi/*` |
|
||||||
|
| `C-04` | AI 生成内容标识的具体形式与位置(PRD Q-06 / R-09) | 角标 + MP4 元数据(技术架构 §7.4),形式待法务确认 |
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# 01 · Git 规范
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 依据 | 技术架构 §4.6(CI/CD)、§10.1(仓库结构) |
|
||||||
|
| 适用范围 | 全仓库(代码、文档、配置、迁移) |
|
||||||
|
| 强制级别 | 强制 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 分支模型
|
||||||
|
|
||||||
|
采用 **trunk-based(主干 + 短生命周期分支)**,不建 `develop`、不建长期 `release` 分支。
|
||||||
|
|
||||||
|
| 类型 | 命名 | 用途 | 生命周期 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 主干 | `main` | 始终可发布 | 永久 |
|
||||||
|
| 功能 | `feat/<scope>-<简述>` | 新功能 | ≤ 5 天,超过须拆分 |
|
||||||
|
| 修复 | `fix/<scope>-<简述>` | 缺陷修复 | ≤ 2 天 |
|
||||||
|
| 文档 | `docs/<简述>` | 仅文档 | ≤ 1 天 |
|
||||||
|
| 重构 | `refactor/<scope>-<简述>` | 行为不变的结构调整 | ≤ 3 天 |
|
||||||
|
| 其他 | `chore/` `test/` `perf/` `build/` `ci/` | 对应类型 | ≤ 2 天 |
|
||||||
|
|
||||||
|
规则:
|
||||||
|
|
||||||
|
- `GIT-01` 分支名全小写 kebab-case,≤ 50 字符,**不含中文**;`scope` 用模块或包名(`api` / `web` / `worker` / `media` / `db` / `ci` / `docs`)。
|
||||||
|
- `GIT-02` 分支必须从最新 `main` 拉出,合并前 rebase 到 `main` 最新。
|
||||||
|
- `GIT-03` 禁止直接 push `main`;分支保护开启:必须 PR + CI 绿 + ≥1 批准。
|
||||||
|
- `GIT-04` 合并后立即删除远端分支。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 提交信息(Conventional Commits 1.0)
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
|
||||||
|
<body:为什么改 + 影响面,中文>
|
||||||
|
|
||||||
|
<footer:BREAKING CHANGE / Refs: R-xx / Closes #12>
|
||||||
|
```
|
||||||
|
|
||||||
|
| `type` | 用途 |
|
||||||
|
| --- | --- |
|
||||||
|
| `feat` | 新功能 |
|
||||||
|
| `fix` | 缺陷修复 |
|
||||||
|
| `docs` | 文档 |
|
||||||
|
| `style` | 仅格式(不影响逻辑) |
|
||||||
|
| `refactor` | 重构(不改行为) |
|
||||||
|
| `perf` | 性能 |
|
||||||
|
| `test` | 测试 |
|
||||||
|
| `build` | 构建 / 依赖 |
|
||||||
|
| `ci` | CI / 脚本 |
|
||||||
|
| `chore` | 杂项 |
|
||||||
|
| `revert` | 回滚 |
|
||||||
|
|
||||||
|
规则:
|
||||||
|
|
||||||
|
- `GIT-05` `subject` 用英文祈使句,≤ 72 字符,**不加句号**;中文放正文。
|
||||||
|
- `GIT-06` `scope` 必填,取值见 `GIT-01`。
|
||||||
|
- `GIT-07` 正文说明「**为什么**」与「**影响面 / 回滚方式**」;纯重命名类可仅一行 subject。
|
||||||
|
- `GIT-08` 破坏性变更:`type` 后加 `!` 且 footer 写 `BREAKING CHANGE: <说明与迁移方式>`。
|
||||||
|
- `GIT-09` 关联业务规则写 `Refs: R-04`;关联 issue 写 `Closes #123`。
|
||||||
|
- `GIT-10` 一个提交只做一件事;**禁止**在功能提交里顺带格式化无关文件。
|
||||||
|
- `GIT-11` 禁止 `WIP`、`fix bug`、`update` 等无信息量信息;禁止 `--no-verify` 绕过钩子。
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(media): 图片批次支持按规格矩阵展开任务项
|
||||||
|
|
||||||
|
批量生产原先按「模板 × 商品图」展开,导致同素材多规格重复建素材,
|
||||||
|
违反 PRD R-25「素材为主对象、规格为属性」。改为展开到 task_item,
|
||||||
|
规格作为属性写入 material_specs。
|
||||||
|
|
||||||
|
影响面:production_tasks 新增 spec_key 列(expand 迁移,兼容旧数据)。
|
||||||
|
回滚方式:关闭 FEATURE_MATRIX_SPEC 开关,旧路径仍可用。
|
||||||
|
Refs: R-25
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 PR 与合并
|
||||||
|
|
||||||
|
- `GIT-12` PR 标题遵循提交规范(Squash 后即主干提交信息)。
|
||||||
|
- `GIT-13` 合并方式统一 **Squash Merge**,保持 `main` 线性;禁止 Merge commit。
|
||||||
|
- `GIT-14` 单个 PR 变更 ≤ 400 行(含测试,不含 lock 文件与生成代码);超出须拆分并在描述说明依赖关系。
|
||||||
|
- `GIT-15` 禁止 force push `main` / 已有人 Review 的公共分支;个人分支允许 `--force-with-lease`。
|
||||||
|
- `GIT-16` PR 描述模板与双人 Review 场景见 [07-CodeReview与PR规范](07-CodeReview与PR规范.md)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 Tag 与发布
|
||||||
|
|
||||||
|
| 项 | 规则 |
|
||||||
|
| --- | --- |
|
||||||
|
| 版本号 | 语义化 `v<主>.<次>.<补>` |
|
||||||
|
| 打 Tag 时机 | 生产发布前,由发布负责人在 `main` 打 annotated tag |
|
||||||
|
| 里程碑 Tag | `m0` / `m1` / `m2`…(对应 PRD 分期),仅标记不进版本链 |
|
||||||
|
| 发布记录 | 每个 Tag 必须写变更摘要(可复用 PR 标题集合) |
|
||||||
|
| 回滚 | 回滚用新 Tag(`v1.2.1` 回滚 `v1.2.0` 内容),**禁止删除已发布 Tag** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 大文件与二进制
|
||||||
|
|
||||||
|
- `GIT-17` 禁止入库 > 10 MB 的二进制(PSD 模板、成片、视频、字体包),一律走对象存储;确需入库(如开源字体)用 **Git LFS** 并在 PR 说明。
|
||||||
|
- `GIT-18` 禁止入库:`.env*`、密钥、证书私钥、构建产物、`node_modules/`、日志、`项目参考/`。
|
||||||
|
- `GIT-19` `pnpm-lock.yaml` 必须提交且与 `package.json` 同步;**禁止**手工编辑 lock 文件。
|
||||||
|
- `GIT-20` 误提交敏感信息:立即轮换密钥 → 用 `git filter-repo` 清理历史 → 全员重新 clone → 记录复盘。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 检查清单(提交前 / 合并前)
|
||||||
|
|
||||||
|
- [ ] 分支名符合 `GIT-01`,从最新 `main` 拉出
|
||||||
|
- [ ] 提交信息符合 Conventional Commits,含 `scope`
|
||||||
|
- [ ] 提交粒度单一,无无关文件
|
||||||
|
- [ ] 无 `.env` / 密钥 / 大文件
|
||||||
|
- [ ] PR 描述含背景 / 影响面 / 回滚方式 / 验证证据
|
||||||
|
- [ ] CI 全绿,Review 通过,Squash 合并
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
# 02 · 代码风格
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 依据 | 技术架构 §2.2 / §2.3 / §10.1 |
|
||||||
|
| 适用范围 | `apps/` `packages/` `prisma/` `infra/` 全部 TypeScript / JavaScript |
|
||||||
|
| 强制级别 | 强制(标注「建议」的除外) |
|
||||||
|
|
||||||
|
> 格式问题交给 Prettier(见 [03](03-格式化与Lint.md)),本文只约定**格式化管不到**的部分。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 命名
|
||||||
|
|
||||||
|
| 对象 | 规则 | 示例 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 目录 / 文件 | kebab-case | `ai-routes/`、`script-task.service.ts` |
|
||||||
|
| React 组件文件 | PascalCase.tsx | `ScriptCard.tsx` |
|
||||||
|
| 变量 / 函数 | camelCase | `nextCursor`、`resolveScope()` |
|
||||||
|
| 类型 / 接口 / 类 | PascalCase | `MediaTask`、`StorageProvider` |
|
||||||
|
| 常量(模块级不可变) | UPPER_SNAKE | `MAX_UPLOAD_MB` |
|
||||||
|
| 布尔 | `is/has/can/should` 前缀 | `isArchived`、`hasPermission` |
|
||||||
|
| 事件回调 | 属性 `onXxx`,处理器 `handleXxx` | `onSelect` / `handleSelect` |
|
||||||
|
| 异步动作 | 动词开头,返回 Promise | `createBatch()` |
|
||||||
|
| 数据库表 / 列 | snake_case(Prisma `@@map` / `@map`) | `media_tasks`、`customer_id` |
|
||||||
|
| 权限点常量 | `UPPER_SNAKE`,与库内 code 一致 | `SCRIPT_APPROVE` |
|
||||||
|
|
||||||
|
- `CS-01` 禁止拼音命名(`shangpin`);业务术语用 PRD 术语表英文(`briefCard`、`videoDirection`、`shot`)。
|
||||||
|
- `CS-02` 单字母变量只允许循环下标 `i/j` 与泛型 `T/K/V`。
|
||||||
|
- `CS-03` 缩写词按单词大小写:`s3Key`、`httpClient`、`ulid`,不写 `S3Key` / `HttpClient` 混排。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 类型
|
||||||
|
|
||||||
|
- `CS-04` `strict: true`,禁止 `any`、隐式 `any`、`@ts-ignore`;确需忽略用 `@ts-expect-error` 并写原因。
|
||||||
|
- `CS-05` 禁止用 `as` 做类型断言绕过检查;只允许「收窄断言」(`as const`、`as unknown as X` 需注释理由)。
|
||||||
|
- `CS-06` 对外导出的函数 / 方法**必须显式标注返回类型**,内部函数可推断。
|
||||||
|
- `CS-07` 对象契约用 `interface`,联合 / 工具 / 映射类型用 `type`;二者不混用于同一实体。
|
||||||
|
- `CS-08` 业务枚举用 `as const` 对象 + 派生联合类型,**禁止 TS `enum`**;库内枚举由字典表提供(Prisma enum 仅用于永不扩展的封闭集合,需迁移评审说明)。
|
||||||
|
- `CS-09` 泛型参数 ≥ 2 个或含义不清时必须命名(`<TInput, TOutput>`),禁止全用 `T`。
|
||||||
|
- `CS-10` 禁止在业务代码中出现 `object`、`Function`、`{}` 作为类型;用具体形状或 `Record<string, unknown>`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 函数与结构
|
||||||
|
|
||||||
|
- `CS-11` 函数单一职责,建议 ≤ 50 行;超过则拆分。
|
||||||
|
- `CS-12` 参数 > 3 个必须用对象参数;可选参数不用位置占位。
|
||||||
|
- `CS-13` 嵌套 ≤ 3 层,用**早返回**替代 `else` 链。
|
||||||
|
- `CS-14` 禁止魔法值:数字 / 字符串常量抽到 `constants` 或读取系统配置(PRD R-26)。
|
||||||
|
- `CS-15` 单文件 ≤ 400 行;超出按职责拆分(Controller / Service / Repository 各自成文件)。
|
||||||
|
- `CS-16` 禁止循环依赖;跨包引用只从包 `index.ts` 出口导入。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 错误处理
|
||||||
|
|
||||||
|
- `CS-17` 统一抛 `AppError(code, message, details?)`,`code` 取错误码表(见 [04](04-API与路由规范.md) §4);**禁止 `throw new Error('...')` 直接面向用户**。
|
||||||
|
- `CS-18` 禁止 `catch` 后空吞;要么处理、要么包装后重抛,并保留 `cause`。
|
||||||
|
- `CS-19` `async` 函数必须有 `await` 或显式 `void`;禁止 floating promise。
|
||||||
|
- `CS-20` 外部调用(AI / 存储 / ffmpeg)必须设置超时 + 分类重试;**内容违规类错误不重试**。
|
||||||
|
- `CS-21` 任务(BullMQ)处理器必须**幂等**:以 `taskItemId` 为幂等键,重复执行结果一致。
|
||||||
|
- `CS-22` 前端错误:路由级 ErrorBoundary + 请求层统一 toast,禁止页面内零散 `alert`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 注释与文档
|
||||||
|
|
||||||
|
- `CS-23` 注释解释**为什么**,不复述代码;业务规则必须引用 PRD 编号:`// R-04 查询必须带 customer_id`。
|
||||||
|
- `CS-24` 导出 API 写 JSDoc(用途、参数、错误码、副作用);内部实现按需。
|
||||||
|
- `CS-25` TODO 格式:`// TODO(@owner, 2026-12-01): 原因与后续`;禁止无主 TODO。
|
||||||
|
- `CS-26` 禁止提交注释掉的整段代码;历史查 git。
|
||||||
|
- `CS-27` 不写「本次改动」「新增」这类时间性注释,注释描述当下状态。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 导入顺序
|
||||||
|
|
||||||
|
按组排序,组间空行,组内按字母序(由 ESLint `simple-import-sort` 自动修复):
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Node 内置(node:fs)
|
||||||
|
2. 第三方(@nestjs/*、zod)
|
||||||
|
3. workspace 包(@lingxi/shared、@lingxi/ui)
|
||||||
|
4. 应用内绝对路径(~/ 或 @/)
|
||||||
|
5. 相对路径(./、../)
|
||||||
|
6. 样式 / 资源(*.css、*.svg?raw)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `CS-28` 禁止 `import * as X` 整包导入(图标包、UI 包尤甚,会破坏 tree-shaking)。
|
||||||
|
- `CS-29` 图标只从 `packages/ui/icons.ts` 导入语义名(`import { IconScript } from '@lingxi/ui/icons'`),禁直接引 `remixicon` 具体路径。
|
||||||
|
- `CS-30` 禁 `export *` 聚合无审阅的公共 API;`index.ts` 显式列出导出。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 React / 前端
|
||||||
|
|
||||||
|
- `CS-31` 只用函数组件 + Hooks;禁 class 组件、禁 `React.FC`(返回类型写 `ReactNode` / `JSX.Element`)。
|
||||||
|
- `CS-32` 服务端数据只用 TanStack Query(`useQuery` / `useMutation`);**禁止**在 `useEffect` 里手写 fetch + 自建缓存。
|
||||||
|
- `CS-33` 全局状态只放跨页共享且非服务端数据(Zustand);页面内状态用 `useState` / `useReducer`,禁止为省 props 上全局。
|
||||||
|
- `CS-34` 列表 `key` 用业务 ID,禁止 `index`。
|
||||||
|
- `CS-35` 组件文件导出一个主组件;子组件同文件时用内部函数,跨文件复用必须提为独立组件。
|
||||||
|
- `CS-36` 样式只用 Semi design token(`cssVar`)/ CSS Modules;**禁止散落十六进制色值**、禁止行内 style 做主题。
|
||||||
|
- `CS-37` 文案集中在 `packages/ui/locale`(zh-CN),组件内不写死中文用户可见文案(日志与注释除外)。
|
||||||
|
- `CS-38` 表单走 `packages/form-engine` 的 schema 渲染;**禁止**在业务页面手写动态字段映射(会绕过白名单校验)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8 后端
|
||||||
|
|
||||||
|
- `CS-39` 分层职责:Controller 只解析入参 / 组装响应;Service 承载业务规则与状态机;Repository 是唯一访问 Prisma 的层。
|
||||||
|
- `CS-40` Repository 方法**必须接收 `ScopeContext`** 作为首个参数,禁止在 Repository 内部从全局取当前用户。
|
||||||
|
- `CS-41` 模块间只通过 Service 接口或领域事件通信;禁止跨模块注入 `PrismaService` 直接查表。
|
||||||
|
- `CS-42` 状态迁移集中在 Service 的状态机函数内,禁止 Controller 直接改 `status` 字段。
|
||||||
|
- `CS-43` 事务边界在 Service;事务内**禁止**外部 IO(AI 调用、HTTP、对象存储)。
|
||||||
|
- `CS-44` 队列任务只做「可重试的纯执行」,业务校验与成本预占在 API 层完成。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9 检查清单
|
||||||
|
|
||||||
|
- [ ] 命名符合 §1,无拼音、无缩写混排
|
||||||
|
- [ ] 无 `any` / `@ts-ignore` / 滥用 `as`
|
||||||
|
- [ ] 函数 ≤ 50 行、文件 ≤ 400 行、嵌套 ≤ 3 层
|
||||||
|
- [ ] 错误统一 `AppError`,无空 catch
|
||||||
|
- [ ] 队列任务幂等,外部调用有超时
|
||||||
|
- [ ] 图标 / UI 从统一出口导入
|
||||||
|
- [ ] 业务规则注释带 R-xx
|
||||||
|
- [ ] 无魔数、无硬编码阈值
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
# 03 · 格式化与 Lint
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 依据 | 技术架构 §2.2(前端)、§10.1(工程约定)、§4.6(CI/CD) |
|
||||||
|
| 适用范围 | 全仓库可执行文件 |
|
||||||
|
| 强制级别 | 强制(CI 门禁,不允许「先合并后修」) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 一句话原则
|
||||||
|
|
||||||
|
**格式交给工具,评审只看逻辑。** 任何格式争论都应由配置解决,禁止在 Review 中讨论空格与换行。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 Prettier(唯一格式化器)
|
||||||
|
|
||||||
|
| 配置项 | 值 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `printWidth` | `100` | 与技术架构示例行宽一致 |
|
||||||
|
| `tabWidth` | `2` | |
|
||||||
|
| `useTabs` | `false` | |
|
||||||
|
| `semi` | `true` | |
|
||||||
|
| `singleQuote` | `true` | |
|
||||||
|
| `trailingComma` | `all` | |
|
||||||
|
| `arrowParens` | `always` | |
|
||||||
|
| `endOfLine` | `lf` | 跨平台一致 |
|
||||||
|
| `plugins` | `prettier-plugin-tailwindcss`(仅 `apps/web`) | 若引入 Tailwind;未引入则不加 |
|
||||||
|
|
||||||
|
规则:
|
||||||
|
|
||||||
|
- `FMT-01` 只允许一个 Prettier 版本、一个根配置;子包**不得**覆盖。
|
||||||
|
- `FMT-02` 禁止在文件中写 `prettier-ignore`,除非是生成代码或大段 markdown 表格,并注明原因。
|
||||||
|
- `FMT-03` CI 执行 `pnpm format:check`,失败即阻断。
|
||||||
|
- `FMT-04` 禁止把格式化产生的无关改动混进功能 PR(会产生冲突与 Review 噪音)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 ESLint(ESLint 9 flat config)
|
||||||
|
|
||||||
|
| 项 | 约定 |
|
||||||
|
| --- | --- |
|
||||||
|
| 配置位置 | 根 `eslint.config.mjs`,按 `apps/*`、`packages/*` 分层 export |
|
||||||
|
| 基础 | `@eslint/js` recommended + `typescript-eslint` 的 `strictTypeChecked` + `stylisticTypeChecked` |
|
||||||
|
| 前端附加 | `eslint-plugin-react-hooks`、`eslint-plugin-react-refresh`、`jsx-a11y` |
|
||||||
|
| 导入排序 | `eslint-plugin-simple-import-sort` |
|
||||||
|
| 命名检查 | `@typescript-eslint/naming-convention`(对应 [02](02-代码风格.md) §1) |
|
||||||
|
|
||||||
|
### 3.1 必须为 `error` 的规则
|
||||||
|
|
||||||
|
| 规则 | 目的 |
|
||||||
|
| --- | --- |
|
||||||
|
| `@typescript-eslint/no-explicit-any` | 禁 `any` |
|
||||||
|
| `@typescript-eslint/no-floating-promises` | 禁未处理 Promise |
|
||||||
|
| `@typescript-eslint/no-misused-promises` | 禁 async 传入非异步回调位 |
|
||||||
|
| `@typescript-eslint/consistent-type-imports` | 类型导入用 `import type` |
|
||||||
|
| `@typescript-eslint/no-unnecessary-condition` | 消除冗余判断(strict 下有效) |
|
||||||
|
| `no-restricted-imports` | **UI / 图标白名单**(见 §3.2) |
|
||||||
|
| `no-console` | 业务代码禁用,`scripts/**` 与 `worker` 的 logger 除外 |
|
||||||
|
| `import/no-cycle` | 禁循环依赖 |
|
||||||
|
| `react-hooks/rules-of-hooks` / `exhaustive-deps` | Hooks 正确性 |
|
||||||
|
| `jsx-a11y/*` 关键项 | 可访问性底线 |
|
||||||
|
|
||||||
|
### 3.2 白名单(`no-restricted-imports` 强制)
|
||||||
|
|
||||||
|
**禁止导入**(含子路径):
|
||||||
|
|
||||||
|
```
|
||||||
|
@douyinfe/semi-ui ← 只能用 @douyinfe/semi-ui-19
|
||||||
|
antd @ant-design/* @arco-design/*
|
||||||
|
@douyinfe/semi-icons @ant-design/icons react-icons lucide-react @heroicons/*
|
||||||
|
element-plus naive-ui @mui/* @chakra-ui/*
|
||||||
|
```
|
||||||
|
|
||||||
|
**规则**:
|
||||||
|
|
||||||
|
- `FMT-05` 图标只能 `import { IconXxx } from '@lingxi/ui/icons'`;禁止 `import * as Icons`、禁止直接引 `remixicon/icons/...`。
|
||||||
|
- `FMT-06` SVG 内联仅允许品牌 Logo(`packages/ui/brand/`),其他一律走图标出口。
|
||||||
|
- `FMT-07` 颜色字面量(`#rrggbb`、`rgb(`)在 `apps/web` 内禁用,除非在 `packages/ui/theme/` 的 token 定义文件。
|
||||||
|
|
||||||
|
### 3.3 允许 `warn` 的规则
|
||||||
|
|
||||||
|
`@typescript-eslint/no-non-null-assertion`(须注释证明非空)、`complexity`(> 15 警告)、`max-lines-per-function`(> 50 警告)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 提交钩子(本地)
|
||||||
|
|
||||||
|
| 钩子 | 工具 | 行为 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `pre-commit` | husky + lint-staged | 仅对暂存文件跑 `eslint --fix` + `prettier --write` |
|
||||||
|
| `commit-msg` | commitlint | 校验 Conventional Commits(见 [01](01-Git规范.md) §2) |
|
||||||
|
| `pre-push` | husky | 跑 `pnpm typecheck`(本地拦截类型错误,避免浪费 CI) |
|
||||||
|
|
||||||
|
- `FMT-08` `lint-staged` 只处理暂存文件,**禁止**在钩子里跑全量 lint / 全量测试(太慢会诱发 `--no-verify`)。
|
||||||
|
- `FMT-09` 禁止 `git commit --no-verify`;如确因钩子故障,须在 PR 描述说明并附 CI 结果。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 CI 门禁(阻断项)
|
||||||
|
|
||||||
|
| 阶段 | 命令 | 失败后果 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 安装 | `pnpm install --frozen-lockfile` | 阻断(lock 不一致直接失败) |
|
||||||
|
| 格式 | `pnpm format:check` | 阻断 |
|
||||||
|
| Lint | `pnpm lint --max-warnings=0` | 阻断 |
|
||||||
|
| 类型 | `pnpm typecheck` | 阻断 |
|
||||||
|
| 单测 | `pnpm test -- --coverage` | 阻断(覆盖率不得低于基线) |
|
||||||
|
| 迁移 | `prisma migrate diff` 校验 schema 与迁移一致 | 阻断 |
|
||||||
|
| 构建 | `pnpm build` | 阻断 |
|
||||||
|
| 安全 | 依赖审计 + 镜像高危漏洞 | 阻断(高危 0) |
|
||||||
|
|
||||||
|
- `FMT-10` CI 与本地必须使用**同一份配置**,禁止 CI 单独放宽规则。
|
||||||
|
- `FMT-11` 覆盖率基线只升不降;下调须在 PR 说明理由并由技术负责人批准。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 例外与豁免
|
||||||
|
|
||||||
|
| 场景 | 做法 |
|
||||||
|
| --- | --- |
|
||||||
|
| 生成代码(Prisma Client、OpenAPI 类型) | 在 `eslint.config.mjs` 中按路径 `ignores`,禁止在生成文件内加注释 |
|
||||||
|
| 第三方类型缺陷 | 文件级 `/* eslint-disable rule -- 原因 */`,必须写原因并在 PR 说明 |
|
||||||
|
| 单行特殊语法 | 行内 `// eslint-disable-next-line rule -- 原因` |
|
||||||
|
| 全文件 disable | **禁止**;确需则改为局部 disable 或调整规则配置 |
|
||||||
|
|
||||||
|
- `FMT-12` 任何 `eslint-disable` 都必须写明原因与到期处理方式(升级依赖 / 提 issue)。
|
||||||
|
- `FMT-13` 规则调整(新增 / 降级 / 关闭)必须单独 PR,说明影响文件数与整改计划。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 检查清单
|
||||||
|
|
||||||
|
- [ ] `pnpm format:check` 通过,无格式化噪音混入
|
||||||
|
- [ ] `pnpm lint --max-warnings=0` 通过
|
||||||
|
- [ ] `pnpm typecheck` 通过
|
||||||
|
- [ ] 无新增 `eslint-disable`;已有的都有原因
|
||||||
|
- [ ] 未引入禁用包 / 禁用图标库
|
||||||
|
- [ ] 无散落色值,样式走 token
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
# 04 · API 与路由规范
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 依据 | 技术架构 §6(接口设计)、§3.2(模块划分) |
|
||||||
|
| 适用范围 | `apps/api` 全部 HTTP / SSE 接口、`apps/web` 全部前端路由 |
|
||||||
|
| 强制级别 | 强制 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 URL 与版本
|
||||||
|
|
||||||
|
- `API-01` 服务端统一前缀 `/api/v1`;版本在 **URL** 中,不使用 header 版本。
|
||||||
|
- `API-02` 资源名用**复数 kebab-case**:`/brief-cards`、`/media-tasks`、`/industry-packs`。
|
||||||
|
- `API-03` 嵌套 ≤ 2 层:`/clients/:clientId/scripts`;更深的归属用 query 过滤。
|
||||||
|
- `API-04` 动作优先用子资源表达(`POST /scripts/:id/approve` → 可接受),**禁止**在 URL 用动词拼接业务流(`/doGenerate`)。
|
||||||
|
- `API-05` 破坏性变更(删字段 / 改语义 / 改状态码)必须升 `/api/v2`;新增可选字段不升版本。
|
||||||
|
- `API-06` 对外契约由 OpenAPI 生成,`pnpm openapi:diff` 检出破坏性变更即阻断(见 [06](06-测试与CI规范.md))。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 请求约定
|
||||||
|
|
||||||
|
| 项 | 约定 |
|
||||||
|
| --- | --- |
|
||||||
|
| 认证 | `Authorization: Bearer <access_token>`;refresh 走独立端点 |
|
||||||
|
| 幂等 | 写接口支持 `Idempotency-Key`(24h 内同 key 返回首次结果);**任务提交必带** |
|
||||||
|
| 内容类型 | `application/json; charset=utf-8`;上传走预签名直传,不经 API 中转 |
|
||||||
|
| 时间 | ISO 8601 带时区(`2026-11-02T14:30:00+08:00`),**存储统一 UTC** |
|
||||||
|
| ID | ULID 字符串,26 字符;请求体里的 ID 一律字符串 |
|
||||||
|
| 字段命名 | JSON body 用 **camelCase**;query 用 **snake_case**(`cursor` / `limit` / `sort` / `next_cursor`,与架构文档示例一致) |
|
||||||
|
| 未知字段 | 严格模式拒绝(Zod `.strict()`),避免静默丢弃 |
|
||||||
|
|
||||||
|
- `API-07` 所有入参用 Zod schema 校验,Controller 不写手写校验;动态 schema(要素卡)用 Ajv + 白名单。
|
||||||
|
- `API-08` 分页:`?cursor=<opaque>&limit=50`,返回 `next_cursor`;`limit` 默认 50、上限 200。
|
||||||
|
- `API-09` 排序:`?sort=-created_at`(`-` 为降序);仅允许白名单字段,非法字段 400。
|
||||||
|
- `API-10` 过滤:显式白名单参数;禁止把任意 query 透传给 Prisma。
|
||||||
|
- `API-11` 列表**禁止返回大字段**(JSONB 富文本、base64、完整分镜);产物只返回签名 URL。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 响应约定
|
||||||
|
|
||||||
|
**成功**
|
||||||
|
|
||||||
|
- 单个资源:直接返回对象(不套 `data` 信封)。
|
||||||
|
- 列表:`{ "items": [...], "next_cursor": "..." }`;无下一页时 `next_cursor` 为 `null`。
|
||||||
|
- 创建:`201` + 资源;异步任务提交:`202` + `{ taskId, estimatedItems, estimatedCost, estimatedSeconds }`。
|
||||||
|
- 删除:`204` 无 body。
|
||||||
|
|
||||||
|
**失败**(统一结构,禁止其他形状)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "code": 40302, "message": "无权访问该客户数据", "details": {}, "trace_id": "01JA..." }
|
||||||
|
```
|
||||||
|
|
||||||
|
- `API-12` `message` 面向用户、中文、不含内部实现细节;`details` 放字段级错误;`trace_id` 必填。
|
||||||
|
- `API-13` 禁止把堆栈、SQL、密钥、供应商原始报文写进响应。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 错误码表
|
||||||
|
|
||||||
|
| 段 | 含义 | 常用码 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `400xx` | 参数校验 | `40001` 参数缺失 / 格式错误 |
|
||||||
|
| `401xx` | 认证 | `40101` 未登录、`40102` token 过期、`40103` 签名失败 |
|
||||||
|
| `403xx` | 授权 | `40301` 无权限点、`40302` 数据范围外、`40303` 缺特殊权限、`40304` 违反职责互斥 |
|
||||||
|
| `404xx` | 不存在 | `40401` 资源不存在 / 已删除 |
|
||||||
|
| `409xx` | 冲突 | `40901` 版本冲突、`40902` 重复提交、`40903` 状态不允许 |
|
||||||
|
| `422xx` | 业务规则 | `42201` 业务校验失败、`42202` 合规拦截(禁忌词 / 极限词)、`42203` 成本超预算 |
|
||||||
|
| `429xx` | 限流 | `42901` 请求过频、`42902` 并发任务超限 |
|
||||||
|
| `500xx` | 系统 | `50001` 内部错误、`50002` 依赖不可用、`50003` 超时 |
|
||||||
|
|
||||||
|
- `API-14` 新增错误码必须集中登记在 `packages/shared` 的错误码常量,禁止在业务代码里写裸数字。
|
||||||
|
- `API-15` 授权类错误**一律 403 且不区分「不存在」与「无权限」的原因**(避免探测他人数据;日志里保留真实原因)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 鉴权与权限校验链
|
||||||
|
|
||||||
|
顺序固定,不得跳步:
|
||||||
|
|
||||||
|
```
|
||||||
|
JwtGuard → PermissionGuard(权限点)→ SpecialPermissionGuard(特殊权限)
|
||||||
|
→ ScopeGuard(数据范围)→ 业务校验 → Repository(ScopeContext) → 审计埋点
|
||||||
|
```
|
||||||
|
|
||||||
|
- `API-16` 权限点由代码注册(常量),角色是权限点组合(数据化),见技术架构 D-07。
|
||||||
|
- `API-17` 特殊权限(导出 / AI 配置 / 审计查看 / 行业包发布 / 跨客户复用授权)必须单独授予,不得打包进普通角色。
|
||||||
|
- `API-18` 数据范围解析失败**一律 403**,禁止降级为「全量可见」。
|
||||||
|
- `API-19` `/public/review/*`、`/public/share/*` 是**唯一免 JWT** 路由组,必须:HMAC 签名 + 有效期 + 范围(只读 / 可改稿)+ 限流 + 访问留痕;不得在其中暴露任何其他资源。
|
||||||
|
- `API-20` 每个端点必须有 3 类权限测试用例(有权限 / 无权限 / 范围外),见 [06](06-测试与CI规范.md)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 异步任务协议(所有耗时操作统一)
|
||||||
|
|
||||||
|
| 步骤 | 约定 |
|
||||||
|
| --- | --- |
|
||||||
|
| 提交 | `POST /xxx-tasks`,带 `Idempotency-Key`,返回 `202` + `{ taskId, estimatedItems, estimatedCost, estimatedSeconds }` |
|
||||||
|
| 订阅 | `GET /xxx-tasks/{id}/events`(SSE),事件 `item` / `progress` / `done` / `error`,支持 `Last-Event-ID` 续推 |
|
||||||
|
| 兜底 | `GET /xxx-tasks/{id}` 轮询(前端 SSE 失败 2 次后自动降级) |
|
||||||
|
| 取消 | `POST /xxx-tasks/{id}/cancel`;仅未开始项取消,已开始项等待结束 |
|
||||||
|
| 重试 | `POST /xxx-tasks/{id}/retry`;**只重跑失败项**,成功项不得重算成本 |
|
||||||
|
| 产物 | 一律签名 URL(5–15 分钟),随 `item` / `done` 事件下发 |
|
||||||
|
|
||||||
|
- `API-21` 长耗时操作**禁止**放在同步请求里(> 2s 一律异步)。
|
||||||
|
- `API-22` `estimatedCost` 必填;为 0 也要返回(前端展示"预估成本"是 PRD R-06 硬要求)。
|
||||||
|
- `API-23` 任务终态写入后事件必须可重放(存事件表或 Redis Stream),断线可恢复。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 上传与下载
|
||||||
|
|
||||||
|
| 场景 | 约定 |
|
||||||
|
| --- | --- |
|
||||||
|
| 上传 | 预签名 PUT 直传对象存储;前端先算 `sha256` 走秒传;> 100 MB 用分片(`listMultipart`) |
|
||||||
|
| 校验 | 上传完成后服务端 `headObject` 校验大小 / 类型;不一致则任务失败并清理 |
|
||||||
|
| 下载 | `302` 到签名 URL(5–15 分钟),支持 `Range`(视频预览断点续播) |
|
||||||
|
| 导出包 | 服务端打包 → `exports` 桶 → 签名 URL,产物保留 3 天 |
|
||||||
|
| 限制 | 单文件上限读环境变量 `MAX_UPLOAD_MB`,**禁止**代码里写死 |
|
||||||
|
|
||||||
|
- `API-24` 禁止 API 进程中转文件流(内存与带宽不可控)。
|
||||||
|
- `API-25` 签名 URL 不得记录到日志(含 query 签名参数)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8 前端路由
|
||||||
|
|
||||||
|
- `API-26` 路由常量集中 `apps/web/src/routes.ts`,禁止散落字符串路径。
|
||||||
|
- `API-27` 路径反映「行业 → 客户」归属:`/clients/:clientId/scripts`、`/clients/:clientId/media/batches/:batchId`。
|
||||||
|
- `API-28` 统一布局路由:登录后进入 `AppLayout`(侧边导航 + 面包屑),未登录访问自动跳 `/login` 并带回跳地址。
|
||||||
|
- `API-29` 权限守卫组件 `<RequirePermission code="SCRIPT_APPROVE">`;无权限渲染 403 页,**不隐藏路由**(避免"菜单有、点进去白屏")。
|
||||||
|
- `API-30` 页面级懒加载(`React.lazy` + `Suspense`),首屏包体积纳入性能预算。
|
||||||
|
- `API-31` URL 是唯一状态源:筛选 / 分页 / 选中态可分享的必须进 query(`useSearchParams`),禁止只存 Zustand。
|
||||||
|
- `API-32` 404 / 403 / 500 三个页面必须存在,且 500 页展示 `trace_id` 供排查。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9 契约与兼容
|
||||||
|
|
||||||
|
- `API-33` OpenAPI 由 NestJS 装饰器生成,提交到仓库,CI 用 `oasdiff` 检测破坏性变更。
|
||||||
|
- `API-34` 新增字段必须可选且前端有默认值;删除字段先标记 `deprecated` 一个版本。
|
||||||
|
- `API-35` 前端不得依赖未在 OpenAPI 声明的字段。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10 检查清单
|
||||||
|
|
||||||
|
- [ ] URL 复数 kebab-case,版本 `/api/v1`,嵌套 ≤ 2 层
|
||||||
|
- [ ] 入参 Zod 校验,未知字段拒绝
|
||||||
|
- [ ] 列表走游标分页,不返回大字段
|
||||||
|
- [ ] 错误体 `{ code, message, details, trace_id }`,错误码取自常量
|
||||||
|
- [ ] 守卫链完整,特殊权限单独校验
|
||||||
|
- [ ] 任务提交带 `Idempotency-Key` 与预估成本
|
||||||
|
- [ ] 产物只给签名 URL,不打日志
|
||||||
|
- [ ] 前端路由常量集中,权限守卫 + 三张错误页齐全
|
||||||
|
- [ ] OpenAPI 已更新,`openapi:diff` 通过
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
# 05 · 数据库规范
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 依据 | 技术架构 §5(数据模型)、§8.3 / §8.4(ADR) |
|
||||||
|
| 适用范围 | `prisma/`、所有 SQL、数据迁移脚本 |
|
||||||
|
| 强制级别 | 强制 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 命名与主键
|
||||||
|
|
||||||
|
| 对象 | 规则 | 示例 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 表 | 复数 snake_case | `media_tasks`、`brief_card_index` |
|
||||||
|
| 列 | snake_case | `customer_id`、`created_at` |
|
||||||
|
| 主键 | `id`,**应用侧生成 ULID**,`char(26)` | `01JA8Z...` |
|
||||||
|
| 外键 | `<单数实体>_id` | `script_id`、`customer_id` |
|
||||||
|
| 布尔 | `is_` / `has_` 前缀 | `is_archived` |
|
||||||
|
| 时间 | `_at` 后缀,`timestamptz` | `reviewed_at` |
|
||||||
|
| 金额 / 成本 | `_cost`、`_amount`,`numeric(18,6)` | `unit_cost` |
|
||||||
|
| JSON | `_json` 或语义名(`params`、`snapshot`) | `output_params` |
|
||||||
|
| 索引 | `idx_<表>_<列>`;唯一 `uq_*`;部分 `idx_*_partial` | `idx_media_tasks_customer_created` |
|
||||||
|
|
||||||
|
- `DB-01` **禁止库内生成主键**(不用 `gen_random_uuid()` / `uuidv7()`);主键统一由应用生成 ULID,便于排序与分片。
|
||||||
|
- `DB-02` 每张业务表必须含 `created_at`、`updated_at`;需要软删的加 `deleted_at`。
|
||||||
|
- `DB-03` Prisma 模型用 PascalCase + `@@map` / `@map` 映射到 snake_case,**禁止**直接在库里用 camelCase 列名。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 客户隔离(最高优先级)
|
||||||
|
|
||||||
|
- `DB-04` 所有业务表 `customer_id NOT NULL` + 外键;同时冗余 `industry_id` 便于按行业聚合。
|
||||||
|
- `DB-05` Repository 方法必须接收 `ScopeContext` 并在 `where` 中注入 `customer_id`;**禁止**「先查全量再在内存过滤」(PRD R-04)。
|
||||||
|
- `DB-06` 唯一约束必须包含 `customer_id`(如客户内脚本编号唯一),跨客户唯一只允许编码类字段。
|
||||||
|
- `DB-07` 跨客户写操作(跨客户复用授权)必须走显式授权表并写审计,**禁止**在业务 SQL 里放宽 `customer_id`。
|
||||||
|
- `DB-08` P1 起启用 PG 行级安全(RLS)兜底;启用前必须在 staging 验证「范围外查询返回 0 行」。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 字段与类型
|
||||||
|
|
||||||
|
| 场景 | 规则 |
|
||||||
|
| --- | --- |
|
||||||
|
| 枚举 | 优先字典表 + 数据行;表内保存 `*_snapshot`(名称快照),避免字典变更影响历史 |
|
||||||
|
| Prisma enum | 仅用于**永不扩展**的封闭集合,且需迁移评审说明 |
|
||||||
|
| 动态字段 | JSONB + `schema_version`;可检索字段物化到 `brief_card_index`(D-03) |
|
||||||
|
| 大文本 | `text`;禁止 `varchar(255)` 装富文本 |
|
||||||
|
| 时长 | 毫秒整数(`duration_ms`),禁止小数秒 |
|
||||||
|
| 比例 | `numeric(6,4)`;禁止 `float` 存比率 |
|
||||||
|
| 标签 | 关联表或 `text[]`(仅查询简单场景);**禁止** JSONB 存关联 ID(PRD R-25) |
|
||||||
|
| 文件 | 只存对象存储 key(`storage_key`)+ 元数据,**禁止存二进制或外链签名 URL** |
|
||||||
|
| 密码 / 密钥 | 只存哈希 / 密文;AI 供应商密钥用 AES-256-GCM 加密列 |
|
||||||
|
|
||||||
|
- `DB-09` JSONB 上默认不建 GIN 索引;仅在确有按字段查询且 `EXPLAIN` 证明需要时建。
|
||||||
|
- `DB-10` 禁止 `SELECT *` 返回含大 JSONB 的列表;查询必须显式 `select`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 索引与查询
|
||||||
|
|
||||||
|
- `DB-11` 所有外键列建索引。
|
||||||
|
- `DB-12` 列表页统一复合索引:`(customer_id, created_at DESC, id DESC)`;游标分页基于 `(created_at, id)`(技术架构 §5.7)。
|
||||||
|
- `DB-13` 唯一约束配合软删用**部分索引**:`WHERE deleted_at IS NULL`。
|
||||||
|
- `DB-14` 禁止无条件的 `COUNT(*)` 大表统计;计数走预聚合表或 `EXPLAIN` 验证代价。
|
||||||
|
- `DB-15` 禁止 OFFSET 深翻页;分页一律游标。
|
||||||
|
- `DB-16` 禁止 N+1:列表查询用 `include`/`select` 显式取关联,或批量 `IN` 查询。
|
||||||
|
- `DB-17` 批量写入单批 ≤ 1000 行,超出分片;`createMany` 需注意不触发单条钩子。
|
||||||
|
- `DB-18` 慢查询(> 200ms)必须记录到日志并带 `trace_id`,纳入每周巡检。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 迁移(Prisma Migrate)
|
||||||
|
|
||||||
|
- `DB-19` 迁移文件命名 `YYYYMMDDHHmmss_<动词>_<对象>`,如 `20261102153000_add_material_specs`。
|
||||||
|
- `DB-20` **禁止修改已合并的迁移文件**;修正用新迁移。
|
||||||
|
- `DB-21` 采用 **expand → migrate → contract** 三步;**禁止**同一次发布内「删字段 + 改代码」(技术架构 §4.6)。
|
||||||
|
- `DB-22` 生产迁移必须幂等、可重入;大表加列用可空列或带默认值的在线加列,禁止锁表长事务。
|
||||||
|
- `DB-23` 迁移前必须备份,并在 staging 演练;回滚脚本随迁移一起提交(`down` 说明)。
|
||||||
|
- `DB-24` `prisma db push` 仅限本地探索,**禁止**用于任何共享环境。
|
||||||
|
- `DB-25` 迁移涉及数据回填时,回填脚本独立于 schema 迁移,可重复执行。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 事务与并发
|
||||||
|
|
||||||
|
- `DB-26` 事务边界在 Service 层;跨表写必须同事务。
|
||||||
|
- `DB-27` 事务内**禁止外部 IO**(AI 调用、HTTP、对象存储、ffmpeg)。
|
||||||
|
- `DB-28` 编号 / 序号类唯一值(脚本编号、批次号)必须用**原子取号**(`UPDATE ... RETURNING` 或序列表 + 行锁),禁止「先查后写」。
|
||||||
|
- `DB-29` 状态迁移用条件更新(`WHERE status = 'pending'`)避免并发覆盖;受影响行数为 0 视为冲突并返回 409。
|
||||||
|
- `DB-30` 避免长事务(> 5s);批量任务状态更新按批提交。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 删除与保留
|
||||||
|
|
||||||
|
| 数据 | 策略 |
|
||||||
|
| --- | --- |
|
||||||
|
| 业务实体 | 软删 `deleted_at`;被引用的资产**只能停用不能删除**(PRD R-02) |
|
||||||
|
| 审计 / 账本 / 任务事件 | **append-only**,禁止 UPDATE / DELETE |
|
||||||
|
| 任务临时产物 | `temp` 桶 30 天自动清理 |
|
||||||
|
| 导出包 | `exports` 桶 3 天 |
|
||||||
|
| 归档 | `archive` 桶 ≥ 12 个月 |
|
||||||
|
| 客户数据删除 | 只按 PRD 约定的保留期与流程执行,**禁止**开发者自行清理 |
|
||||||
|
|
||||||
|
- `DB-31` 硬删只允许:临时文件、失败任务产物、测试数据;且必须有清理任务与日志。
|
||||||
|
- `DB-32` 审计表权限:应用账号仅 `INSERT` + `SELECT`,无 `UPDATE` / `DELETE` 授权。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8 检查清单
|
||||||
|
|
||||||
|
- [ ] 表 / 列 snake_case,主键 ULID,`created_at` / `updated_at` 齐全
|
||||||
|
- [ ] 业务表有 `customer_id` + 外键 + 冗余 `industry_id`
|
||||||
|
- [ ] Repository 接收 `ScopeContext`,SQL 带客户维度
|
||||||
|
- [ ] 列表查询走复合索引 + 游标分页,无 `SELECT *`、无 N+1
|
||||||
|
- [ ] 迁移 expand/contract,未改历史迁移,staging 已演练
|
||||||
|
- [ ] 事务内无外部 IO;编号原子取号;状态迁移带条件
|
||||||
|
- [ ] 审计 append-only;临时产物有清理策略
|
||||||
|
- [ ] 新增 JSONB 字段有 `schema_version` 与检索策略
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# 06 · 测试与 CI 规范
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 依据 | 技术架构 §10.4(测试策略)、§4.6(CI/CD)、§4.1.1(本地环境) |
|
||||||
|
| 适用范围 | 全仓库 |
|
||||||
|
| 强制级别 | 强制 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 测试分层
|
||||||
|
|
||||||
|
| 层级 | 工具 | 范围 | 运行时机 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 单元 | Vitest | Service、工具函数、状态机、命名 / 表单引擎 | 每次提交(CI + 本地) |
|
||||||
|
| 集成 | Vitest + Testcontainers(PG 18 / Redis 7.4 / MinIO) | Repository、队列、迁移、事务 | 每次 PR(CI) |
|
||||||
|
| 契约 | OpenAPI + `oasdiff` | 接口兼容性 | 每次 PR |
|
||||||
|
| E2E | Playwright | 主链路(登录 → 生成 → 预览 → 勾选 → 导出) | 主干 + 发布前 |
|
||||||
|
| 性能 | k6 | 列表 / 任务提交 / SSE | 发布前(仅 staging) |
|
||||||
|
| 视觉回归 | 图片像素比对 | PSD 合成产物 | 改动 `packages/render` 时 |
|
||||||
|
|
||||||
|
- `TST-01` 单测与源码同目录,命名 `<name>.spec.ts`;集成 `*.int.spec.ts`;E2E 放 `e2e/`。
|
||||||
|
- `TST-02` 关键包(`shared` / `form-engine` / `render` / `ai-sdk`)行覆盖率 **≥ 70%**;整体基线只升不降。
|
||||||
|
- `TST-03` 禁止提交 `.skip` / `.only`;CI 检测到即失败。
|
||||||
|
- `TST-04` 禁止依赖执行顺序;每个测试自建数据、自清理(用独立 `customer_id` 前缀)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 必测清单(缺一项即 Review 不通过)
|
||||||
|
|
||||||
|
| 编号 | 场景 |
|
||||||
|
| --- | --- |
|
||||||
|
| `TST-05` | **权限三态**:每个端点至少「有权限 / 无权限 / 范围外」3 条用例 |
|
||||||
|
| `TST-06` | **客户隔离**:跨客户读取必须 403 或空集,且不得泄漏存在性 |
|
||||||
|
| `TST-07` | **状态机**:合法迁移 + 非法迁移(返回 409)各一条 |
|
||||||
|
| `TST-08` | **幂等**:同 `Idempotency-Key` 重复提交只产生一次副作用 |
|
||||||
|
| `TST-09` | **成本前置**:任务提交未带预估成本 / 超预算时被拒 |
|
||||||
|
| `TST-10` | **审计**:导出、权限变更、AI 配置变更均落审计 |
|
||||||
|
| `TST-11` | **降级路径**:抠图失败 → 原图 + `low_quality` 标记;AI 内容违规 → 不重试 |
|
||||||
|
| `TST-12` | **迁移**:升级后可回滚;expand 阶段旧代码仍可用 |
|
||||||
|
| `TST-13` | **任务断点**:SSE 断线后 `Last-Event-ID` 可续推 |
|
||||||
|
| `TST-14` | **命名规则**:非法字符、超长、同名重名各一条(PRD R-01) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 测试数据与环境
|
||||||
|
|
||||||
|
- `TST-15` 禁止连接生产库 / 生产对象存储;CI 用 Testcontainers 起同版本依赖。
|
||||||
|
- `TST-16` 测试数据用 factory 构造,禁止依赖线上导出数据。
|
||||||
|
- `TST-17` 测试内置时钟可注入(`clock` 包),禁止 `new Date()` 直接断言时间。
|
||||||
|
- `TST-18` AI 调用在测试中一律 mock Provider,**禁止**真实外网调用(防止费用与不稳定)。
|
||||||
|
- `TST-19` 涉及文件的测试用内存 / 临时目录,结束后清理。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 CI 流水线
|
||||||
|
|
||||||
|
```
|
||||||
|
install --frozen-lockfile
|
||||||
|
→ format:check → lint --max-warnings=0 → typecheck
|
||||||
|
→ unit test(coverage)→ build
|
||||||
|
→ integration(Testcontainers)→ openapi:diff
|
||||||
|
→ e2e(main / 发布分支)→ 镜像构建 + 漏洞扫描
|
||||||
|
```
|
||||||
|
|
||||||
|
| 阶段 | 阻断条件 |
|
||||||
|
| --- | --- |
|
||||||
|
| 安装 | lock 文件与 `package.json` 不一致 |
|
||||||
|
| 格式 / Lint | 任一错误或警告 |
|
||||||
|
| 类型 | 任一错误 |
|
||||||
|
| 单测 | 失败或覆盖率低于基线 |
|
||||||
|
| 集成 | 失败或超时(单测总时长 ≤ 10 分钟) |
|
||||||
|
| 契约 | `oasdiff` 检出破坏性变更但未升版本 |
|
||||||
|
| 构建 | 失败 |
|
||||||
|
| 安全 | 高危漏洞 > 0 |
|
||||||
|
|
||||||
|
- `TST-20` 流水线并行化按包拆分(Turborepo),但门禁结论必须在聚合 job 上判定。
|
||||||
|
- `TST-21` 禁止通过 `--passWithNoTests`、跳过集成测试等方式「修绿」;豁免须技术负责人批准并留 PR 记录。
|
||||||
|
- `TST-22` flaky 测试 24 小时内修复或移除并登记,**禁止**重试 3 次掩盖。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 特性开关与灰度
|
||||||
|
|
||||||
|
| 变量 | 用途 |
|
||||||
|
| --- | --- |
|
||||||
|
| `FEATURE_VIDEO` | 视频线开关(未完成不得外露) |
|
||||||
|
| 其他 `FEATURE_*` | 按里程碑临时开关,上线稳定后删除 |
|
||||||
|
|
||||||
|
- `TST-23` 未完成功能必须藏在开关后,**禁止**半成品直接可见。
|
||||||
|
- `TST-24` 开关必须有默认值、有 owner、有清理期限(写进 PR 或配置注释)。
|
||||||
|
- `TST-25` 开关组合不得导致数据不一致(关闭后已产生的数据仍可读)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 发布与回滚
|
||||||
|
|
||||||
|
```
|
||||||
|
staging 部署 → 冒烟(登录 / 生成 / 预览 / 导出)
|
||||||
|
→ 生产迁移(expand)→ 灰度 10% → 观察 30 分钟 → 全量 → 收缩(contract,下个版本)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `TST-26` 迁移先行、代码后行;收缩步骤必须在**确认无旧版本流量**后执行。
|
||||||
|
- `TST-27` 每次发布必须有回滚预案:代码回滚 + 数据回滚(或前向修复)分别说明。
|
||||||
|
- `TST-28` 发布后观察:错误率、P95、队列积压、AI 费用;异常 15 分钟内决策回滚。
|
||||||
|
- `TST-29` 发布记录写入 `docs/releases/`(版本、变更、影响面、回滚方式)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 检查清单
|
||||||
|
|
||||||
|
- [ ] 新增 / 修改逻辑有对应单测,关键包覆盖率达标
|
||||||
|
- [ ] 端点有权限三态用例,客户隔离用例存在
|
||||||
|
- [ ] 状态机非法迁移、幂等、成本前置已覆盖
|
||||||
|
- [ ] 集成测试用 Testcontainers,无外网依赖
|
||||||
|
- [ ] CI 全绿且未使用豁免
|
||||||
|
- [ ] 迁移可回滚,staging 已演练
|
||||||
|
- [ ] 未完成功能藏开关,开关有清理期限
|
||||||
|
- [ ] 发布记录与回滚预案已写
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# 07 · Code Review 与 PR 规范
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 依据 | 技术架构 §10.1(工程约定)、§4.6(CI/CD) |
|
||||||
|
| 适用范围 | 所有进入 `main` 的变更 |
|
||||||
|
| 强制级别 | 强制 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 PR 描述模板(缺项即打回)
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 背景
|
||||||
|
为什么要改(关联 PRD R-xx / 技术架构章节 / issue)
|
||||||
|
|
||||||
|
## 改动
|
||||||
|
做了什么(按文件或模块列 3–6 条,不贴大段 diff)
|
||||||
|
|
||||||
|
## 影响面
|
||||||
|
- 受影响模块 / 端点 / 表 / 队列
|
||||||
|
- 是否影响存量数据、是否需要回填
|
||||||
|
- 是否影响权限、审计、成本
|
||||||
|
|
||||||
|
## 回滚方式
|
||||||
|
代码回滚 / 开关关闭 / 数据回滚,写清具体操作
|
||||||
|
|
||||||
|
## 验证证据
|
||||||
|
- 本地命令与结果(lint / typecheck / test)
|
||||||
|
- 关键截图或录屏(UI)/ 接口示例(API)
|
||||||
|
- 迁移在 staging 的执行结果
|
||||||
|
```
|
||||||
|
|
||||||
|
- `PR-01` 标题遵循 Conventional Commits(Squash 后即主干提交)。
|
||||||
|
- `PR-02` 变更 ≤ 400 行(不含 lock / 生成代码);超出拆分为多个 PR 并在描述写依赖顺序。
|
||||||
|
- `PR-03` 禁止在功能 PR 中混入无关重构、格式化、依赖升级。
|
||||||
|
- `PR-04` Draft PR 可用于早期讨论,但**不得**在 CI 未绿时请求 Review。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 Review 分工
|
||||||
|
|
||||||
|
| 变更类型 | 要求 |
|
||||||
|
| --- | --- |
|
||||||
|
| 普通业务代码 | ≥ 1 名同模块 Owner |
|
||||||
|
| 数据库迁移 / 索引 / 数据回填 | **2 人**(含后端负责人) |
|
||||||
|
| 权限点 / 角色 / 数据范围 / RLS | **2 人**(含安全或技术负责人) |
|
||||||
|
| AI 路由 / 供应商 / 密钥 / 成本闸门 | **2 人**(含 AI 工程) |
|
||||||
|
| 导出 / 分享链接 / 对外公开路由 | **2 人**(含安全) |
|
||||||
|
| 发布配置 / CI / 基础设施 | **2 人**(含运维) |
|
||||||
|
| 仅文档 | 1 人(可为作者同组) |
|
||||||
|
|
||||||
|
- `PR-05` 作者不得自我批准;禁止「LGTM」式无内容批准。
|
||||||
|
- `PR-06` 首轮 Review 时效 24 小时内;作者负责推动,超过 48 小时升级到模块负责人。
|
||||||
|
- `PR-07` Review 意见分「必须改(blocking)/ 建议(non-blocking)」,作者逐条回复;未解决不得合并。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 Review 检查清单(按序检查)
|
||||||
|
|
||||||
|
**正确性**
|
||||||
|
|
||||||
|
- [ ] 逻辑与 PRD 规则一致,边界(空 / 超限 / 并发 / 重试)已处理
|
||||||
|
- [ ] 状态机迁移合法,非法路径返回明确错误
|
||||||
|
- [ ] 异步任务幂等,失败可重试且不重复计费
|
||||||
|
|
||||||
|
**数据与隔离**
|
||||||
|
|
||||||
|
- [ ] 所有查询带 `customer_id`,Repository 接收 `ScopeContext`
|
||||||
|
- [ ] 无跨模块直接 `prisma.*`
|
||||||
|
- [ ] 迁移 expand/contract,索引与查询匹配,无 `SELECT *` / N+1
|
||||||
|
|
||||||
|
**安全与合规**
|
||||||
|
|
||||||
|
- [ ] 守卫链完整,特殊权限单独校验
|
||||||
|
- [ ] 无密钥 / Token / 签名 URL 进日志或响应
|
||||||
|
- [ ] 导出 / 分享 / 公开路由有签名、有效期、限流、留痕
|
||||||
|
- [ ] 审计埋点齐全(谁、何时、对什么、改前改后)
|
||||||
|
|
||||||
|
**接口与前端**
|
||||||
|
|
||||||
|
- [ ] 入参 Zod 校验,错误码取自常量
|
||||||
|
- [ ] UI 只用 Semi + 统一图标出口,无散落色值
|
||||||
|
- [ ] 权限守卫 + 403/404 页;URL 承载可分享状态
|
||||||
|
|
||||||
|
**质量**
|
||||||
|
|
||||||
|
- [ ] 命名、类型、错误处理符合 [02](02-代码风格.md)
|
||||||
|
- [ ] 测试覆盖必测清单([06](06-测试与CI规范.md) §2)
|
||||||
|
- [ ] 无 `eslint-disable` 新增;无 TODO 无主
|
||||||
|
- [ ] 文档 / OpenAPI / 变更记录同步更新
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 DoD(合并即完成的定义)
|
||||||
|
|
||||||
|
同时满足才可合并:
|
||||||
|
|
||||||
|
1. CI 全绿(含契约与集成),无豁免。
|
||||||
|
2. Review 通过,blocking 意见全部解决。
|
||||||
|
3. 文档同步:PRD 规则编号、技术架构、OpenAPI、本目录细则(如涉及)。
|
||||||
|
4. 迁移可回滚,或在描述说明为何不可回滚与前向修复方案。
|
||||||
|
5. 可观测性就位:关键路径有日志、有指标、失败有告警(如适用)。
|
||||||
|
6. 影响面与回滚方式已写清,release 记录已补。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 合并与合并后
|
||||||
|
|
||||||
|
- `PR-08` 统一 **Squash Merge**,提交信息用 PR 标题 + 描述要点。
|
||||||
|
- `PR-09` 合并后删除分支;关注 CI 与错误率 15 分钟。
|
||||||
|
- `PR-10` 合并后发现缺陷:优先前向修复;影响面大则按回滚预案执行,并补一次复盘(写 `docs/releases/` 或 issue)。
|
||||||
|
- `PR-11` 禁止「合并后再补测试 / 再补文档」作为常态;确因紧急修复,须 24 小时内补齐并在 PR 记录追踪链接。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 反模式(Review 直接打回)
|
||||||
|
|
||||||
|
| 反模式 | 原因 |
|
||||||
|
| --- | --- |
|
||||||
|
| 巨型 PR(> 800 行) | 无法有效 Review,缺陷率高 |
|
||||||
|
| 「顺手」重构无关模块 | 混淆影响面,增加回滚难度 |
|
||||||
|
| 用 `any` / `@ts-ignore` 绕过类型 | 把问题推到运行时 |
|
||||||
|
| 在 Controller 写业务规则 | 破坏分层,无法复用与测试 |
|
||||||
|
| 直接改 `status` 字段 | 绕过状态机,产生非法状态 |
|
||||||
|
| 同步接口里跑长任务 | 违反异步任务协议,超时不可控 |
|
||||||
|
| 硬编码阈值 / 客户 ID | 违反 PRD R-26 与客户隔离 |
|
||||||
|
| 无回滚方案的迁移 | 生产事故无法收敛 |
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
# 08 · 日志与可观测性规范
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 依据 | 技术架构 §2.7(可观测性)、§9.7、§5.4.5(审计表) |
|
||||||
|
| 适用范围 | `apps/api`、`apps/worker`、`apps/web`(错误上报) |
|
||||||
|
| 强制级别 | 强制 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 日志(Pino,结构化 JSON)
|
||||||
|
|
||||||
|
**必带字段**(每条日志)
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `ts` | ISO 8601 UTC |
|
||||||
|
| `level` | `error` / `warn` / `info` / `debug` |
|
||||||
|
| `msg` | 稳定短句(可被聚合),变量放字段里 |
|
||||||
|
| `trace_id` | 贯穿请求链路;HTTP 入口生成,响应头 `x-trace-id` 回传 |
|
||||||
|
| `module` | 模块名(`media` / `script` / `identity`…) |
|
||||||
|
| `action` | 动作(`media.task.submit`、`script.generate`) |
|
||||||
|
|
||||||
|
**按需字段**:`user_id`、`customer_id`、`task_id`、`item_id`、`duration_ms`、`error.code`、`error.stack`(仅后端内部日志)、`retry_count`、`provider`、`model`。
|
||||||
|
|
||||||
|
- `LOG-01` 禁止 `console.log`;后端用注入的 logger,前端用统一 logger + 错误上报。
|
||||||
|
- `LOG-02` `msg` 用英文短句 + 常量,禁止拼业务变量(`"task 123 failed"`),便于聚合统计。
|
||||||
|
- `LOG-03` 禁止记录:密码、Token、密钥、Cookie、签名 URL、`Authorization` 头、完整手机号 / 身份证、客户素材原始内容。
|
||||||
|
- `LOG-04` 大对象(请求体 / 模型响应)必须先裁剪:≤ 2 KB,且标注 `truncated: true`。
|
||||||
|
- `LOG-05` AI 调用日志记录:供应商、模型、任务类型、耗时、token 数、费用、是否降级;**不记录 prompt 全文**(敏感客户信息),需要复现时用引用 ID。
|
||||||
|
- `LOG-06` 日志级别语义:`error` = 需人处理;`warn` = 已降级 / 重试;`info` = 业务里程碑与状态变更;`debug` = 仅开发环境默认关闭。
|
||||||
|
- `LOG-07` 生产默认 `info`,可按模块动态调级(不重启);禁止长期开 `debug`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 链路追踪(OpenTelemetry)
|
||||||
|
|
||||||
|
- `LOG-08` HTTP 入口创建 span,`trace_id` 写入响应头、错误体、日志三处,便于用户报错时定位。
|
||||||
|
- `LOG-09` 跨队列传递 `traceparent`(BullMQ job data 中携带),worker 侧继续同一 trace。
|
||||||
|
- `LOG-10` 外部调用(AI / 存储 / ffmpeg)必须建子 span,记录目标、耗时、结果状态。
|
||||||
|
- `LOG-11` 采样率:生产默认 10%,错误与慢请求 100%;采样配置走环境变量。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 审计(与日志严格分离)
|
||||||
|
|
||||||
|
| 维度 | 日志 | 审计 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 存储 | 日志系统(可轮转、可丢弃) | `audit_logs` 表(append-only) |
|
||||||
|
| 用途 | 排障 | 追责、合规、回溯「为什么这样」 |
|
||||||
|
| 内容 | 技术细节 | actor / action / target / before / after / ip / ua / trace_id |
|
||||||
|
| 修改 | 可删可改 | **仅 INSERT**,任何角色不可改(PRD 9.2) |
|
||||||
|
|
||||||
|
- `LOG-12` 必须审计的操作:登录 / 登出、权限点与角色变更、客户创建与状态变更、导出、分享链接创建与作废、AI 配置变更、行业包发布、字段变更与审批、资产删除 / 停用、跨客户复用授权。
|
||||||
|
- `LOG-13` 审计写入与业务写在同一事务内(或 outbox 保证最终一致),**禁止**业务成功但审计丢失。
|
||||||
|
- `LOG-14` 审计查询本身也要留痕(谁查了审计)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 指标(Prometheus 命名)
|
||||||
|
|
||||||
|
命名:`<domain>_<metric>_<unit>`,如 `media_task_finished_total`、`queue_depth`、`ai_cost_cny_total`。
|
||||||
|
|
||||||
|
| 类别 | 必含指标 |
|
||||||
|
| --- | --- |
|
||||||
|
| HTTP | 请求量、错误率、P50/P95 延迟(按路由模板,不按原始 URL) |
|
||||||
|
| 队列 | 各队列深度、等待时长、失败率、重试次数 |
|
||||||
|
| 业务 | 任务成功率、单条成本、图片有效率、抠图降级率、低质量产出率 |
|
||||||
|
| AI | 调用次数、失败 / 降级率、token 用量、费用(预算占比) |
|
||||||
|
| 存储 | 桶容量、上传失败率、签名 URL 下发量 |
|
||||||
|
| 依赖 | PG 连接数 / 慢查询、Redis 内存 / 命中率 |
|
||||||
|
|
||||||
|
- `LOG-15` 标签基数受控:禁止把 `user_id`、`customer_id`、`task_id` 作为指标标签(用日志 / trace 查)。
|
||||||
|
- `LOG-16` 派生指标(汇总 / 均值 / 占比)运行时计算,**不落库**(技术架构 §5.7)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 告警
|
||||||
|
|
||||||
|
| 告警 | 阈值(起步,可调) | 处置 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 接口错误率 | 5 分钟内 > 5% | 查 trace → 定位依赖 → 必要时回滚 |
|
||||||
|
| 队列积压 | 单队列深度 > 1000 或等待 > 10 分钟 | 扩 worker / 查卡死任务 |
|
||||||
|
| AI 预算 | 达软限 80% 告警、硬限 100% 阻断 | 降级路由 / 通知负责人 |
|
||||||
|
| 任务失败率 | 10 分钟内 > 10% | 查供应商 / 依赖 → 切路由 |
|
||||||
|
| 磁盘 / 存储 | 使用率 > 80% | 清理 temp / 扩容 |
|
||||||
|
| 证书到期 | < 14 天 | 续签 |
|
||||||
|
| 抠图降级率 | 单批次 > 30% | 排查模型 / 输入质量 |
|
||||||
|
|
||||||
|
- `LOG-17` 每条告警必须可执行:附 runbook 链接(`docs/runbook/`)与负责人。
|
||||||
|
- `LOG-18` 禁止「告警风暴」:同一根因聚合,5 分钟内同一告警只发一次。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 前端可观测
|
||||||
|
|
||||||
|
- `LOG-19` 前端错误上报必须带:`release` 版本、`trace_id`(若有)、路由、浏览器、是否客户数据脱敏。
|
||||||
|
- `LOG-20` 禁止上报用户隐私与客户素材内容;上报前统一脱敏。
|
||||||
|
- `LOG-21` 关键交互(生成、预览、勾选、导出)埋点事件名稳定,带 `customer_id` / `batch_id` / `user_id` 三维(PRD 8.1)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 检查清单
|
||||||
|
|
||||||
|
- [ ] 日志结构化,必带 `trace_id` / `module` / `action`
|
||||||
|
- [ ] `msg` 稳定可聚合,变量进字段
|
||||||
|
- [ ] 无敏感信息、无签名 URL、无 prompt 全文
|
||||||
|
- [ ] 跨队列传递 `traceparent`
|
||||||
|
- [ ] 审计与日志分离,关键操作全部入 `audit_logs`
|
||||||
|
- [ ] 新增关键路径有指标与告警
|
||||||
|
- [ ] 指标标签基数受控,派生指标不落库
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
# 09 · 环境变量与密钥规范
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 依据 | 技术架构 附录 C(关键环境变量)、§2.8(安全)、§4.1 / §4.4 |
|
||||||
|
| 适用范围 | 所有环境(dev / staging / prod)、CI、本地开发 |
|
||||||
|
| 强制级别 | 强制 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 基本规则
|
||||||
|
|
||||||
|
- `ENV-01` 只有 `.env.example` 入库;`.env`、`.env.local`、`.env.*.local` 一律不入库(`.gitignore` 已覆盖)。
|
||||||
|
- `ENV-02` 变量名 `UPPER_SNAKE_CASE`,按前缀分组;新增变量必须同时更新 `.env.example`、本文附录、部署配置三处。
|
||||||
|
- `ENV-03` 应用启动时用 Zod 校验环境变量,**缺失或非法直接 fail fast**,禁止带默认值「悄悄跑起来」。
|
||||||
|
- `ENV-04` 代码中只能通过集中配置模块读取(`config/env.ts`),**禁止**到处 `process.env.XXX`。
|
||||||
|
- `ENV-05` 禁止在仓库、PR、聊天工具、日志中传递真实密钥;示例值用 `xxx` / `changeme`。
|
||||||
|
- `ENV-06` 三个环境密钥**完全独立**:dev / staging / prod 不得复用同一套数据库、Redis、S3、JWT、AI 密钥。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 变量清单(前缀分组)
|
||||||
|
|
||||||
|
### 运行与数据库
|
||||||
|
|
||||||
|
| 变量 | 说明 | 示例 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `NODE_ENV` | 运行环境 | `development` / `production` |
|
||||||
|
| `DATABASE_URL` | PG 连接串 | `postgresql://user:pass@host:5432/lingxi` |
|
||||||
|
| `REDIS_URL` | Redis 连接串 | `redis://host:6379/0` |
|
||||||
|
| `QUEUE` | worker 订阅队列名 | `image:render` |
|
||||||
|
|
||||||
|
### 对象存储(S3 协议)
|
||||||
|
|
||||||
|
| 变量 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `S3_ENDPOINT` | MinIO / 云 OSS 端点 |
|
||||||
|
| `S3_ACCESS_KEY` / `S3_SECRET_KEY` | 访问密钥(按环境独立) |
|
||||||
|
| `S3_BUCKET_ASSETS` / `S3_BUCKET_RENDERS` / `S3_BUCKET_TEMP` | 资产 / 产物 / 临时桶 |
|
||||||
|
| `S3_BUCKET_ARCHIVE` / `S3_BUCKET_UPLOADS` / `S3_BUCKET_EXPORTS` | 归档 / 上传 / 导出桶 |
|
||||||
|
|
||||||
|
### 认证与安全
|
||||||
|
|
||||||
|
| 变量 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `JWT_SECRET` / `JWT_REFRESH_SECRET` | 长度 ≥ 32,按环境独立 |
|
||||||
|
| `ENCRYPTION_KEY` | AES-256-GCM 主密钥(加密 AI 供应商密钥等) |
|
||||||
|
| `PUBLIC_LINK_HMAC_SECRET` | 免登录审核 / 分享链接签名密钥 |
|
||||||
|
|
||||||
|
### AI 接入
|
||||||
|
|
||||||
|
| 变量 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `AI_PROVIDER_<NAME>_API_KEY` | 按供应商分(如 `AI_PROVIDER_JIMENG_API_KEY`) |
|
||||||
|
| `AI_PROVIDER_<NAME>_BASE_URL` | 自定义端点(可选) |
|
||||||
|
| `AI_ROUTE_REFRESH_MS` | 路由表缓存刷新间隔 |
|
||||||
|
|
||||||
|
> 供应商 API Key 以加密形式存库(`ai_configs`)时,环境变量只用于**首次注入 / 灾备**;运行时读取库内配置,避免多处真相。
|
||||||
|
|
||||||
|
### 媒体处理
|
||||||
|
|
||||||
|
| 变量 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `FFMPEG_PATH` / `FFPROBE_PATH` | 二进制路径(镜像内固定) |
|
||||||
|
| `MAX_UPLOAD_MB` | 单文件上限 |
|
||||||
|
| `WORKER_CONCURRENCY` | 单 worker 并发 |
|
||||||
|
|
||||||
|
### 成本与开关
|
||||||
|
|
||||||
|
| 变量 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `COST_SOFT_LIMIT` / `COST_HARD_LIMIT` | 预算软 / 硬限(默认 0.8 / 1.0) |
|
||||||
|
| `FEATURE_VIDEO` | 视频线开关 |
|
||||||
|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | 链路追踪导出端点 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 密钥管理
|
||||||
|
|
||||||
|
| 项 | 规则 |
|
||||||
|
| --- | --- |
|
||||||
|
| 存储 | 生产用云 KMS / Vault / Secrets Manager;**禁止**明文写进 compose 或镜像 |
|
||||||
|
| 注入 | CI/CD 注入环境变量;本地用 `.env.local`(不入库) |
|
||||||
|
| 加密 | AI 供应商密钥等入库字段用 AES-256-GCM,密钥不落同一库 |
|
||||||
|
| 权限 | 应用 DB 账号非 superuser;MinIO 按桶最小权限;CI 只读必要范围 |
|
||||||
|
| 轮换 | JWT / HMAC / 加密密钥至少每 6 个月或人员变动时轮换;轮换需兼容期(新旧并存) |
|
||||||
|
| 审计 | 密钥变更记审计(谁、何时、哪个 key 的版本号,**不记值**) |
|
||||||
|
|
||||||
|
- `ENV-07` 前端只能暴露 `VITE_PUBLIC_*` 前缀变量;构建产物必须扫描确认无服务端密钥。
|
||||||
|
- `ENV-08` 禁止把密钥拼进 URL / query / 日志 / 异常信息。
|
||||||
|
- `ENV-09` 本地开发连的 MinIO / PG 凭据只能是 dev 值,且与生产不一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 泄露处置(30 分钟内)
|
||||||
|
|
||||||
|
1. **立即轮换**对应密钥(先轮换再排查,不等待确认)。
|
||||||
|
2. 撤销相关 token / session,作废签名链接。
|
||||||
|
3. 检查访问日志与 `ai_usage`,评估是否被滥用(尤其 AI 费用)。
|
||||||
|
4. 用 `git filter-repo` 清理历史并强制全员重新 clone(见 [01](01-Git规范.md) `GIT-20`)。
|
||||||
|
5. 24 小时内出复盘:泄露路径、影响面、改进项(通常补 CI 扫描 / pre-commit)。
|
||||||
|
|
||||||
|
- `ENV-10` 泄露事件必须记录在案,**禁止**「悄悄换掉当没发生」。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 本地开发
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env.local # 填 dev 值(不得填生产值)
|
||||||
|
pnpm compose:up # PG 18 / Redis 7.4 / MinIO
|
||||||
|
pnpm db:migrate && pnpm db:seed
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- `ENV-11` 禁止 `docker compose down -v` 清库;重置数据用 `pnpm db:reset`。
|
||||||
|
- `ENV-12` 本地不得连生产数据库 / 生产对象存储 / 生产 AI 供应商(费用与数据风险)。
|
||||||
|
- `ENV-13` 开发机 Node 版本由 `.nvmrc` 固定,`package.json` 声明 `engines`,`preinstall` 用 `only-allow pnpm` 拦截其他包管理器。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 检查清单
|
||||||
|
|
||||||
|
- [ ] 新增变量已写 `.env.example` 与本文
|
||||||
|
- [ ] 启动校验覆盖新变量,缺失即失败
|
||||||
|
- [ ] 未在代码中散读 `process.env`
|
||||||
|
- [ ] 三环境密钥独立,未复用
|
||||||
|
- [ ] 无密钥进日志 / URL / 前端 bundle
|
||||||
|
- [ ] 生产密钥来自 KMS / Secrets Manager
|
||||||
|
- [ ] 轮换与泄露处置流程已知晓
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# 10 · 资产命名与 PSD 图层规范
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 文档版本 | V1.0(2026-11-02) |
|
||||||
|
| 依据 | PRD §5.3(资产)、§5.2(素材线)、R-01 / R-19 / R-25 / R-29 / R-30;技术架构 §7.3 / §7.5 / §8.11 |
|
||||||
|
| 适用范围 | 资产上传、模板制作、批次命名、成片导出、对象存储 |
|
||||||
|
| 强制级别 | 强制(`PSD-*` 为图片线硬前提,不满足即不可入库) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 归属与层级
|
||||||
|
|
||||||
|
- `AST-01` 全部资产必须挂在「**行业 → 客户**」两级目录下;行业是客户的业务属性,**客户不可跨行业移动**(PRD R-20)。
|
||||||
|
- `AST-02` 资产三级:**行业级(公共)→ 客户级(私有)→ 批次级(一次性)**;同名时客户级覆盖行业级。
|
||||||
|
- `AST-03` 跨客户复用必须显式授权并留痕,**禁止**默认共享(PRD 5.3.2)。
|
||||||
|
- `AST-04` 15 类资产固定为:行业包、脚本模板、视频方向、已入库脚本、图片模板、Logo、产品位置、警示语、人物池、场景池、光影/色调预设、BGM/音效、提示词模板、要素卡模板/卖点库、成片。新增类型须先改 PRD 与架构。
|
||||||
|
- `AST-05` 资产状态:草稿 → 启用 → 停用;**被引用的资产只能停用不能删除**(R-02);模板与提示词修改产生新版本并记录被引用的版本号(R-03)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 命名通用规则
|
||||||
|
|
||||||
|
| 项 | 规则 |
|
||||||
|
| --- | --- |
|
||||||
|
| 允许字符 | 中文、字母、数字、下划线 `_`、短横线 `-` |
|
||||||
|
| 禁止字符 | 空格、`\ / : * ? " < > |`、`# % & { } $ ! ' @ + =`、控制字符 |
|
||||||
|
| 长度 | ≤ 100 字符(含扩展名) |
|
||||||
|
| 占位符 | `{行业}` `{客户}` `{视频方向}` `{日期}` `{序号}`(PRD R-01) |
|
||||||
|
| 默认格式 | `{行业}-{客户}-{视频方向}-{序号}` |
|
||||||
|
| 重名 | 自动追加序号(`-02`、`-03`),**禁止覆盖** |
|
||||||
|
| 大小写 | 语义名大小写敏感;扩展名统一小写 |
|
||||||
|
|
||||||
|
- `AST-06` 占位符由服务端在**批次创建时快照**,之后修改命名规则不影响历史批次。
|
||||||
|
- `AST-07` 文件名中的客户名使用**客户简称**(必填字段),不使用全称,避免超长与敏感信息。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 各类编号与成片命名
|
||||||
|
|
||||||
|
| 对象 | 规则 | 示例 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 脚本编号 | `{客户编码}-YYMMDD + 当日序号`,**过审入库时原子取号**;草稿不占号;一经分配不重用、不可改(R-21) | `LX001-261102-007` |
|
||||||
|
| 素材批次号 | `{YYMMDD}{3 位当日序号}` | `261102003` |
|
||||||
|
| 图片成片 | `{行业}-{客户}-{脚本方向}-{日期}-{序号}-{规格}` | `电商-某服饰-通勤穿搭-261102-01-9x16` |
|
||||||
|
| 视频成片 | `{行业}-{客户}-{视频方向}-{序号}` | `电商-某服饰-达人到店-03` |
|
||||||
|
| 导出目录 | `客户 / 交付日期 / 类型 / 批次` | `某服饰/2026-11-02/图片/261102003` |
|
||||||
|
|
||||||
|
- `AST-08` 规格后缀用 `宽x高` 或平台别名(`9x16`、`1x1`),由字典统一,**禁止**自由书写。
|
||||||
|
- `AST-09` 成片命名与脚本编号必须可对账(交付清单同 key,PRD 5.5)。
|
||||||
|
- `AST-10` 对象存储 key 与展示名分离:`storage_key = assets/{industry_id}/{customer_id}/{asset_id}/v{version}/{filename}`;展示名可改,key 不可改。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 PSD 模板与图层约定(图片线核心)
|
||||||
|
|
||||||
|
### 4.1 图层前缀(唯一合法语义声明方式)
|
||||||
|
|
||||||
|
| 前缀 | 语义 | 数量要求 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `image_replacement_` | 商品图替换位(主体) | ≥ 1 |
|
||||||
|
| `clipping_mask_` | 剪贴容器 / 裁切区域 | 0..n |
|
||||||
|
| `text_replacement_` | 文字替换位(可配置字号 / 对齐 / 颜色) | 0..n |
|
||||||
|
|
||||||
|
- `PSD-01` 命名结构:`<前缀><语义名>`,如 `image_replacement_main`、`text_replacement_price`;**前缀全小写且大小写敏感**。
|
||||||
|
- `PSD-02` 同一模板内语义名唯一;重复命名视为非法。
|
||||||
|
- `PSD-03` **无命名图层 / 解析失败 / 缺少 `image_replacement_` → 模板不得入库**,返回明确错误并提示设计师修正。
|
||||||
|
- `PSD-04` 禁止用图层组名冒充命名图层;前缀必须落在**实际图层**上。
|
||||||
|
- `PSD-05` 剪贴蒙版必须挂在 `clipping_mask_` 容器下,商品图替换后仍保持裁切关系。
|
||||||
|
- `PSD-06` 新增图层前缀类型须先更新技术架构 §7.3 与本文,**禁止**在代码里私自识别新前缀。
|
||||||
|
- `PSD-07` 模板入库时校验:图层语义完整性、画布尺寸与目标规格比例、字体是否在白名单、是否存在隐藏图层(隐藏图层默认忽略并记录)。
|
||||||
|
- `PSD-08` 字体:镜像内置中文字体,启动自检;模板引用的字体不在白名单 → 拒绝入库(避免豆腐块,风险 R-T-18)。
|
||||||
|
|
||||||
|
### 4.2 抠图与合成
|
||||||
|
|
||||||
|
- `PSD-09` 抠图失败必须降级为原图并标 `low_quality`(原因码 `matting_fallback`),**默认不勾选**(R-30)。
|
||||||
|
- `PSD-10` 合成结果必须过质量校验:安全区、Logo 完整性、主体不变形、警示语四项;未过则标 `low_quality` 并记录原因码。
|
||||||
|
- `PSD-11` 视觉回归:`packages/render` 改动需跑像素级比对,阈值与基线图入库管理。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 规格矩阵(字典化,禁止硬编码)
|
||||||
|
|
||||||
|
| 平台 | 比例 | 分辨率 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 抖音 | 9:16 | 1080×1920 |
|
||||||
|
| 小红书 | 3:4 | 1080×1440 |
|
||||||
|
| 朋友圈 | 1:1 / 4:5 | 1080×1080 / 1080×1350 |
|
||||||
|
| 横版 | 16:9 | 1920×1080 |
|
||||||
|
| 电商主图 | 1:1 | 800×800 / 1200×1200 |
|
||||||
|
|
||||||
|
- `AST-11` 规格来自字典表,前后端与任务判定读同一来源(R-26);新增规格改字典不发版。
|
||||||
|
- `AST-12` 素材为主对象、规格为属性:**同源多规格不各建素材**,用 `material_specs` 承载(R-25);素材与脚本多对多用关联表,**禁止 JSON 列存 ID**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6 输出编码与存储
|
||||||
|
|
||||||
|
| 类型 | 参数 |
|
||||||
|
| --- | --- |
|
||||||
|
| 视频 | MP4 / H.264 / AAC / 1080×1920 / 30fps / `+faststart` |
|
||||||
|
| 图片 | JPG(质量 92)或 PNG;导出质量 ≥ 90 |
|
||||||
|
| 兜底 | 超限降质必须标 `low_quality`(原因码 `downscaled`),不得静默 |
|
||||||
|
|
||||||
|
| 桶 | 内容 | 保留 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `assets` | 长期资产 | 永久(版本化) |
|
||||||
|
| `renders` | 生成产物 | 按客户策略 |
|
||||||
|
| `temp` | 任务中间文件 | 30 天 |
|
||||||
|
| `archive` | 归档 | ≥ 12 个月 |
|
||||||
|
| `uploads` | 原始上传 | 按资产生命周期 |
|
||||||
|
| `exports` | 打包导出 | 3 天 |
|
||||||
|
|
||||||
|
- `AST-13` 下载一律短时效签名 URL(5–15 分钟),**禁止**公开读权限、禁止把签名 URL 存库。
|
||||||
|
- `AST-14` 临时目录按任务隔离:`/tmp/task/{taskItemId}`,任务结束清理;失败任务保留 1 天便于排查后清理。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 低质量原因码(统一枚举)
|
||||||
|
|
||||||
|
`matting_fallback`(抠图降级)· `missing_font`(缺字体)· `downscaled`(超限降质)· `logo_incomplete`(Logo 不完整)· `warning_missing`(警示语缺失)· `safe_area_violated`(安全区越界)· `subject_distorted`(主体变形)
|
||||||
|
|
||||||
|
- `AST-15` 原因码只增不改语义;前端与看板按码聚合,**禁止**在文案里写自由文本。
|
||||||
|
- `AST-16` 低质量条目计入质量看板与指标(PRD 8.1),可被人工复核转为正常,但**必须留痕**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8 检查清单(模板入库前)
|
||||||
|
|
||||||
|
- [ ] PSD 至少 1 个 `image_replacement_`,剪贴关系正确
|
||||||
|
- [ ] 所有可替换位均有合法前缀,语义名唯一
|
||||||
|
- [ ] 画布比例与目标规格匹配
|
||||||
|
- [ ] 字体在白名单内,无隐藏图层未说明
|
||||||
|
- [ ] 模板归属「行业 → 客户」正确,版本号已生成
|
||||||
|
- [ ] 命名符合 §2,无非法字符,长度 ≤ 100
|
||||||
|
- [ ] 低质量降级路径可触发,原因码已定义
|
||||||
|
- [ ] 产物编码参数符合 §6,签名 URL 生效
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# 工程规范索引
|
||||||
|
|
||||||
|
> 本目录是 [`AGENTS.md`](../../AGENTS.md) 的下游细则。**总纲只写红线与索引,细则在这里展开**。
|
||||||
|
> 阅读顺序:先读总纲 §3 硬红线,再按任务查对应细则。
|
||||||
|
|
||||||
|
| 编号 | 文档 | 覆盖范围 | 强制级别 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 01 | [Git 规范](01-Git规范.md) | 分支模型、提交信息、合并与发布、Tag、大文件 | 强制 |
|
||||||
|
| 02 | [代码风格](02-代码风格.md) | 命名、类型、错误处理、异步、React、注释、导入顺序 | 强制 |
|
||||||
|
| 03 | [格式化与 Lint](03-格式化与Lint.md) | Prettier / ESLint 配置、钩子、CI 门禁、豁免流程 | 强制 |
|
||||||
|
| 04 | [API 与路由规范](04-API与路由规范.md) | REST、版本、错误码、鉴权链、任务协议、上传下载、前端路由 | 强制 |
|
||||||
|
| 05 | [数据库规范](05-数据库规范.md) | 命名、主键、客户隔离、索引、迁移、事务、保留期 | 强制 |
|
||||||
|
| 06 | [测试与 CI 规范](06-测试与CI规范.md) | 分层测试、必测清单、门禁、发布与回滚 | 强制 |
|
||||||
|
| 07 | [Code Review 与 PR 规范](07-CodeReview与PR规范.md) | PR 模板、双人场景、Review 清单、DoD | 强制 |
|
||||||
|
| 08 | [日志与可观测性规范](08-日志与可观测性规范.md) | 日志字段、trace、审计、指标、告警 | 强制 |
|
||||||
|
| 09 | [环境变量与密钥规范](09-环境变量与密钥规范.md) | env 命名、校验、密钥管理、轮换、泄露处置 | 强制 |
|
||||||
|
| 10 | [资产命名与 PSD 图层规范](10-资产命名与PSD图层规范.md) | 资产归属、命名占位符、图层前缀、规格矩阵、低质量标记 | 强制 |
|
||||||
|
|
||||||
|
## 规范文档自身的约定
|
||||||
|
|
||||||
|
| 项 | 约定 |
|
||||||
|
| --- | --- |
|
||||||
|
| 编写语言 | 中文说明 + 英文标识符 / 命令 / 路径 |
|
||||||
|
| 结构 | 作用与边界 → 规则(表格 / 编号)→ 检查清单 → 违规示例 |
|
||||||
|
| 变更 | 修改规则须单独 PR,说明「为什么改」+「存量代码如何处理」 |
|
||||||
|
| 版本 | `V<主>.<次>`;主版本 = 破坏性(存量代码需整改),次版本 = 补充 |
|
||||||
|
| 冲突 | 与本目录细则冲突时以技术架构为准;技术架构未覆盖以 PRD 为准 |
|
||||||
|
| 有效性 | 规则必须可被检查(lint / CI / review 清单 / 脚本),否则降为「建议」 |
|
||||||
|
| 废止 | 删除规则必须在 PR 描述里写清理计划,禁止直接删条目 |
|
||||||
|
|
||||||
|
## 与总纲的关系
|
||||||
|
|
||||||
|
- 总纲 `AGENTS.md` §3 的 14 条硬红线**不得在本目录被弱化**,只能细化。
|
||||||
|
- 本目录新增的「强制」规则,须在总纲 §9 索引同步登记。
|
||||||
|
- 规则编号规则:`<文档号>-<序号>`,如 `DB-07`、`API-03`,便于代码注释与 Review 引用。
|
||||||
|
|
||||||
|
## 当前状态说明(2026-11-02)
|
||||||
|
|
||||||
|
仓库处于**文档阶段**,尚无代码。本目录是**建仓前置规范**:代码落地时必须按此执行,不允许「先写代码、后补规范」。
|
||||||
|
|
||||||
|
待代码落地后需要补充/校准的配置类文件(届时以实际配置为准,规范条目不变):
|
||||||
|
|
||||||
|
`.editorconfig` · `.nvmrc` · `.npmrc` · `prettier.config.mjs` · `eslint.config.mjs` · `commitlint.config.js` · `.lintstagedrc` · `turbo.json` · `docker-compose.dev.yaml` · `.env.example`
|
||||||
Reference in new issue
Block a user