# P2 / P3 升级路线 本文档定义「本地单店专业工具」向稳定 SaaS 平台演进的落地路线。原则是:先不推翻现有 Node + 原生前端架构,而是先把任务、资产、配置三条核心数据流稳定下来;等 API 和测试稳定后,再逐步完成 TypeScript 化、CI/CD 和平台能力。 ## 总体顺序 ```text P2-A 任务中心 P2-B 配置引导 P2-C 历史资产库 P2-D 工程质量 P3-A 模板库 P3-B 审核流 P3-C 开放 API P3-D 模型市场 ``` 推荐按「数据层 → 服务层 → API → UI → 工程化」推进,而不是先大规模重写前端。这样可以保持当前产品可用,同时降低迁移风险。 --- ## P2-A 任务中心 ### 目标 让生成和采集任务具备持久化、可恢复、可观测、可重试的能力,解决刷新页面、重启服务、批量任务失败后状态不清楚的问题。 ### 数据模型 本地版先使用 SQLite;后续 SaaS 可平滑迁移到 PostgreSQL。 ```sql CREATE TABLE jobs ( id TEXT PRIMARY KEY, type TEXT NOT NULL, -- crawl | generation status TEXT NOT NULL, -- queued | submitted | running | succeeded | failed | canceled | timeout workspace_root TEXT NOT NULL, folder_name TEXT, shop_name TEXT, kind TEXT, model TEXT, prompt TEXT, prompt_version TEXT, provider TEXT, provider_task_id TEXT, request_hash TEXT, idempotency_key TEXT UNIQUE, progress TEXT, cost REAL, attempts INTEGER NOT NULL DEFAULT 0, max_attempts INTEGER NOT NULL DEFAULT 3, error_code TEXT, error_message TEXT, warning TEXT, created_at TEXT NOT NULL, started_at TEXT, finished_at TEXT, updated_at TEXT NOT NULL ); CREATE TABLE job_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, job_id TEXT NOT NULL, event TEXT NOT NULL, level TEXT NOT NULL, message TEXT, payload_json TEXT, created_at TEXT NOT NULL ); ``` ### 后端改动 1. 新增任务仓库模块: - `packages/jobs/repository.js` - `packages/jobs/queue.js` - `packages/jobs/provider-client.js` 2. 任务状态机固定为: ```text queued → submitted → running → succeeded ↘ failed / timeout / canceled ``` 3. 服务启动时: - 加载未完成任务; - 对有 `provider_task_id` 的任务继续轮询; - 对仍处于 `queued` 的任务重新入队; - 对超过最大重试次数的任务标记失败。 4. 任务创建时先写数据库,再提交供应商。 5. 供应商返回任务 ID 后立即更新 `provider_task_id`。 6. 增加统一接口: ```text GET /api/tasks GET /api/tasks/:id POST /api/tasks/:id/retry POST /api/tasks/:id/cancel DELETE /api/tasks/:id GET /api/tasks/stream ``` 7. `/api/tasks/stream` 使用 SSE 推送: - 任务新增; - 进度更新; - 状态变化; - 日志摘要。 ### 前端改动 1. 在侧边栏增加「任务中心」入口。 2. 顶栏增加全局任务状态图标: - 空闲; - N 个进行中; - 有失败; - 全部完成。 3. 任务中心支持: - 按类型筛选:全部、采集、生成; - 按状态筛选:排队、进行中、成功、失败; - 查看任务详情; - 重试; - 取消; - 删除记录; - 跳转到结果。 4. 生成页面保留轻量进度区,但复杂状态统一收敛到任务中心。 5. 页面刷新后自动恢复未完成任务。 ### 验收标准 - 服务重启后,未完成任务仍可见。 - 页面刷新后,正在生成的任务不丢失。 - 失败任务能看到错误原因,并能手动重试。 - 同一个任务的供应商任务 ID 可以追溯。 - 批量任务不会因为页面关闭而中断。 --- ## P2-B 配置引导 ### 目标 把「先配置成功,再使用产品」变成可视化流程,减少 API Key、Cookie、输出目录配置错误带来的隐性失败。 ### 引导流程 ```text 1. 欢迎 2. 配置 API Key 3. 测试生成服务 4. 配置输出目录 5. 配置采集 Cookie 6. 测试采集账号 7. 选择或扫描门店 8. 完成首次生成 ``` ### 后端接口 新增配置健康接口: ```text POST /api/config/test-api POST /api/config/test-workspace POST /api/config/test-crawl-cookie GET /api/config/status POST /api/config/onboarding-complete ``` `/api/config/status` 返回: ```json { "apiConfigured": true, "apiReachable": true, "workspaceReady": true, "workspaceWritable": true, "crawlCookieConfigured": true, "crawlCookieValid": true, "shopsReady": true, "firstGenerationCompleted": false, "onboardingCompleted": false } ``` ### 交互设计 1. 首次启动后弹出引导,不在首屏强制打断老用户。 2. 侧边栏显示 Setup 状态点。 3. 每个步骤包含: - 当前状态; - 填写入口; - 测试按钮; - 成功提示; - 错误原因; - 下一步。 4. 失败信息必须给出可操作建议,例如: - API Key 无效; - API 地址不可达; - 目录不可写; - Cookie 缺少 `ksid`; - 采集账号已过期。 5. 完成后写入本地 onboarding 状态,可在系统设置中重新打开。 ### 验收标准 - 新用户能在引导中完成首次可用配置。 - 每个配置项都有独立的健康状态。 - 配置错误不会只显示「失败」,而是能定位到具体原因。 - 用户完成引导后,首页能直接进入门店和生成流程。 --- ## P2-C 历史资产库 ### 目标 把「历史创作图片墙」升级为可管理的资产库,支持筛选、版本、收藏、删除、恢复和稳定下载。 ### 数据模型 ```sql CREATE TABLE assets ( id TEXT PRIMARY KEY, job_id TEXT, workspace_root TEXT NOT NULL, folder_name TEXT NOT NULL, shop_name TEXT NOT NULL, kind TEXT NOT NULL, name TEXT NOT NULL, status TEXT NOT NULL, -- generated | pending_review | approved | rejected | deleted local_path TEXT, object_key TEXT, remote_url TEXT, thumbnail_path TEXT, file_name TEXT NOT NULL, width INTEGER, height INTEGER, file_size_bytes INTEGER, checksum TEXT, model TEXT, prompt_version TEXT, cost REAL, version INTEGER NOT NULL DEFAULT 1, parent_asset_id TEXT, favorite INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE asset_versions ( id TEXT PRIMARY KEY, asset_group_id TEXT NOT NULL, version INTEGER NOT NULL, asset_id TEXT NOT NULL, created_at TEXT NOT NULL ); ``` ### 后端接口 ```text GET /api/assets GET /api/assets/:id PATCH /api/assets/:id POST /api/assets/:id/favorite POST /api/assets/:id/review POST /api/assets/:id/regenerate DELETE /api/assets/:id GET /api/assets/:id/download GET /api/assets/:id/thumbnail ``` 支持查询参数: ```text keyword shopName kind status favorite startDate endDate model page pageSize sort ``` ### 存储策略 本地版: - 保留本地文件作为主存储; - 数据库保存资产索引; - 生成缩略图; - 计算 checksum; - 删除默认进入回收站。 后续 SaaS: - 主存储迁移到 S3 / OSS / COS; - 本地路径仅作为缓存字段; - 下载使用短期签名 URL; - 历史记录不依赖供应商临时 URL。 ### 前端改动 1. 历史创作页升级为资产库。 2. 增加筛选栏: - 门店; - 类型; - 状态; - 收藏; - 时间; - 模型; - 关键词。 3. 图片墙继续默认一行 8 个,但大列表使用虚拟滚动。 4. 卡片悬浮操作: - 放大; - 下载; - 收藏; - 重新生成; - 删除。 5. 详情抽屉显示: - 大图; - 门店; - 类型; - 尺寸; - 模型; - 提示词版本; - 成本; - 创建时间; - 关联任务。 6. 增加回收站入口。 ### 验收标准 - 重复生成不会覆盖历史索引。 - 删除后可恢复。 - 支持按门店、类型、状态、收藏、时间筛选。 - 大量图片时页面不卡顿。 - 每个资产都能追溯生成任务和提示词版本。 --- ## P2-D 工程质量 ### 原则 不建议一次性重写成 TypeScript + 大型前端框架。推荐渐进式迁移: ```text 1. 先补测试和模块边界 2. 引入 ESLint / Prettier 3. 开启 TypeScript checkJs 4. 核心模块改为 .ts 5. 前端复杂度超过阈值后再引入 Vite + Vue/React ``` ### 第一阶段:模块拆分 目标不是马上引入框架,而是让 `server.js` 不再继续膨胀。 ```text src/ server.js config/ routes/ services/ jobs/ assets/ crawl/ providers/ image/ storage/ db/ shared/ ``` 拆分顺序: 1. `config`:读取、校验、默认值; 2. `db`:SQLite 连接和 migration; 3. `jobs`:任务状态机和队列; 4. `assets`:资产索引、缩略图、下载; 5. `crawl`:门店采集; 6. `providers`:图片生成供应商适配; 7. `routes`:HTTP 路由; 8. `image`:图片处理。 ### TypeScript 路线 先引入最小可用配置: ```json { "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "allowJs": true, "checkJs": true, "noEmit": true, "strict": true, "skipLibCheck": true }, "include": ["src/**/*.js", "src/**/*.ts"] } ``` 优先补充类型的模块: 1. `config`; 2. `db`; 3. `jobs`; 4. `assets`; 5. `providers`; 6. API 请求和响应。 建议补充 `zod`: - 启动时校验配置; - API 请求体校验; - 供应商响应校验; - 数据库写入前校验。 ### 测试策略 #### 单元测试 优先覆盖: 1. 路径安全; 2. 文件名清洗; 3. 配置校验; 4. 提示词渲染; 5. 模型参数构建; 6. 任务状态机; 7. 资产状态机; 8. 图片尺寸解析。 #### 集成测试 覆盖 API: 1. `/healthz`; 2. `/readyz`; 3. 设置读写; 4. 输出根目录限制; 5. 门店扫描; 6. 生成任务创建; 7. 任务状态查询; 8. 资产筛选和下载。 供应商和淘宝闪购接口必须 mock,不能在 CI 中真实调用或产生费用。 #### E2E 测试 使用 Playwright 覆盖: 1. 启动进入门店采集; 2. 添加店铺; 3. 系统设置保存; 4. 视觉生成页面扫描; 5. 任务中心状态; 6. 历史资产筛选; 7. 悬浮放大和下载; 8. 页面刷新后任务恢复。 ### CI/CD 第一阶段 GitHub Actions: ```text install → lint → typecheck → unit → build → integration → e2e → docker build ``` 建议脚本: ```json { "scripts": { "dev": "node --watch src/server.js", "build": "vite build", "lint": "eslint .", "format": "prettier --write .", "typecheck": "tsc --noEmit", "test": "vitest run", "test:e2e": "playwright test", "db:migrate": "node src/db/migrate.js" } } ``` 分支策略: ```text main 可发布 develop 集成分支 feature/* 功能分支 fix/* 修复分支 release/* 发布准备 ``` 发布流程: 1. CI 全绿; 2. 版本号更新; 3. 生成 Changelog; 4. 构建 Docker 镜像; 5. 打 tag; 6. 本地版发布压缩包或安装器; 7. SaaS 版部署到预发环境; 8. 预发验证后发布生产。 ### Docker 化 本地版使用多阶段构建: ```dockerfile FROM node:22-bookworm-slim AS deps WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci FROM node:22-bookworm-slim AS app WORKDIR /app ENV NODE_ENV=production COPY --from=deps /app/node_modules ./node_modules COPY . . EXPOSE 5177 CMD ["node", "server.js"] ``` `docker-compose.yml`: ```yaml services: app: build: . ports: - "127.0.0.1:5177:5177" environment: NODE_ENV: production HOST: 0.0.0.0 volumes: - ./workspace:/app/workspace - ./data:/app/data - ./config.json:/app/config.json:ro healthcheck: test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:5177/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"] interval: 30s timeout: 5s retries: 3 ``` 后续 SaaS 增加: ```text postgres redis minio worker nginx / caddy ``` ### 可观测性 P2 阶段先落地本地可观测性: 1. 结构化日志; 2. request id; 3. job id; 4. 错误码; 5. 日志文件轮转; 6. `/metrics`; 7. 任务耗时统计; 8. 供应商成功率; 9. 队列长度; 10. 存储增长。 核心指标: ```text generation_jobs_total generation_jobs_failed_total generation_job_duration_seconds provider_request_duration_seconds provider_error_total queue_depth crawl_shop_success_total crawl_image_failed_total asset_storage_bytes ``` --- ## P3-A 模板库 ### 目标 把提示词和视觉模板从分散配置升级为可复用、可版本化、可分发的模板库。 ### 数据模型 ```sql CREATE TABLE templates ( id TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT, category TEXT NOT NULL, kind TEXT NOT NULL, latest_version INTEGER NOT NULL, visibility TEXT NOT NULL, -- private | workspace | public status TEXT NOT NULL, created_by TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE template_versions ( id TEXT PRIMARY KEY, template_id TEXT NOT NULL, version INTEGER NOT NULL, prompt TEXT NOT NULL, negative_prompt TEXT, variables_json TEXT NOT NULL, default_params_json TEXT NOT NULL, preview_asset_id TEXT, changelog TEXT, status TEXT NOT NULL, created_at TEXT NOT NULL ); ``` ### 变量系统 支持变量: ```text {{shopName}} {{dishName}} {{brandColor}} {{brandStyle}} {{platform}} {{aspectRatio}} ``` 渲染时必须校验: 1. 必填变量; 2. 变量类型; 3. 变量长度; 4. 禁用占位符残留; 5. 模板版本; 6. 渲染结果快照。 ### 功能 1. 模板列表; 2. 模板预览; 3. 模板复制; 4. 模板编辑; 5. 版本对比; 6. 默认模板; 7. 门店覆盖模板; 8. 模板试运行; 9. 模板评分; 10. 使用统计。 ### 验收标准 - 每个生成任务都能追溯到模板 ID 和版本。 - 修改模板不会影响历史任务。 - 模板变量渲染后不允许出现未替换占位符。 - 用户可以基于历史作品反向保存模板。 --- ## P3-B 审核流 ### 目标 让生成结果可以进入“待审核 → 通过 / 驳回 → 发布”的标准流程,适合团队和连锁门店运营。 ### 状态机 ```text generated → pending_review pending_review → approved pending_review → rejected rejected → regenerated approved → published published → archived ``` ### 数据模型 ```sql CREATE TABLE reviews ( id TEXT PRIMARY KEY, asset_id TEXT NOT NULL, status TEXT NOT NULL, reviewer_id TEXT, comment TEXT, reasons_json TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); ``` ### 审核规则 1. 自动初审: - 图片是否生成成功; - 尺寸是否符合要求; - 文件是否损坏; - 是否包含禁止内容; - 敏感词检查。 2. 人工复审: - 品牌一致性; - LOGO 位置; - 菜品是否失真; - 文案是否正确; - 是否符合平台规范。 3. 驳回时必须选择原因: - 品牌不一致; - LOGO 错误; - 菜品失真; - 文案错误; - 分辨率不足; - 其他。 ### 功能 1. 审核队列; 2. 批量通过; 3. 批量驳回; 4. 审核评论; 5. 对比原图和生成图; 6. 驳回后一键重新生成; 7. 审核历史; 8. 责任人统计。 ### 验收标准 - 每个资产的状态变化都有记录。 - 驳回后能带着原因重新生成。 - 团队成员只能处理自己有权限的工作空间。 - 发布后的资产不可被普通删除操作直接影响。 --- ## P3-C 开放 API ### 目标 允许门店系统、ERP、运营平台或自动化脚本安全调用生成和查询能力。 ### API 分层 ```text 内部 UI API:/api/* 开放 API:/open/v1/* Webhook:/open/v1/events/* ``` ### 核心接口 ```text POST /open/v1/generations GET /open/v1/generations/:id GET /open/v1/assets/:id GET /open/v1/shops GET /open/v1/templates POST /open/v1/templates/:id/render ``` ### 认证 1. 开放 API Key 独立于 UI 登录态; 2. Key 哈希存储; 3. 只显示一次完整 Key; 4. 支持设置: - 过期时间; - 权限范围; - IP 白名单; - QPS 限制; - 日配额; - 月配额。 ### 幂等 创建生成任务必须支持: ```http Idempotency-Key: ``` 服务端根据 Key 和请求 hash 判断: - 首次请求:创建任务; - 重复请求:返回原任务; - 冲突请求:返回 409。 ### 限流和配额 ```sql CREATE TABLE api_keys ( id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL, name TEXT NOT NULL, key_hash TEXT NOT NULL, scopes_json TEXT NOT NULL, rate_limit_per_minute INTEGER, daily_quota INTEGER, monthly_quota INTEGER, expires_at TEXT, last_used_at TEXT, status TEXT NOT NULL, created_at TEXT NOT NULL ); ``` ### Webhook 事件类型: ```text generation.succeeded generation.failed generation.canceled asset.approved asset.rejected asset.published ``` Webhook 要求: 1. HMAC 签名; 2. 重试退避; 3. 事件幂等; 4. 可查询投递日志; 5. 支持手动重发。 ### 验收标准 - 第三方可以用 API Key 安全创建任务。 - 重复 Idempotency-Key 不会重复扣费。 - 超过配额返回明确错误码。 - 所有开放 API 操作都有审计日志。 --- ## P3-D 模型市场 ### 目标 把固定模型列表升级为可配置、可评价、可路由的模型市场,降低供应商锁定风险。 ### 数据模型 ```sql CREATE TABLE models ( id TEXT PRIMARY KEY, provider TEXT NOT NULL, slug TEXT NOT NULL, display_name TEXT NOT NULL, description TEXT, capabilities_json TEXT NOT NULL, price_json TEXT NOT NULL, status TEXT NOT NULL, health_status TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE model_routes ( id TEXT PRIMARY KEY, name TEXT NOT NULL, strategy TEXT NOT NULL, -- manual | best | price | speed | success_rate fallback_model_ids_json TEXT NOT NULL, status TEXT NOT NULL ); ``` ### 模型能力 每个模型声明: ```json { "maxImages": 4, "sizes": ["1K", "2K", "4K"], "aspectRatios": ["1:1", "4:3", "16:9"], "supportsReferenceImage": true, "supportsLogoOverlay": true, "supportsNegativePrompt": false, "estimatedSeconds": 45, "billing": "per_image" } ``` ### 路由策略 1. 指定模型; 2. 综合最优; 3. 价格优先; 4. 速度优先; 5. 成功率优先; 6. 指定回退链。 ### 市场信息 1. 模型名称; 2. 提供商; 3. 单价; 4. 预计耗时; 5. 支持比例; 6. 支持尺寸; 7. 参考图数量; 8. 成功率; 9. 健康状态; 10. 最近生成示例。 ### 验收标准 - 新增模型不需要重写业务流程。 - 模型不可用时能自动或手动切换回退模型。 - 每次生成记录实际使用的模型和成本。 - 模型失败原因能归类为限流、余额不足、参数错误、供应商故障等。 --- ## 实施节奏 ### Sprint 1:任务持久化 - 本地单店阶段先用 `.genpic/tasks.json` 保存任务快照; - 生成任务创建、进行中和终态状态落盘; - 服务重启后恢复供应商轮询,无法安全恢复的排队任务标记失败; - 任务中心 MVP 支持状态统计、筛选和结果查看。 - 后续批量/SaaS 阶段再迁移到 SQLite 的 `jobs` / `job_events` 模型。 ### Sprint 2:任务体验 - SSE; - 重试 / 取消; - 批量任务汇总; - 失败原因展示; - 任务日志。 ### Sprint 3:配置引导 - 配置状态接口; - API / Cookie / 目录测试; - Onboarding 弹窗; - 侧边栏健康状态。 ### Sprint 4:资产库 - assets / asset_versions; - 历史创作迁移; - 筛选、收藏、详情; - 回收站; - 稳定下载。 ### Sprint 5:工程质量 - 模块拆分; - ESLint / Prettier; - TypeScript checkJs; - 单元和集成测试; - GitHub Actions; - Docker。 ### Sprint 6:模板库 - 模板和版本表; - 变量渲染; - 模板选择; - 历史任务版本追溯; - 模板保存入口。 ### Sprint 7:审核流 - 资产状态机; - 审核队列; - 批量操作; - 驳回原因; - 重新生成联动。 ### Sprint 8:开放 API - API Key; - 幂等; - 限流; - 开放任务和资产接口; - Webhook。 ### Sprint 9:模型市场 - 模型注册; - 能力声明; - 路由策略; - 健康检查; - 成本与成功率面板。 --- ## 当前阶段建议 近期只做三件事: 1. **任务持久化 + 任务中心 MVP**:这是所有批量生成体验的基础。 2. **配置引导 + 健康检查**:减少 API Key、Cookie、目录配置错误。 3. **资产库数据化**:把历史创作从文件扫描升级为数据库索引。 TypeScript、CI/CD、Docker 应该与任务中心和资产库并行推进,但不要先做大规模前端重写。P3 能力必须等 P2 数据模型稳定后再开启,否则模板、审核、开放 API 和模型市场都会建立在脆弱的文件扫描和内存任务状态上。