架构 - 后端从 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
24 KiB
24 KiB
配方研发智能平台 — 项目全貌
最后更新 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 开发环境
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) │
└────────────────────────────────────────────┘
docker compose -f docker-compose.prod.yml up -d
7.3 备份
# 手动备份
./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 命令速查
# 后端
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 |
产品需求 |