企业级重构:四层模块化架构 + RBAC授权 + 安全加固 + 颜色引擎/配方推演增强
架构 - 后端从 flat routes/ 重构为 modules/<domain>/ 模块化结构(8个模块) - 四层架构:Route -> Service -> Repository -> Prisma - 新增 shared/ 基础设施(AppError 异常体系、ALS 上下文、prom-client 指标) - 前端 Toast/Skeleton/Alert 组件基建 + formulaService 模板 安全 - JWT 签名算法修复(HS256 用 createHmac 而非 createHash) - 密码哈希 async scrypt + timingSafeEqual - API Key 从 localStorage 迁移至服务端 runtime/config.json - Helmet 安全头 + rate-limit 全局限流 100 req/min - 全局 auth preHandler + RBAC + Ownership 中间件 颜色引擎 - 色匹配切换为 cube 粗筛 + CIEDE2000 精排 - PantoneColor 表 + 种子数据 + 搜索端点 - AI 配色 Prompt 注入成分库 colorant 列表 配方推演 - 本地优化引擎(同 category 替换 + 成本排序) - baseFormulaId 支持 + Pareto 散点图 文档 - ADR-0003 四层架构、ADR-0004 RBAC 授权模型 - 更新 ADR-0001/0002 - api-reference.md(29端点)、project-overview.md 部署 - Dockerfile * 2 + nginx.conf + docker-compose.prod.yml - 健康探针 + 优雅关闭 + pg_dump 备份脚本 - ESLint + Prettier + tsconfig strict
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# ADR-0001: 整体技术栈选型
|
||||
|
||||
> **状态**: 已决议
|
||||
> **日期**: 2026-05-20
|
||||
> **状态**: 已决议(2026-05-21 修订)
|
||||
> **日期**: 2026-05-20
|
||||
> **修订**: 2026-05-21
|
||||
> **决策者**: 架构评审
|
||||
|
||||
---
|
||||
@@ -14,7 +15,7 @@
|
||||
|
||||
## 决策
|
||||
|
||||
### 1. 前端框架 → React 18 + TypeScript 5.7
|
||||
### 1. 前端框架 → React 19 + TypeScript 5.7
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
@@ -23,11 +24,13 @@
|
||||
| Svelte 5 | 编译时框架,运行时极小 | 生态较小;关键库适配风险;社区资源少 | ❌ |
|
||||
| SolidJS | 性能优于 React;API 相似 | 社区太小(GitHub stars ~30k vs React ~230k);生产风险高 | ❌ |
|
||||
|
||||
**决策**:React 18 + TypeScript strict mode。React 19 待生态稳定后再升级。
|
||||
**决策**:React 19 + TypeScript strict mode。
|
||||
|
||||
> **修订(2026-05-21)**:从 React 18 升级到 React 19。生态已稳定,React Compiler 带来额外性能收益。
|
||||
|
||||
---
|
||||
|
||||
### 2. 构建工具 → Vite 6
|
||||
### 2. 构建工具 → Vite 8
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
@@ -36,11 +39,13 @@
|
||||
| Remix | SSR 优先;Web 标准 | 同上;社区较 Next.js 小 | ❌ |
|
||||
| CRA | — | 已停止维护;Webpack 构建慢 | ❌ |
|
||||
|
||||
**决策**:Vite 6,SPA 模式。平台为内部工具,无需 SEO/SSR。
|
||||
**决策**:Vite 8,SPA 模式。平台为内部工具,无需 SEO/SSR。
|
||||
|
||||
> **修订(2026-05-21)**:从 Vite 6 升级到 Vite 8。
|
||||
|
||||
---
|
||||
|
||||
### 3. 状态管理 → Zustand 5
|
||||
### 3. 状态管理 → Zustand
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
@@ -84,128 +89,83 @@
|
||||
| **Radix UI** | Headless(完全控制样式);WAI-ARIA 内置;组件粒度合适;与 Tailwind 天配 | 无预置视觉风格(需要自行设计) | ✅ 推荐 |
|
||||
| Ant Design | 开箱即用,组件丰富 | 企业后台感强;视觉定制困难;不适合创意工具;bundle 大 | ❌ |
|
||||
| MUI | Material Design 完整实现 | 同上;Google 风格固化 | ❌ |
|
||||
| shadcn/ui | 基于 Radix + Tailwind,复制源码 | 本质是 Radix 封装;直接 Radix 更灵活 | ❌ |
|
||||
| shadcn/ui | 基于 Radix + Tailwind 预封装 | 封装度低,仍需二次开发 | — |
|
||||
|
||||
**决策**:Radix UI 提供 Dialog、Popover、Dropdown、Tabs、Tooltip 等行为组件。视觉层完全自定义,匹配配方研发工具的专业调性。
|
||||
**决策**:Radix UI。平台 UI 需要与化妆品实验室品牌调性一致,Radix 的 Headless 模式允许完全定制视觉。
|
||||
|
||||
---
|
||||
|
||||
### 7. 图表可视化 → ECharts 5(主力) + D3.js 7(定制)
|
||||
|
||||
| 候选 | 雷达图 | 拖拽饼图 | 仪表盘 | 散点图 | 自定义度 | Bundle | 结论 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| **ECharts** | ✅ 内置 | ✅ resize 事件 | ✅ 内置 | ✅ 内置 | 中 | ~300KB(按需~150KB) | ✅ 主力 |
|
||||
| **D3.js** | 需自建 | 需自建 | 需自建 | 需自建 | ✅ 最高 | ~150KB | ✅ 定制场景 |
|
||||
| Recharts | ✅ | ⚠️ 有限 | ✅ | ✅ | 低 | ~100KB | ❌ 拖拽不足 |
|
||||
| Nivo | ✅ | ⚠️ 有限 | ❌ | ✅ | 中 | ~200KB | ❌ 缺仪表盘 |
|
||||
| Plotly.js | ✅ | ⚠️ | ✅ | ✅ | 中 | ~3MB | ❌ 体积过大 |
|
||||
| Visx | 需自建 | 需自建 | 需自建 | 需自建 | 高 | 按需 | ❌ 开发量大 |
|
||||
|
||||
**决策**:
|
||||
- **ECharts** 处理标准化图表:雷达图(肤感指标)、饼图(成分比例,监听 resize 实现拖拽联动)、仪表盘(稳定性/成本)、散点图(成本-功效 Pareto 前沿)
|
||||
- **D3.js** 处理高度定制场景:色相环/颜色盘、配方路径示意图、配方结构树图
|
||||
- React 绑定使用 `echarts-for-react`
|
||||
|
||||
---
|
||||
|
||||
### 8. 色彩科学 → color.js
|
||||
|
||||
| 候选 | 色空间 | ΔE | Display P3 | 色域映射 | TS 类型 | 维护者 | 结论 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| **color.js** | Lab/LCH/OKLab/Display P3 + 20+ | CIEDE2000/CMC/... | ✅ | ✅ | ✅ 完备 | Lea Verou (W3C CSS WG) | ✅ 推荐 |
|
||||
| chroma.js | Lab/LCH/RGB | CIEDE2000 | ❌ | ❌ | ⚠️ 部分 | Gregor Aisch | ❌ 缺 P3 |
|
||||
| culori | Lab/LCH/OKLab | CIEDE2000 | ⚠️ 有限 | ⚠️ 有限 | ✅ | Dan Burzo | ❌ 社区小 |
|
||||
| d3-color | Lab/RGB | ❌ 无 | ❌ | ❌ | ✅ | Mike Bostock | ❌ 功能太基础 |
|
||||
|
||||
**决策**:color.js。唯一同时支持 Display P3 色空间、CIEDE2000 ΔE、色域映射的开源 JS 库。由 CSS Color Level 4 规范编者维护,与浏览器标准对齐。
|
||||
|
||||
---
|
||||
|
||||
### 9. 拖拽交互 → DnD Kit
|
||||
|
||||
| 候选 | 状态 | 鼠标/触摸/键盘 | 碰撞检测 | TS | 维护 | 结论 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| **@dnd-kit** | 活跃 | ✅ 全部 | 可定制 | ✅ | 活跃 | ✅ 推荐 |
|
||||
| react-beautiful-dnd | 停止维护 | 鼠标/触摸 | 内置 | ⚠️ | Atlassian 停止维护(2023) | ❌ |
|
||||
| Pragmatic drag and drop | 活跃 | ✅ 全部 | 可定制 | ✅ | Atlassian 新项目 | ⚠️ 太新 |
|
||||
|
||||
**决策**:@dnd-kit/core + @dnd-kit/sortable。饼图段的拖拽调整比例场景完美匹配,支持自定义碰撞检测算法。
|
||||
|
||||
---
|
||||
|
||||
### 10. 数据请求 → TanStack Query 5
|
||||
### 7. 图表 → ECharts
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **TanStack Query** | 缓存/重试/乐观更新/无限滚动开箱即用;与 Zustand 分工清晰 | — | ✅ 推荐 |
|
||||
| SWR | 类似功能 | API 不如 TanStack Query 丰富 | ❌ |
|
||||
| RTK Query | Redux 集成 | 绑定 Redux | ❌ |
|
||||
| **ECharts** | 可视化类型最丰富(雷达图/桑基图/热力图等);React 绑定成熟(echarts-for-react);大数据集高性能 | 包体积较大(~1MB) | ✅ 推荐 |
|
||||
| D3.js | 自由度最高;定制性极强 | 命令式 API;React 集成需大量封装;开发效率低 | ❌ |
|
||||
| Recharts | React 声明式;组件化 | 图表类型有限;大数据集性能差 | ❌ |
|
||||
|
||||
**决策**:TanStack Query v5。推荐列表、配方搜索、AI 预测等所有异步请求均通过它管理。
|
||||
**决策**:ECharts 作为主图表库,D3.js 作为辅助(颜色空间可视化等高度定制场景)。
|
||||
|
||||
---
|
||||
|
||||
### 11. 后端 → Fastify(BFF 单体)+ 外部 AI API
|
||||
### 8. 色彩科学 → colorjs.io
|
||||
|
||||
```
|
||||
┌─────────┐ HTTP/SSE ┌──────────┐ HTTP ┌──────────┐
|
||||
│ React │ ◄─────────────► │ Fastify │ ◄────────────► │ AI API │
|
||||
│ 前端 │ │ BFF 层 │ │ (外部) │
|
||||
└─────────┘ └────┬─────┘ └──────────┘
|
||||
│
|
||||
┌────▼─────┐
|
||||
│PostgreSQL │
|
||||
│ + pgvector│
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
| 层 | 技术 | 职责 | 理由 |
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **BFF** | Fastify 5 | API 聚合、认证鉴权、文件上传、配方 CRUD、AI API 调用代理、SSE 推送 | TS 全栈一致性;Schema 验证内置;性能优于 Express |
|
||||
| **AI** | 外部 API | 配方预测、配方生成、NL 解析、颜色匹配推荐 | 无需自建 ML infra;按需调用;模型持续更新 |
|
||||
| **通信** | REST + SSE | 前端 ↔ BFF REST;AI 预测 SSE 实时推送;BFF ↔ AI API HTTP | SSE 适合单向实时数据流;BFF 统一处理 AI 调用的编排和降级 |
|
||||
| **colorjs.io** | 支持所有颜色空间(CIELAB/Display P3/LCH 等);ΔE 2000/CMC 计算;积极维护 | 社区较 chroma.js 小 | ✅ 推荐 |
|
||||
| chroma.js | 轻量 API;流行度高 | 不支持 ΔE 2000;不支持 Display P3 | ❌ |
|
||||
| d3-color | 与 D3 生态集成 | 颜色空间有限;无 ΔE | ❌ |
|
||||
|
||||
| BFF 框架对比 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Fastify** | 高性能(~60k req/s);插件体系;内置 Schema 验证(AJV);TS 支持;日志(pino) | 社区较 Express 小 | ✅ 推荐 |
|
||||
| Express | 生态最大;中间件极多 | 性能较低;无内置验证;回调风格 | ❌ |
|
||||
| Hono | 极快;边缘部署 | 主要用于 Cloudflare/边缘场景 | ❌ |
|
||||
|
||||
**AI API 调用设计**(详见 ADR-0002):
|
||||
|
||||
BFF 作为 AI API 的统一网关,负责:
|
||||
- **编排**:组合多次 API 调用(如"先解析 NL 查询 → 再向量搜索 → 组合 prompt → 生成推荐")
|
||||
- **降级**:API 超时或失败时返回缓存结果或降级提示
|
||||
- **限流**:保护 API 额度,控制并发
|
||||
- **缓存**:相似查询复用结果,减少 API 调用
|
||||
- **SSE 流式**:配方推演等长耗时场景,BFF 转发 AI API 的 streaming 响应
|
||||
|
||||
| AI 能力 | 调用方式 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| 配方指标预测 | Prompt + 历史数据(few-shot) | 将成分列表和比例作为 context,LLM 预测肤感/稳定性/成本 |
|
||||
| NL 搜索解析 | LLM → 结构化查询 → pgvector | LLM 将自然语言转为过滤条件 + 向量搜索 |
|
||||
| 配方生成/推演 | LLM with constraints | 给定约束条件,LLM 生成候选配方方案 |
|
||||
| 颜色推荐 | LLM + 色彩库 | 结合色差计算和 LLM 推理推荐色浆组合 |
|
||||
| 成分标签提取 | LLM 结构化提取 | 从配方文本中提取 INCI 名称、比例、工艺参数 |
|
||||
**决策**:colorjs.io。
|
||||
|
||||
---
|
||||
|
||||
### 12. 数据库 → PostgreSQL 16 + pgvector
|
||||
### 9. 后端框架 → Fastify
|
||||
|
||||
| 候选 | 结构化 | 向量搜索 | JSONB | 全文搜索 | 运维 | 结论 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| **PostgreSQL + pgvector** | ✅ | ✅ IVFFlat/HNSW | ✅ | ✅ | 成熟 | ✅ 推荐 |
|
||||
| MongoDB | ⚠️ 文档型 | ✅ Atlas Vector | ✅ | ⚠️ | 成熟 | ❌ 配方强关系 |
|
||||
| Elasticsearch | ❌ | ⚠️ 需插件 | ❌ | ✅ | 复杂 | ❌ 过度 |
|
||||
| Neo4j | ❌ 图 | ⚠️ | ❌ | ❌ | 小众 | ❌ 过度 |
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Fastify** | 性能最高(~60k req/s);插件生态完善(CORS/Swagger/Helmet);TypeScript 原生支持;schema 验证 | 社区较 Express 小 | ✅ 推荐 |
|
||||
| Express | 最广泛使用;中间件生态极丰富 | 性能较差(~15k req/s);TS 支持需额外配置;回调风格 | ❌ |
|
||||
| Hono | 极轻量(< 10KB);运行时无关(Node/Deno/Bun/Edge) | 生态较小;企业级插件不成熟 | ❌ |
|
||||
| NestJS | 开箱即用架构(Module/Controller/Service);DI 容器 | 过度工程化;装饰器侵入性强;学习曲线陡峭;冷启动慢 | ❌ |
|
||||
|
||||
**决策**:PostgreSQL 16 + pgvector。
|
||||
**决策**:Fastify 5 + TypeScript。插件体系完整且性能优异,适合 API 密集型场景。
|
||||
|
||||
- 配方是强结构化数据(成分→相→配方→版本,经典关系模型)
|
||||
- pgvector 0.7+ 支持 HNSW 索引,NL 搜索和相似配方匹配性能优秀
|
||||
- JSONB 处理灵活的工艺参数和元数据
|
||||
- 一个数据库同时满足关系查询 + 向量搜索 + 全文搜索,运维简单
|
||||
- 配方版本管理利用 PostgreSQL 的 MVCC + 时间戳快照
|
||||
---
|
||||
|
||||
### 10. 数据库 → PostgreSQL + pgvector
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **PostgreSQL + pgvector** | 成熟稳定;pgvector 扩展支持向量搜索(HNSW 索引);ACID 事务 | 向量搜索性能不如专用向量 DB | ✅ 推荐 |
|
||||
| MongoDB | 文档模型灵活;内置 Atlas Search | 缺乏 ACID;向量搜索需 Atlas | ❌ |
|
||||
| Elasticsearch | 全文搜索最强 | 运维成本高;需额外同步数据 | ❌ |
|
||||
|
||||
**决策**:PostgreSQL + pgvector。配方数据是典型的关系型结构(配方→相→成分),PostgreSQL 的关系模型天然适配;向量搜索用于语义配方查找,pgvector 性能足够。
|
||||
|
||||
---
|
||||
|
||||
### 11. ORM → Prisma
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Prisma** | 类型安全;Schema-first;迁移工具成熟;Client 自动生成;关系模型直观 | 复杂查询性能不如原生 SQL | ✅ 推荐 |
|
||||
| Drizzle ORM | 轻量零依赖;SQL-like API;性能接近原生 | 生态较新;迁移工具不如 Prisma 成熟 | ❌ |
|
||||
| Knex.js | SQL 构建器;灵活 | 无类型安全;手写迁移 | ❌ |
|
||||
| Sequelize | 历史悠久;生态大 | 类型支持弱;API 设计过时 | ❌ |
|
||||
|
||||
**决策**:Prisma 7 + pg adapter。项目中配方的关系嵌套深度大(Formula→Version→Phase→FormulaIngredient→Ingredient),Prisma 的关联查询和事务支持良好。
|
||||
|
||||
---
|
||||
|
||||
### 12. 包管理 → pnpm workspace
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **pnpm** | 硬盘高效(硬链接);严格依赖;monorepo workspace 支持;速度快 | lockfile 格式与 npm 不兼容 | ✅ 推荐 |
|
||||
| npm | 默认工具 | 依赖扁平化导致幽灵依赖;磁盘占用大;慢 | ❌ |
|
||||
| Yarn | Plug'n'Play 模式 | PnP 兼容性问题多;社区分裂 | ❌ |
|
||||
|
||||
**决策**:pnpm。前后端独立 package,共享 workspace 协议。
|
||||
|
||||
---
|
||||
|
||||
@@ -213,56 +173,31 @@ BFF 作为 AI API 的统一网关,负责:
|
||||
|
||||
**决策**:MinIO(S3 兼容),存储参考图片、导出文件。本地部署,S3 API 可无缝迁移到云。
|
||||
|
||||
> **修订(2026-05-21)**:MinIO 设为 docker compose `profiles: ["full"]`,默认不启动。当前无实际代码使用。
|
||||
|
||||
---
|
||||
|
||||
### 14. 部署 → Docker Compose(开发)+ K8s(生产)
|
||||
### 14. 部署 → Docker Compose + Traefik
|
||||
|
||||
| 服务 | 端口 | 职责 |
|
||||
| :--- | :--- | :--- |
|
||||
| Nginx | 80/443 | 静态资源、反向代理、SSL 终结 |
|
||||
| Fastify BFF | 3001 | API 聚合、认证、文件上传、AI API 代理 |
|
||||
| PostgreSQL | 5432 | 配方数据 + 向量 |
|
||||
| MinIO | 9000 | 对象存储 |
|
||||
| Redis(可选) | 6379 | 缓存、Session、AI 响应缓存 |
|
||||
**决策**:开发环境 `docker compose up`(PostgreSQL + MinIO(可选)),生产环境 `docker compose -f docker-compose.prod.yml up`(Traefik + PostgreSQL + Backend + Frontend)。
|
||||
|
||||
**开发环境**:Docker Compose 一键启动所有服务(无需 AI 依赖,BFF 可 mock AI API)
|
||||
**生产环境**:Kubernetes,仅 4 个核心服务,显著降低运维复杂度
|
||||
> **修订(2026-05-21)**:新增生产环境部署方案。Traefik 负责反向代理和自动负载均衡,Backend/Frontend 均已容器化。
|
||||
|
||||
---
|
||||
|
||||
### 15. 安全加固
|
||||
|
||||
| 组件 | 用途 | 添加日期 |
|
||||
|------|------|----------|
|
||||
| `@fastify/helmet` | 安全 HTTP 头(HSTS/X-Frame/X-Content-Type 等) | 2026-05-21 |
|
||||
| `@fastify/rate-limit` | 全局速率限制(100 req/min) | 2026-05-21 |
|
||||
| `@fastify/swagger` + `@fastify/swagger-ui` | OpenAPI 文档生成 + `/docs` 交互式浏览 | 2026-05-21 |
|
||||
|
||||
---
|
||||
|
||||
## 后果
|
||||
|
||||
### 正向
|
||||
|
||||
- React + Vite + Zustand + TanStack Query 形成轻量高效的现代前端栈
|
||||
- ECharts + D3.js 组合覆盖标准化图表和定制可视化全场景
|
||||
- color.js 唯一满足 Display P3 + CIEDE2000 需求的色彩库
|
||||
- AI 通过外部 API 调用,无需自建 ML 基础设施,运维简单
|
||||
- 仅 4 个核心服务(Nginx + BFF + PostgreSQL + MinIO),部署轻量
|
||||
- PostgreSQL + pgvector 单一数据库减少运维复杂度
|
||||
|
||||
### 风险和缓解
|
||||
|
||||
| 风险 | 缓解 |
|
||||
| :--- | :--- |
|
||||
| color.js 仍为 Beta(v0.x) | 锁定版本;Delta E 逻辑简单,必要时可自实现 CIEDE2000 |
|
||||
| DnD Kit 维护节奏慢 | API 稳定,核心功能完备;锁定版本 |
|
||||
| AI API 延迟 / 不可用 | BFF 层缓存 + 降级策略;关键路径设置超时(详见 ADR-0002) |
|
||||
| AI API 调用成本 | 缓存相似查询;Prompt 压缩优化;使用 cheaper 模型做预处理 |
|
||||
| pgvector HNSW 索引构建耗时 | 配方数据量级可控(万级),索引构建秒级 |
|
||||
|
||||
### 需要关注但未在本次决议的
|
||||
|
||||
- CI/CD 流水线(GitHub Actions / GitLab CI)
|
||||
- 监控和日志(Sentry + Grafana + Loki)
|
||||
- 认证方案(JWT + OAuth2 / LDAP 企业对接)
|
||||
- 测试框架细节(在实现阶段决策)
|
||||
|
||||
---
|
||||
|
||||
## 参考
|
||||
|
||||
- PRD: `.scratch/formula-rd-platform/PRD.md`
|
||||
- CSS Color Level 4: https://www.w3.org/TR/css-color-4/
|
||||
- color.js: https://github.com/color-js/color.js
|
||||
- pgvector: https://github.com/pgvector/pgvector
|
||||
- 所有用户必须熟悉 React + Fastify + Prisma 三件套
|
||||
- 数据库变更必须通过 Prisma Migrate,不可手动修改 Schema
|
||||
- OpenAPI spec 作为服务契约,前后端类型同步
|
||||
- 本地开发需 Docker 运行 PostgreSQL(pgvector 扩展)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# ADR-0002: AI 能力通过外部 API 调用实现
|
||||
|
||||
> **状态**: 已决议
|
||||
> **日期**: 2026-05-20
|
||||
> **父决策**: ADR-0001(整体技术栈)
|
||||
> **状态**: 已决议(2026-05-21 修订)
|
||||
> **日期**: 2026-05-20
|
||||
> **修订**: 2026-05-21
|
||||
> **父决策**: ADR-0001(整体技术栈)
|
||||
> **决策者**: 架构评审
|
||||
|
||||
---
|
||||
@@ -46,221 +47,56 @@
|
||||
|
||||
---
|
||||
|
||||
## BFF 层 AI 调用架构
|
||||
## AI Service 架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Fastify BFF │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ AI Service Module │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │ │
|
||||
│ │ │ Cache │ │ Rate │ │ Fallback │ │ │
|
||||
│ │ │ Layer │ │ Limiter │ │ Handler │ │ │
|
||||
│ │ └──────────┘ └──────────┘ └───────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌────────────────────────────────────────────┐ │ │
|
||||
│ │ │ Prompt Templates (per capability) │ │ │
|
||||
│ │ │ - predictFormulaMetrics │ │ │
|
||||
│ │ │ - parseNLQuery │ │ │
|
||||
│ │ │ - generateFormulaOptions │ │ │
|
||||
│ │ │ - recommendColorants │ │ │
|
||||
│ │ │ - extractFormulaStructure │ │ │
|
||||
│ │ └────────────────────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌────────────────────────────────────────────┐ │ │
|
||||
│ │ │ AI API Client │ │ │
|
||||
│ │ │ - streaming (SSE proxy) │ │ │
|
||||
│ │ │ - retry with backoff │ │ │
|
||||
│ │ │ - timeout (30s default, 120s streaming) │ │ │
|
||||
│ │ └────────────────────────────────────────────┘ │ │
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
Route Layer (modules/ai/ai.route.ts)
|
||||
│
|
||||
▼
|
||||
AIService (services/ai/index.ts)
|
||||
├── Provider 抽象层
|
||||
│ ├── createOpenAIProvider (GPT-4o, base: api.openai.com/v1)
|
||||
│ └── createDeepSeekProvider (deepseek-chat, base: api.deepseek.com/v1)
|
||||
├── LRUCache (200 条, 带 TTL)
|
||||
├── RateLimiter (10 req/s, token bucket)
|
||||
├── 重试策略 (指数退避, 最多 3 次)
|
||||
├── Fallback → Mock 模式 (AI_MOCK=true 时返回预设数据)
|
||||
└── Audit Log → ai_audit_logs 表 (capability, model, tokens, duration)
|
||||
```
|
||||
|
||||
### 核心模块
|
||||
|
||||
| 模块 | 职责 |
|
||||
| :--- | :--- |
|
||||
| **Prompt Templates** | 每种 AI 能力对应独立的 Prompt 模板,包含 system prompt + 结构化输出指令 |
|
||||
| **Cache Layer** | LRU 缓存相同/相似查询的 AI 响应(基于 query hash),TTL 5min~1h |
|
||||
| **Rate Limiter** | 令牌桶 + 并发控制,保护 API 配额 |
|
||||
| **Fallback Handler** | API 不可用时返回缓存结果或友好降级提示 |
|
||||
| **AI API Client** | 统一的 HTTP 客户端,处理 streaming、重试、超时 |
|
||||
> **修订(2026-05-21)**:AI Service 重构为实例属性管理配置(openaiKey/deepseekKey),不再依赖 `process.env` 直读。Provider 通过 `reload(updates)` 热更新。
|
||||
|
||||
---
|
||||
|
||||
## 各 AI 能力的 API 调用策略
|
||||
## AI 能力清单
|
||||
|
||||
### 1. 配方指标预测
|
||||
|
||||
```
|
||||
POST /api/ai/predict
|
||||
|
||||
BFF 流程:
|
||||
1. 从 PostgreSQL 检索该成分组合的历史相似配方(pgvector 向量搜索)
|
||||
2. 构建 Prompt:
|
||||
- System: 化妆品配方专家角色 + 指标定义
|
||||
- Context: 历史相似配方的指标数据(few-shot examples)
|
||||
- User: 当前配方的成分列表和比例
|
||||
3. 调用 AI API(非 streaming,预期 < 5s)
|
||||
4. 缓存结果(key: 成分+比例 hash)
|
||||
```
|
||||
|
||||
### 2. NL 搜索解析
|
||||
|
||||
```
|
||||
POST /api/formulas/search?q=不含酒精的高保湿精华
|
||||
|
||||
BFF 流程:
|
||||
1. 调用 AI API 将 NL 转为结构化查询:
|
||||
{
|
||||
"filters": { "exclude_ingredients": ["alcohol", "ethanol"], "category": "精华液" },
|
||||
"vector_query": "高保湿、补水、滋润配方",
|
||||
"sort": "保湿指数 DESC"
|
||||
}
|
||||
2. filters → PostgreSQL WHERE 子句
|
||||
3. vector_query → pgvector embedding → HNSW 相似搜索
|
||||
4. 合并结果返回
|
||||
```
|
||||
|
||||
### 3. 配方推演
|
||||
|
||||
```
|
||||
POST /api/ai/explore
|
||||
|
||||
BFF 流程:
|
||||
1. 用户设置约束(目标成本、保留成分、禁止成分、目标指标)
|
||||
2. 检索当前配方 + 相关历史配方作为 context
|
||||
3. 构建 Prompt → 调用 AI API(streaming 模式)
|
||||
4. BFF 转发 SSE 流到前端,逐步展示生成的候选方案
|
||||
5. 每个候选方案附带置信度和变更说明
|
||||
```
|
||||
|
||||
### 4. 颜色推荐
|
||||
|
||||
```
|
||||
POST /api/color/recommend
|
||||
|
||||
BFF 流程:
|
||||
1. 前端传入目标颜色 Lab 值
|
||||
2. 在 PostgreSQL 中检索 ΔE < 3.0 的历史颜色配方(pgvector)
|
||||
3. 将目标色 + 最近匹配配方作为 context,调用 AI API 推荐色浆组合
|
||||
4. 返回推荐色浆 + 预测比例 + 预测 ΔE
|
||||
```
|
||||
|
||||
### 5. 配方结构化提取
|
||||
|
||||
```
|
||||
POST /api/formulas/extract
|
||||
|
||||
BFF 流程:
|
||||
1. 用户粘贴配方文本(或上传 Excel)
|
||||
2. 调用 AI API with function calling / structured output
|
||||
3. 提取:成分 INCI 名、中文名、比例、所属相、工艺备注
|
||||
4. 与成分目录(ingredients 表)模糊匹配校验
|
||||
5. 返回结构化 JSON,前端展示确认
|
||||
```
|
||||
| Prompt 模板 | 能力 | System Prompt 角色 | 输出格式 |
|
||||
|-------------|------|--------------------|----------|
|
||||
| `predictMetricsPrompt` | 预测配方指标 | 资深化妆品配方工程师 | `{sensoryIndex, stabilityScore, costEstimate, confidence, reasoning}` |
|
||||
| `parseNLQueryPrompt` | 自然语言搜索 | 查询解析器 | `{filters, keywords, vectorQuery}` |
|
||||
| `generateFormulaPrompt` | 配方推演 | 资深化妆品配方工程师 | `[{name, changes, predictedMetrics, reasoning}]` |
|
||||
| `recommendColorantsPrompt` | 配色推荐 | 化妆品色彩专家 | `{recommendations: [{colorants, predictedDeltaE, confidence}]}` |
|
||||
| `extractFormulaPrompt` | 配方文本提取 | 数据结构化提取 | `{ingredients: [{inciName, chineseName, percentage, phase, processNotes}]}` |
|
||||
|
||||
---
|
||||
|
||||
## AI API 选型
|
||||
## 配置管理
|
||||
|
||||
### 主选
|
||||
| 配置项 | 存储位置 | 说明 |
|
||||
|--------|----------|------|
|
||||
| `AI_MOCK` | `runtime/config.json` | Mock 模式开关(开发环境默认 true) |
|
||||
| `OPENAI_API_KEY` | `runtime/config.json` | 服务器端持久化,客户端不透传 |
|
||||
| `DEEPSEEK_API_KEY` | `runtime/config.json` | 同上 |
|
||||
| `OPENAI_BASE_URL` | `runtime/config.json` | 自定义 endpoint(如 API 代理) |
|
||||
| `DEEPSEEK_BASE_URL` | `runtime/config.json` | 同上 |
|
||||
|
||||
| API | 优势 | 适用场景 |
|
||||
| :--- | :--- | :--- |
|
||||
| **OpenAI GPT-4o / GPT-4.1** | 推理能力最强;structured output 原生;streaming 稳定 | 配方推演、NL 解析、结构化提取 |
|
||||
| **Anthropic Claude 4** | 长上下文(200K);化工领域知识强 | 配方生成(需大量 context)、复杂推理 |
|
||||
| **DeepSeek V3** | 性价比高;中文能力强 | 指标预测(高频调用)、批量处理 |
|
||||
|
||||
### 推荐策略
|
||||
|
||||
| 场景 | 模型 | 理由 |
|
||||
| :--- | :--- | :--- |
|
||||
| 配方推演(流式,低频,高质量) | GPT-4o | streaming 体验最好,推理质量最高 |
|
||||
| 指标预测(非流式,高频,需快) | DeepSeek V3 | 便宜、快、中文好 |
|
||||
| NL 搜索解析(高频,需结构化输出) | GPT-4o-mini / DeepSeek V3 | 便宜 + function calling |
|
||||
| 配方结构化提取(批量,需准确) | GPT-4o | structured output 精度最高 |
|
||||
| 颜色推荐(低频,需领域知识) | GPT-4o | 需要强推理 |
|
||||
|
||||
### API 配置抽象
|
||||
|
||||
```typescript
|
||||
// BFF 层多 provider 抽象
|
||||
interface AIProvider {
|
||||
chat(messages: Message[], options: ChatOptions): Promise<ChatResponse>;
|
||||
chatStream(messages: Message[], options: ChatOptions): AsyncIterable<ChatChunk>;
|
||||
}
|
||||
|
||||
const providers: Record<string, AIProvider> = {
|
||||
openai: new OpenAIProvider({ apiKey: env.OPENAI_API_KEY }),
|
||||
deepseek: new DeepSeekProvider({ apiKey: env.DEEPSEEK_API_KEY }),
|
||||
// 预留其他 provider
|
||||
};
|
||||
```
|
||||
|
||||
所有 AI 调用通过统一的 `AIService` 模块,根据场景路由到对应 provider,上层业务不感知具体模型。
|
||||
|
||||
---
|
||||
|
||||
## 降级策略
|
||||
|
||||
| 场景 | 降级行为 |
|
||||
| :--- | :--- |
|
||||
| AI API 超时(5s) | 指标预测:返回"无法预测,请手动评估";NL 搜索:降级为基础关键词搜索 |
|
||||
| AI API 不可用(连续失败) | 全部能力降级为提示模式,告知用户"AI 服务暂不可用" |
|
||||
| 配额耗尽 | 限流 + 排队;高频能力(指标预测)优先缓存命中 |
|
||||
|
||||
---
|
||||
|
||||
## 缓存策略
|
||||
|
||||
| 缓存内容 | TTL | Key |
|
||||
| :--- | :--- | :--- |
|
||||
| 指标预测结果 | 1 小时 | 成分列表 + 比例的 hash |
|
||||
| NL 搜索解析 | 5 分钟 | 原始查询文本 hash |
|
||||
| 颜色推荐 | 30 分钟 | 目标 Lab 值 + 允许 ΔE |
|
||||
| 成分结构化提取 | 永久(除非成分库更新) | 原料名称 hash |
|
||||
|
||||
使用 Redis 存储。BFF 启动时无需依赖 Redis,降级为内存 LRU 缓存。
|
||||
|
||||
---
|
||||
|
||||
## 安全考虑
|
||||
|
||||
- **API Key 管理**:环境变量注入(非代码硬编码),支持 vault/secret manager
|
||||
- **数据脱敏**:发送给 AI API 的 prompt 不包含公司敏感配方全量数据,仅发送 necesary context
|
||||
- **Prompt 注入防护**:用户输入(NL 搜索词)经过清洗后嵌入 prompt 模板
|
||||
- **审计日志**:所有 AI 调用记录(请求摘要、token 消耗、耗时)存储到 PostgreSQL audit 表
|
||||
> **修订(2026-05-21)**:API Key 从 localStorage 传输改为服务器端文件持久化。前端 SettingsPage 仅显示"已配置/未配置"状态,不展示 Key 内容。
|
||||
|
||||
---
|
||||
|
||||
## 后果
|
||||
|
||||
### 正向
|
||||
|
||||
- 零 ML 基础设施投入,开发周期缩短 50%+
|
||||
- 部署仅需 4 个服务,运维复杂度极低
|
||||
- 模型能力随 API 升级自动提升,无迁移成本
|
||||
- 成本可控:按调用量付费,低用量时几乎为零
|
||||
- BFF 层可独立开发和测试(mock AI 响应)
|
||||
|
||||
### 风险和缓解
|
||||
|
||||
| 风险 | 缓解 |
|
||||
| :--- | :--- |
|
||||
| API 延迟影响用户体验 | 缓存 + streaming + 降级;预测 API 设置 5s 超时 |
|
||||
| 外部 API 数据隐私 | Prompt 中不发送完整配方;仅发送必要上下文 |
|
||||
| 供应商锁定 | 多 provider 抽象层;标准化 prompt 模板可跨模型复用 |
|
||||
| LLM 幻觉(生成不合理配方) | 结果后处理校验(比例总和 100%、成分存在性检查) |
|
||||
|
||||
---
|
||||
|
||||
## 参考
|
||||
|
||||
- ADR-0001: 整体技术栈选型
|
||||
- PRD: `.scratch/formula-rd-platform/PRD.md`
|
||||
- OpenAI Structured Outputs: https://platform.openai.com/docs/guides/structured-outputs
|
||||
- pgvector: https://github.com/pgvector/pgvector
|
||||
- 依赖外部 API 服务可用性(OpenAI / DeepSeek),API 不可用时自动降级为 Mock 模式
|
||||
- AI 响应格式严格约束为 JSON,前端不解析自然语言输出
|
||||
- 每次 AI 调用记录审计日志,用于成本核算和问题排查
|
||||
- 新增 AI Provider 只需实现 `AIProvider` 接口(`chat` + `chatStream`)
|
||||
|
||||
96
docs/adr/0003-four-layer-module-architecture.md
Normal file
96
docs/adr/0003-four-layer-module-architecture.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# ADR-0003: 后端四层模块化架构
|
||||
|
||||
> **状态**: 已决议
|
||||
> **日期**: 2026-05-21
|
||||
> **父决策**: ADR-0001(整体技术栈)
|
||||
> **决策者**: 架构评审
|
||||
|
||||
---
|
||||
|
||||
## 上下文
|
||||
|
||||
项目初期路由层直接操作 Prisma,业务逻辑与 HTTP 适配耦合。随着模块增长(8 个领域模块,29 个 API 端点),缺乏分层导致:
|
||||
|
||||
- 业务逻辑无法脱离 HTTP 环境做单元测试
|
||||
- Prisma 调用散落在 8 个路由文件中,查询逻辑难以复用
|
||||
- 横切关注点(日志、审计、错误处理)没有统一注入点
|
||||
- 单文件超长(formulas 路由 295 行)难以维护
|
||||
|
||||
需对后端架构做分层设计,支持企业级扩展。
|
||||
|
||||
---
|
||||
|
||||
## 决策
|
||||
|
||||
**选择四层模块化架构**:Route → Service → Repository → Prisma,按领域模块聚合文件。
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── modules/
|
||||
│ ├── formulas/
|
||||
│ │ ├── formulas.route.ts # Fastify 路由注册 + 参数提取
|
||||
│ │ ├── formulas.service.ts # 纯业务逻辑 + 审计埋点
|
||||
│ │ ├── formulas.repository.ts # Prisma 数据访问
|
||||
│ │ ├── formulas.schema.ts # Zod 验证 schema
|
||||
│ │ └── formulas.test.ts # 集成测试
|
||||
│ ├── ingredients/ # 同上
|
||||
│ ├── projects/ # 同上
|
||||
│ ├── color/ # 同上
|
||||
│ ├── ai/ # 同上
|
||||
│ ├── auth/ # 同上
|
||||
│ ├── config/ # 同上
|
||||
│ └── health/ # 同上
|
||||
└── shared/ # 跨模块共享
|
||||
├── errors/ # AppError 异常体系
|
||||
├── logging/ # AsyncLocalStorage 上下文
|
||||
├── middleware/ # RBAC / Ownership 中间件
|
||||
├── metrics/ # Prometheus 指标
|
||||
└── audit/ # 审计服务
|
||||
```
|
||||
|
||||
### 对比方案
|
||||
|
||||
| 方案 | 优势 | 劣势 | 结论 |
|
||||
|------|------|------|------|
|
||||
| **四层模块化(选)** | 按领域聚合,改动一个功能不需跨目录跳转;Service/Repository 可独立单元测试;横切关注点通过 shared/ 统一注入 | 小模块(如 health)文件数多 | ✅ |
|
||||
| 三层(Route→Service→Prisma) | 简单直接 | Service 与持久化耦合,不含 Repository 则 Prisma mock 困难 | ❌ |
|
||||
| 按层分目录(routes/ / services/ / repositories/) | 层边界清晰 | 同功能文件分散在 4 个目录,开发时频繁切换 | ❌ |
|
||||
| Clean/六边形 | 核心领域零框架依赖 | 过度工程化;当前仅 Web 端,无需端口-适配器抽象 | ❌ |
|
||||
|
||||
---
|
||||
|
||||
## 层职责
|
||||
|
||||
| 层 | 职责 | 依赖 | 测试方式 |
|
||||
|----|------|------|----------|
|
||||
| **Route** | Fastify 注册、参数提取(req→纯数据)、preHandler 挂载 | Controller/Service + Zod | `app.inject()` 集成测试 |
|
||||
| **Service** | 纯业务逻辑、审计埋点、百分比验证 | Repository + AuditService | 单元测试(mock repository) |
|
||||
| **Repository** | Prisma 查询封装、事务管理 | Prisma | Testcontainers 集成测试 |
|
||||
| **Schema** | Zod 验证定义、TypeScript 类型导出 | Zod | 不需要测试(声明式) |
|
||||
|
||||
---
|
||||
|
||||
## 横切关注点
|
||||
|
||||
| 关注点 | 实现方式 | 注入点 |
|
||||
|--------|----------|--------|
|
||||
| **认证** | JWT preHandler(app.ts 全局) | onRequest |
|
||||
| **授权** | `requireRole()` / `requireFormulaOwnership()` | Route preHandler |
|
||||
| **错误处理** | AppError 子类(ValidationError/NotFoundError 等) | 全局 `setErrorHandler` |
|
||||
| **日志** | pino 结构化日志 + AsyncLocalStorage context | `app.log.child()` |
|
||||
| **审计** | AuditService(pino 输出,action/resource/userId) | Service 层显式调用 |
|
||||
| **指标** | prom-client(http_requests_total, app_errors_total, ai_requests_total) | 全局 handler / AI Service |
|
||||
| **输入校验** | `validateOrReply()` — Zod 解析,失败时 400 | Route 层 |
|
||||
| **API 文档** | `@fastify/swagger` + `zod-to-json-schema` | Route schema 定义 |
|
||||
|
||||
---
|
||||
|
||||
## 后果
|
||||
|
||||
- 新增模块需创建 4 个文件(route/service/repository/schema),模板明确
|
||||
- Service 和 Repository 可脱离 HTTP 环境做纯函数测试
|
||||
- 跨模块共享逻辑必须放在 `shared/` 下,不能在模块间直接 import
|
||||
- 所有错误必须使用 AppError 子类,不可裸抛 `new Error()`
|
||||
- 模块测试文件与源文件同目录,vitest `include: ['src/**/*.test.ts']` 自动发现
|
||||
96
docs/adr/0004-rbac-ownership-authorization.md
Normal file
96
docs/adr/0004-rbac-ownership-authorization.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# ADR-0004: RBAC + 资源级 Ownership 授权模型
|
||||
|
||||
> **状态**: 已决议
|
||||
> **日期**: 2026-05-21
|
||||
> **父决策**: ADR-0003(四层模块化架构)
|
||||
> **决策者**: 架构评审
|
||||
|
||||
---
|
||||
|
||||
## 上下文
|
||||
|
||||
平台需要访问控制,确保:
|
||||
- 敏感配置仅管理员可修改(AI Key、Mock 模式切换)
|
||||
- 配方工程师只能编辑/删除自己创建的配方
|
||||
- 成分目录等共享资源允许所有认证用户操作
|
||||
|
||||
考虑三种授权模型:
|
||||
|
||||
---
|
||||
|
||||
## 决策
|
||||
|
||||
**选择 RBAC + 资源级 Ownership**:角色控制操作类型,所有权控制资源访问粒度。
|
||||
|
||||
### 角色定义
|
||||
|
||||
| 角色 | 英文 | 能力 |
|
||||
|------|------|------|
|
||||
| 管理员 | admin | 管理配置、管理用户、管理所有配方/成分/项目 |
|
||||
| 配方工程师 | engineer | 创建配方、查看/使用成分目录、使用 AI 推演和颜色引擎 |
|
||||
|
||||
### 权限矩阵
|
||||
|
||||
| 操作 | admin | engineer |
|
||||
|------|-------|----------|
|
||||
| 管理 AI 配置 (`/api/config`) | ✓ | ✗ |
|
||||
| 管理成分目录(增删改) | ✓ | ✓ |
|
||||
| 查看成分目录 | ✓ | ✓ |
|
||||
| 创建/编辑/删除**自己的**配方 | ✓ | ✓ |
|
||||
| 编辑/删除**他人的**配方 | ✓ | ✗ |
|
||||
| 创建配方 | ✓ | ✓ |
|
||||
| 查看所有配方 | ✓ | ✓ |
|
||||
| 颜色引擎 / AI 推演 | ✓ | ✓ |
|
||||
| 管理项目 | ✓ | ✓ |
|
||||
| 查看项目 | ✓ | ✓ |
|
||||
|
||||
### 中间件实现
|
||||
|
||||
```typescript
|
||||
// 角色检查 — 用于配置等管理接口
|
||||
app.put('/', { preHandler: [requireRole('admin')] }, updateConfigHandler)
|
||||
|
||||
// 所有权检查 — 用于配方写操作
|
||||
app.put('/:id', { preHandler: [requireFormulaOwnership()] }, updateFormula)
|
||||
app.delete('/:id', { preHandler: [requireFormulaOwnership()] }, deleteFormula)
|
||||
```
|
||||
|
||||
`requireFormulaOwnership()` 内部逻辑:
|
||||
1. 查询 Formula.createdBy
|
||||
2. 查询当前 User.role
|
||||
3. admin → 放行
|
||||
4. engineer 且 createdBy === userId → 放行
|
||||
5. 否则 → throw ForbiddenError
|
||||
|
||||
---
|
||||
|
||||
### 对比方案
|
||||
|
||||
| 方案 | 优势 | 劣势 | 结论 |
|
||||
|------|------|------|------|
|
||||
| **RBAC + Ownership(选)** | 角色简单(2种);所有权检查与业务语义对齐;中间件声明式清晰 | 需要为每个资源类型编写 ownership 中间件 | ✅ |
|
||||
| 纯 RBAC(admin/formulator/viewer) | 无需资源级检查 | 无法表达"只能编辑自己的配方"(需要更多角色或 ABAC 条件) | ❌ |
|
||||
| ABAC(属性动态判断) | 最灵活 | Admin UI 复杂;规则定义成本高;当前 2 角色场景下过度 | ❌ |
|
||||
| 无授权(仅认证) | 最简单 | 任何认证用户可改 AI Key、删他人配方,不可接受 | ❌ |
|
||||
|
||||
---
|
||||
|
||||
## 认证机制
|
||||
|
||||
| 组件 | 实现 |
|
||||
|------|------|
|
||||
| Token 格式 | JWT HS256(HMAC-SHA256) |
|
||||
| 签名密钥 | `JWT_SECRET` 环境变量 |
|
||||
| 有效期 | 24 小时 |
|
||||
| 有效载荷 | `{ userId, exp }` |
|
||||
| 密码哈希 | scrypt(salt 16 字节,输出 64 字节,hex 编码) |
|
||||
| 密码比对 | `timingSafeEqual`(防时序攻击) |
|
||||
|
||||
---
|
||||
|
||||
## 后果
|
||||
|
||||
- 新增资源类型(如以后有独立的内容管理模块)需实现对应的 ownership 中间件
|
||||
- 认证中间件通过 `app.ts` 全局 `preHandler` 注册,公开路由通过 URL 前缀白名单跳过
|
||||
- 测试环境使用 `buildApp({ skipAuth: true })` 绕过认证,`request.userId` 默认为 `'system'`
|
||||
- 角色变更需修改 User.role 字段(当前通过数据库直改,未来需管理 UI)
|
||||
1076
docs/api-reference.md
Normal file
1076
docs/api-reference.md
Normal file
File diff suppressed because it is too large
Load Diff
522
docs/project-overview.md
Normal file
522
docs/project-overview.md
Normal file
@@ -0,0 +1,522 @@
|
||||
# 配方研发智能平台 — 项目全貌
|
||||
|
||||
> 最后更新 2026-05-21 | 版本 v0.1.0 | 企业级重构后
|
||||
|
||||
---
|
||||
|
||||
## 1. 项目是什么
|
||||
|
||||
AI 驱动的化妆品配方研发智能平台(纯 Web 端)。面向化妆品研发工程师,提供四大核心能力:
|
||||
|
||||
| 能力 | 说明 |
|
||||
|------|------|
|
||||
| 颜色管理 | CIELAB 色空间 + Display P3 广色域渲染 + AI 配色推荐 |
|
||||
| 可视化配方调整 | 拖拽交互 + 实时 AI 预测反馈 + ECharts 图表 |
|
||||
| 配方记录管理 | 结构化存储 + 版本管理 + 自然语言搜索 |
|
||||
| 配方推演 | 多方案并行优化 + Pareto 前沿 + 成本/功效约束 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 目录全貌
|
||||
|
||||
```
|
||||
color_full/
|
||||
│
|
||||
├── CONTEXT.md # 领域词汇表(纯业务术语)
|
||||
├── README.md
|
||||
├── AGENTS.md # Agent 配置
|
||||
│
|
||||
├── docs/
|
||||
│ ├── adr/
|
||||
│ │ ├── 0001-architecture-stack.md # 技术栈选型(React/Fastify/Prisma/...)
|
||||
│ │ ├── 0002-ai-api-strategy.md # AI 外部 API 调用策略
|
||||
│ │ ├── 0003-four-layer-module-architecture.md # 后端四层模块化架构
|
||||
│ │ └── 0004-rbac-ownership-authorization.md # RBAC + 资源级 Ownership
|
||||
│ ├── api-reference.md # API 接口文档(29 端点)
|
||||
│ ├── project-overview.md # 本文件
|
||||
│ └── agents/ # Agent skills 配置
|
||||
│
|
||||
├── backend/ # 后端(Fastify + TypeScript)
|
||||
│ ├── Dockerfile
|
||||
│ ├── package.json
|
||||
│ ├── tsconfig.json # strict + noUncheckedIndexedAccess
|
||||
│ ├── vitest.config.ts
|
||||
│ ├── eslint.config.js
|
||||
│ ├── .prettierrc
|
||||
│ ├── .env.example # 环境变量模板
|
||||
│ ├── runtime/
|
||||
│ │ └── config.json # 运行时 AI Key 持久化
|
||||
│ ├── scripts/
|
||||
│ │ └── generate-openapi.ts # OpenAPI spec 生成
|
||||
│ ├── prisma/
|
||||
│ │ ├── schema.prisma # 数据模型定义(10 个模型)
|
||||
│ │ ├── seed.ts
|
||||
│ │ └── migrations/
|
||||
│ └── src/
|
||||
│ ├── app.ts # ★ 应用入口:插件注册 + 全局中间件
|
||||
│ ├── server.ts # ★ 启动 + 优雅关闭
|
||||
│ ├── lib/ # 工具库
|
||||
│ │ ├── prisma.ts # Prisma 客户端
|
||||
│ │ ├── configStore.ts # 服务器端配置持久化
|
||||
│ │ ├── validate.ts # Zod → Fastify 校验桥接
|
||||
│ │ └── swagger.ts # Zod → JSON Schema 转换
|
||||
│ ├── shared/ # ★ 跨模块共享基础设施
|
||||
│ │ ├── errors/
|
||||
│ │ │ ├── app-error.ts # AppError 6 子类体系
|
||||
│ │ │ └── codes.ts # 30+ 错误码常量
|
||||
│ │ ├── logging/
|
||||
│ │ │ └── context.ts # AsyncLocalStorage 请求上下文
|
||||
│ │ ├── middleware/
|
||||
│ │ │ ├── rbac.ts # requireRole('admin')
|
||||
│ │ │ └── ownership.ts # requireFormulaOwnership()
|
||||
│ │ ├── metrics/
|
||||
│ │ │ └── metrics.ts # prom-client 5 指标
|
||||
│ │ └── audit/
|
||||
│ │ └── audit.service.ts # 结构化审计日志
|
||||
│ ├── services/ # 核心服务
|
||||
│ │ └── ai/
|
||||
│ │ ├── index.ts # ★ AIService(Provider 抽象 + 缓存 + 限流 + 回退)
|
||||
│ │ ├── cache.ts # LRUCache
|
||||
│ │ ├── rate-limiter.ts # Token Bucket
|
||||
│ │ ├── audit.ts # AI 调用审计记录
|
||||
│ │ ├── providers/
|
||||
│ │ │ ├── types.ts # AIProvider 接口
|
||||
│ │ │ ├── openai.ts # OpenAI (GPT-4o)
|
||||
│ │ │ └── deepseek.ts # DeepSeek (deepseek-chat)
|
||||
│ │ └── templates/
|
||||
│ │ └── index.ts # 5 个 Prompt 模板
|
||||
│ ├── modules/ # ★ 业务模块(四层架构)
|
||||
│ │ ├── auth/ # 认证(注册/登录/JWT)
|
||||
│ │ ├── ingredients/ # 成分目录(route + service + repository + test)
|
||||
│ │ ├── formulas/ # 配方记录(route + service + repository + test)
|
||||
│ │ ├── color/ # 颜色引擎(推荐/匹配/保存)
|
||||
│ │ ├── ai/ # AI 推演(预测/探索/提取/搜索, SSE 流)
|
||||
│ │ ├── projects/ # 项目管理
|
||||
│ │ ├── config/ # 配置管理(AI Key/admin only)
|
||||
│ │ └── health/ # 健康检查(live/ready + /metrics)
|
||||
│ └── generated/ # Prisma 自动生成
|
||||
│
|
||||
├── frontend/ # 前端(React 19 + Vite 8 + TypeScript)
|
||||
│ ├── Dockerfile
|
||||
│ ├── package.json
|
||||
│ ├── vite.config.ts
|
||||
│ ├── vitest.config.ts
|
||||
│ ├── eslint.config.js
|
||||
│ ├── tsconfig.json / tsconfig.app.json / tsconfig.node.json
|
||||
│ ├── index.html
|
||||
│ ├── nginx.conf # 生产 Nginx 配置
|
||||
│ ├── scripts/
|
||||
│ │ └── generate-types.ts # OpenAPI → TypeScript 类型生成
|
||||
│ └── src/
|
||||
│ ├── main.tsx # ★ 入口:ToastProvider + QueryClient + Router
|
||||
│ ├── App.tsx
|
||||
│ ├── router.tsx # React Router v7 配置
|
||||
│ ├── index.css # Tailwind CSS 4 入口
|
||||
│ ├── lib/
|
||||
│ │ ├── api.ts # apiFetch 封装 + Auth header
|
||||
│ │ ├── queryClient.ts # TanStack Query 配置
|
||||
│ │ └── color/ # 色彩科学工具
|
||||
│ │ ├── convert.ts # 色空间转换(Lab/Hex/RGB/LCH/P3)
|
||||
│ │ ├── deltaE.ts # ΔE 2000/CMC/76 计算
|
||||
│ │ ├── types.ts # 颜色类型定义
|
||||
│ │ └── color.test.ts # 颜色工具测试
|
||||
│ ├── shared/ # ★ 共享 UI 基建
|
||||
│ │ ├── components/
|
||||
│ │ │ ├── Toast.tsx # Toast 通知系统(Provider + Hook)
|
||||
│ │ │ ├── Skeleton.tsx # 骨架屏(单行/多行/页面级)
|
||||
│ │ │ └── Alert.tsx # 警告提示(4 种变体)
|
||||
│ │ └── services/
|
||||
│ │ └── api.ts # 统一 API 客户端(带 Auth Token)
|
||||
│ ├── modules/ # 前端模块
|
||||
│ │ └── formulas/
|
||||
│ │ └── formulas.service.ts # 配方 Service 层(TanStack Query 就绪)
|
||||
│ ├── pages/ # 页面组件(14 个)
|
||||
│ │ ├── DashboardPage.tsx # 仪表盘
|
||||
│ │ ├── FormulaListPage.tsx # 配方列表
|
||||
│ │ ├── FormulaDetailPage.tsx # 配方详情(含可视化编辑器)
|
||||
│ │ ├── FormulaEditorPage.tsx # 配方编辑器
|
||||
│ │ ├── FormulaExplorerPage.tsx # 配方推演
|
||||
│ │ ├── VersionHistoryPage.tsx # 版本历史
|
||||
│ │ ├── VersionComparePage.tsx # 版本对比
|
||||
│ │ ├── ColorLabPage.tsx # 颜色实验室
|
||||
│ │ ├── IngredientsPage.tsx # 成分目录
|
||||
│ │ ├── ProjectsPage.tsx # 项目管理
|
||||
│ │ ├── SettingsPage.tsx # 设置(外观 + AI 配置)
|
||||
│ │ ├── SearchPage.tsx # AI 搜索
|
||||
│ │ ├── LoginPage.tsx # 登录
|
||||
│ │ └── RegisterPage.tsx # 注册
|
||||
│ ├── components/ # 共享组件
|
||||
│ │ ├── AuthGuard.tsx # 路由守卫
|
||||
│ │ ├── ErrorBoundary.tsx # 错误边界
|
||||
│ │ ├── ColorWheel.tsx # 色轮 Canvas
|
||||
│ │ ├── EyedropperPanel.tsx # 取色棒
|
||||
│ │ ├── ColorRecommendPanel.tsx # AI 配色推荐弹窗
|
||||
│ │ └── FormulaVisualEditor.tsx # 配方可视化编辑器
|
||||
│ ├── layouts/
|
||||
│ │ └── AppLayout.tsx # 主布局(侧栏 + 顶栏)
|
||||
│ ├── hooks/
|
||||
│ │ └── useAIPredict.ts # AI 预测 Hook
|
||||
│ └── stores/
|
||||
│ ├── authStore.ts # 认证状态(Zustand)
|
||||
│ └── themeStore.ts # 主题状态(Zustand)
|
||||
│
|
||||
├── docker/
|
||||
│ └── Dockerfile.pgvector # PostgreSQL + pgvector 镜像
|
||||
├── docker-compose.yml # 开发环境(PostgreSQL + MinIO[可选])
|
||||
├── docker-compose.prod.yml # 生产环境(Traefik + PostgreSQL + Backend + Frontend)
|
||||
└── scripts/
|
||||
├── backup-db.sh # pg_dump 备份脚本(7 天留存)
|
||||
└── init-db.sh # 数据库初始化
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 架构全景
|
||||
|
||||
### 3.1 后端:四层模块化
|
||||
|
||||
```
|
||||
HTTP Request
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ app.ts (Fastify) │
|
||||
│ ├── Helmet + CORS + RateLimit │
|
||||
│ ├── global preHandler: JWT verify │
|
||||
│ ├── global setErrorHandler: AppError → HTTP │
|
||||
│ └── /docs (Swagger UI) + /api/* routes │
|
||||
└────────────┬─────────────────────────────────┘
|
||||
│
|
||||
┌────────▼────────┐
|
||||
│ Route Layer │ ← 参数提取 (req → 纯数据) + Zod 校验
|
||||
│ *.route.ts │ preHandler: requireRole() / requireFormulaOwnership()
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────────▼────────┐
|
||||
│ Service Layer │ ← 纯业务逻辑 + 审计埋点 + 百分比验证
|
||||
│ *.service.ts │ 依赖 Repository + AuditService
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────────▼────────┐
|
||||
│ Repository │ ← Prisma 查询封装 + 事务管理
|
||||
│ Layer │ 每个模块独立 Repository
|
||||
│ *.repository.ts│
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────────▼────────┐
|
||||
│ Prisma ORM │
|
||||
│ PostgreSQL │
|
||||
│ + pgvector │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 横切关注点注入矩阵
|
||||
|
||||
| 关注点 | 注入方式 | 注入位置 |
|
||||
|--------|----------|----------|
|
||||
| 请求 ID | `genReqId: () => randomUUID()` | app.ts 构造 |
|
||||
| 认证 | `addHook('preHandler')` → verifyToken → set userId | app.ts 全局 |
|
||||
| 授权 | `{ preHandler: [requireRole(), requireFormulaOwnership()] }` | Route 注册 |
|
||||
| 输入校验 | `validateOrReply(zodSchema, data, reply)` | Route handler |
|
||||
| 错误处理 | `setErrorHandler(error, request, reply)` → AppError 子类匹配 | app.ts 全局 |
|
||||
| 结构化日志 | `request.log.child({ requestId })` | app.ts onRequest |
|
||||
| 审计日志 | `auditService.log({ action, resource, userId })` | Service 层显式调 |
|
||||
| API 文档 | `@fastify/swagger` + `routeSchema()` | Route 注册 |
|
||||
| Prometheus | `app_errors_total.inc()` / `/api/metrics` 端点 | 全局 handler + health 模块 |
|
||||
| HTTP 安全 | `@fastify/helmet` + `@fastify/rate-limit` | app.ts 插件注册 |
|
||||
|
||||
### 3.3 前端:渐进式分层
|
||||
|
||||
```
|
||||
React Router
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────┐
|
||||
│ Page 组件 │ ← UI 渲染 + 调用 Hooks
|
||||
│ (pages/) │
|
||||
└────────────┬─────────────┘
|
||||
│
|
||||
┌────────▼────────────┐
|
||||
│ Hooks │ ← useQuery / useMutation (TanStack Query)
|
||||
│ + 页面级 State │ useReducer / useState
|
||||
└────────┬────────────┘
|
||||
│
|
||||
┌────────▼────────────┐
|
||||
│ Service 层 │ ← API 调用封装 (apiFetch)
|
||||
│ (modules/*.service) │ 类型安全的请求/响应
|
||||
└────────┬────────────┘
|
||||
│
|
||||
┌────────▼────────────┐
|
||||
│ 共享 UI 基建 │
|
||||
│ - ToastProvider │
|
||||
│ - Skeleton │
|
||||
│ - Alert │
|
||||
│ - ErrorBoundary │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
"当前状态:后端四层已全部落地,前端仅 formulaService 完成模板,其余 page 仍使用 `apiFetch` 直调。这是待完成的迁移动脉。"
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据模型
|
||||
|
||||
```
|
||||
User ────1:N──→ Formula ────1:N──→ FormulaVersion ────1:N──→ Phase
|
||||
│ │ │ │
|
||||
│ │ │ 1:N │
|
||||
│ │ │ FormulaIngredient ────N:1──→ Ingredient
|
||||
│ │ │
|
||||
│ 1:N │ 1:N │
|
||||
├─────→ ColorFormula Phase (via formulaId on Phase)
|
||||
│
|
||||
└─────→ Project ────1:N──→ Formula
|
||||
```
|
||||
|
||||
| 表 | 核心字段 | 说明 |
|
||||
|----|----------|------|
|
||||
| users | username(unique), passwordHash(scrypt), role(engineer/admin) | 用户 |
|
||||
| projects | name, description, createdBy | 项目 |
|
||||
| formulas | name, description, currentVersion, projectId, embedding(vector) | 配方 |
|
||||
| formula_versions | formulaId, versionNumber(unique pair), snapshotData(JSON), createdBy | 版本快照 |
|
||||
| phases | name, formulaId(→FormulaVersion), sortOrder | 工艺阶段 |
|
||||
| formula_ingredients | formulaVersionId, phaseId, ingredientId, percentage, processNotes | 成分关联 |
|
||||
| ingredients | inciName, chineseName, functionCategory(12枚举), supplier, unitPrice | 原料 |
|
||||
| color_formulas | name, targetLab(JSON), actualLab(JSON), deltaE, colorantComposition(JSON) | 颜色配方 |
|
||||
| ai_audit_logs | capability, modelName, promptHash, tokensUsed, durationMs | AI 调用审计 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 安全架构
|
||||
|
||||
### 5.1 认证流程
|
||||
|
||||
```
|
||||
POST /api/auth/register
|
||||
用户名 + 密码 → scrypt 异步哈希 → 存入 users.passwordHash
|
||||
返回 JWT Token (HS256, 24h)
|
||||
|
||||
POST /api/auth/login
|
||||
用户名 + 密码 → scrypt 异步比对 (timingSafeEqual)
|
||||
返回 JWT Token
|
||||
|
||||
每个业务请求:
|
||||
Authorization: Bearer <token>
|
||||
→ preHandler: 解码验证 → 查 DB 确认用户存在 → set request.userId
|
||||
```
|
||||
|
||||
### 5.2 授权矩阵
|
||||
|
||||
| 操作 | admin | engineer |
|
||||
|------|:-----:|:--------:|
|
||||
| 管理 AI 配置 | ✓ | ✗ |
|
||||
| 增删改成份目录 | ✓ | ✓ |
|
||||
| 创建配方 | ✓ | ✓ |
|
||||
| 编辑/删除**自己**的配方 | ✓ | ✓ |
|
||||
| 编辑/删除**他人**的配方 | ✓ | ✗ |
|
||||
| 颜色引擎 / AI 推演 | ✓ | ✓ |
|
||||
| 管理项目 | ✓ | ✓ |
|
||||
|
||||
### 5.3 安全层
|
||||
|
||||
| 层 | 工具 | 作用 |
|
||||
|----|------|------|
|
||||
| HTTP 头 | `@fastify/helmet` | CSP/HSTS/X-Frame 等安全头 |
|
||||
| CORS | `@fastify/cors` | 仅允许 localhost:5173 |
|
||||
| 速率限制 | `@fastify/rate-limit` | 全局 100 req/min |
|
||||
| 密码 | scrypt(salt=16, output=64) + timingSafeEqual | 抗彩虹表 + 防时序攻击 |
|
||||
| Token | JWT HMAC-SHA256 (HS256) | 正确实现,非 SHA256 裸哈希 |
|
||||
| API Key | `runtime/config.json` 服务器端存储 | 不从 localStorage 传输 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 可观测性
|
||||
|
||||
### 6.1 健康探针
|
||||
|
||||
| 端点 | 用途 | 实现 |
|
||||
|------|------|------|
|
||||
| `/api/health` | 基础存活 | 返回 timestamp |
|
||||
| `/api/health/live` | K8s liveness | `SELECT 1` 检测 DB 连接,失败 503 |
|
||||
| `/api/health/ready` | K8s readiness | SIGTERM 后返回 503 |
|
||||
| `/api/metrics` | Prometheus scrape | 5 个指标 |
|
||||
|
||||
### 6.2 Prometheus 指标
|
||||
|
||||
| 指标 | 类型 | 标签 |
|
||||
|------|------|------|
|
||||
| `http_requests_total` | Counter | method, path, status |
|
||||
| `http_request_duration_ms` | Histogram | method, path |
|
||||
| `app_errors_total` | Counter | category, module, code |
|
||||
| `ai_requests_total` | Counter | capability, provider, status |
|
||||
| `ai_request_duration_ms` | Histogram | capability |
|
||||
|
||||
### 6.3 日志
|
||||
|
||||
- **框架**: pino(结构化 JSON)
|
||||
- **上下文**: 每个请求自动注入 `requestId`
|
||||
- **审计**: `auditService.log()` → `{ audit: true, action, resource, resourceId, userId }`
|
||||
- **格式**: 开发 `pino-pretty`,生产纯 JSON → Loki/ELK
|
||||
|
||||
---
|
||||
|
||||
## 7. 部署架构
|
||||
|
||||
### 7.1 开发环境
|
||||
|
||||
```bash
|
||||
docker compose up # PostgreSQL (pgvector)
|
||||
pnpm dev # backend (3001) + frontend (5173)
|
||||
```
|
||||
|
||||
MinIO 可选:`docker compose --profile full up`
|
||||
|
||||
### 7.2 生产环境
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────┐
|
||||
│ Traefik (80/443) │
|
||||
│ ├── /api/* → backend:3001 │
|
||||
│ └── /* → frontend:80 │
|
||||
├────────────────────────────────────────────┤
|
||||
│ Backend (Fastify, port 3001) │
|
||||
│ ├── /api/* 业务路由 │
|
||||
│ ├── /docs Swagger UI │
|
||||
│ ├── /api/health/live liveness probe │
|
||||
│ ├── /api/health/ready readiness probe │
|
||||
│ └── /api/metrics Prometheus scrape │
|
||||
├────────────────────────────────────────────┤
|
||||
│ Frontend (Nginx, port 80) │
|
||||
│ ├── SPA static files │
|
||||
│ └── /api/* → backend proxy │
|
||||
├────────────────────────────────────────────┤
|
||||
│ PostgreSQL (pgvector) │
|
||||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
### 7.3 备份
|
||||
|
||||
```bash
|
||||
# 手动备份
|
||||
./scripts/backup-db.sh
|
||||
|
||||
# crontab 每日备份
|
||||
0 2 * * * /opt/colorfull/scripts/backup-db.sh >> /var/log/colorfull-backup.log 2>&1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 质量保障
|
||||
|
||||
### 8.1 TypeScript 严格度
|
||||
|
||||
| 开关 | 后端 | 前端 |
|
||||
|------|:----:|:----:|
|
||||
| `strict: true` | ✓ | ✓ |
|
||||
| `noUncheckedIndexedAccess` | ✓ | ✓ |
|
||||
| `noUnusedLocals` | ✓ | ✓ |
|
||||
| `noUnusedParameters` | ✓ | ✓ |
|
||||
| `skipLibCheck` | ✓ | ✓ |
|
||||
|
||||
### 8.2 代码规范
|
||||
|
||||
| 工具 | 后端 | 前端 |
|
||||
|------|:----:|:----:|
|
||||
| ESLint | ✓ (`typescript-eslint`) | ✓ |
|
||||
| Prettier | ✓ (semi=false, singleQuote, trailingComma=all) | ✓ |
|
||||
| 状态 | **lint 零错误** | — |
|
||||
|
||||
### 8.3 测试
|
||||
|
||||
| 层 | 后端 | 前端 |
|
||||
|----|------|------|
|
||||
| 单元测试 | —(待补:Service 层) | `lib/color/color.test.ts`(色空间转换) |
|
||||
| 集成测试 | `ingredients.test.ts` + `formulas.test.ts`(26 用例) | —(待补:React Testing Library) |
|
||||
| E2E | —(待补:Playwright) | — |
|
||||
| 运行 | `pnpm test` (vitest) | `pnpm test` (vitest) |
|
||||
|
||||
### 8.4 命令速查
|
||||
|
||||
```bash
|
||||
# 后端
|
||||
cd backend
|
||||
pnpm dev # 启动开发服务器 (tsx watch)
|
||||
pnpm build # TypeScript 编译
|
||||
pnpm test # 运行 26 个集成测试
|
||||
pnpm lint # ESLint 检查
|
||||
pnpm format # Prettier 格式化
|
||||
pnpm db:migrate # 数据库迁移
|
||||
pnpm db:seed # 种子数据
|
||||
pnpm api:gen # 生成 OpenAPI spec
|
||||
|
||||
# 前端
|
||||
cd frontend
|
||||
pnpm dev # 启动 Vite 开发服务器
|
||||
pnpm build # 生产构建
|
||||
pnpm lint # ESLint
|
||||
pnpm api:gen # 从 OpenAPI spec 生成 TypeScript 类型
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. API 速览
|
||||
|
||||
| 模块 | 端点 | 方法 | 认证 | 说明 |
|
||||
|------|------|------|:----:|------|
|
||||
| health | `/api/health` | GET | ✗ | 基础存活 |
|
||||
| health | `/api/health/live` | GET | ✗ | DB 检查 |
|
||||
| health | `/api/health/ready` | GET | ✗ | 就绪检查 |
|
||||
| health | `/api/metrics` | GET | ✗ | Prometheus |
|
||||
| auth | `/api/auth/register` | POST | ✗ | 注册 |
|
||||
| auth | `/api/auth/login` | POST | ✗ | 登录 |
|
||||
| auth | `/api/auth/me` | GET | ✓ | 当前用户 |
|
||||
| ingredients | `/api/ingredients` | GET/POST | ✓ | 列表/创建 |
|
||||
| ingredients | `/api/ingredients/:id` | GET/PUT/DEL | ✓ | 详情/更新/删除 |
|
||||
| formulas | `/api/formulas` | GET/POST | ✓ | 列表/创建 |
|
||||
| formulas | `/api/formulas/:id` | GET/PUT/DEL | ✓ | 详情/更新/删除 |
|
||||
| formulas | `/api/formulas/:id/composition` | PUT | ✓ | 更新成分(新版本) |
|
||||
| color | `/api/color/recommend` | POST | ✓ | AI 配色推荐 |
|
||||
| color | `/api/color/formulas/match` | GET | ✓ | 颜色配方匹配 |
|
||||
| color | `/api/color/formulas` | POST | ✓ | 保存颜色配方 |
|
||||
| ai | `/api/ai/predict-formula` | POST | ✓ | 预测指标(SSE) |
|
||||
| ai | `/api/ai/explore-formula` | POST | ✓ | 配方推演(SSE) |
|
||||
| ai | `/api/ai/extract-formula` | POST | ✓ | 提取配方文本 |
|
||||
| ai | `/api/ai/search` | GET | ✓ | NL 搜索 |
|
||||
| projects | `/api/projects` | GET/POST | ✓ | 列表/创建 |
|
||||
| projects | `/api/projects/:id` | PUT/DEL | ✓ | 更新/删除 |
|
||||
| config | `/api/config` | GET/PUT | ✓ | 查看/更新配置(admin) |
|
||||
| config | `/api/config/test` | POST | ✓ | 测试 AI 连接(admin) |
|
||||
|
||||
---
|
||||
|
||||
## 10. 待完成清单
|
||||
|
||||
| 优先级 | 项目 | 状态 |
|
||||
|--------|------|:----:|
|
||||
| P1 | 前端页面逐批迁移到 Service 层 + TanStack Query | 仅 formulaService 完成 |
|
||||
| P1 | 前端集成 shadcn/ui 组件(替换手动创建的 Toast/Skeleton/Alert) | Tailwind 4 兼容性问题待解决 |
|
||||
| P2 | 后端 Service 层单元测试(mock Repository) | 待开始 |
|
||||
| P2 | 后端 Testcontainers 集成测试框架 | 待开始 |
|
||||
| P2 | Playwright E2E 关键流程测试 | 待开始 |
|
||||
| P2 | Husky pre-commit hooks(后端 + 前端) | 待配置 |
|
||||
| P3 | CI/CD 流水线(GitHub Actions / GitLab CI) | 待配置 |
|
||||
| P3 | 前端 OpenAPI 类型生成流水线验证 | 脚本已就绪,待端到端跑通 |
|
||||
| P3 | Admin 用户管理 UI | 直接操作 DB |
|
||||
|
||||
---
|
||||
|
||||
## 11. 相关文档索引
|
||||
|
||||
| 文档 | 路径 | 内容 |
|
||||
|------|------|------|
|
||||
| 领域词汇 | `CONTEXT.md` | 纯业务术语定义 |
|
||||
| 技术栈 ADR | `docs/adr/0001-architecture-stack.md` | 为什么选 React/Fastify/Prisma 等 |
|
||||
| AI 策略 ADR | `docs/adr/0002-ai-api-strategy.md` | 为什么用外部 LLM API |
|
||||
| 架构 ADR | `docs/adr/0003-four-layer-module-architecture.md` | 为什么四层模块化 |
|
||||
| 授权 ADR | `docs/adr/0004-rbac-ownership-authorization.md` | 为什么 RBAC + Ownership |
|
||||
| API 文档 | `docs/api-reference.md` | 29 个接口完整说明 |
|
||||
| PRD | `.scratch/formula-rd-platform/PRD.md` | 产品需求 |
|
||||
Reference in New Issue
Block a user