docs: 完善技术基线与实施约束 (#1)
This commit is contained in:
@@ -14,7 +14,7 @@ MVP-0 的范围、非目标和验收口径见 [产品需求总览](docs/09-produ
|
||||
|
||||
## 快速开始(Wiki 初始化门禁)
|
||||
|
||||
线上 Gitea Wiki 已于 2026-08-20 初始化完成,15 个核心页面均已创建并回读确认,`docs/` 已转为只读镜像。以下是该门禁的完整顺序,供后续项目复用与核对:
|
||||
线上 Gitea Wiki 已于 2026-08-20 初始化完成,当前 16 个映射页面均已创建并回读确认,`docs/` 已转为只读镜像。以下是该门禁的完整顺序,供后续项目复用与核对:
|
||||
|
||||
1. 创建 Gitea 远端仓库并推送当前引导提交,启用工单和 Wiki。
|
||||
2. 配置 `wiki-docs.json` 和安全访问方式;优先使用已配置的 Gitea MCP,MCP 不可用时才使用 Gitea API 并记录原因。令牌只通过环境变量或 MCP 安全配置提供。
|
||||
@@ -41,6 +41,7 @@ MVP-0 的范围、非目标和验收口径见 [产品需求总览](docs/09-produ
|
||||
| 怎样跑起来、怎样验证 | [docs/04-local-development-and-verification.md](docs/04-local-development-and-verification.md) |
|
||||
| 简单修改怎么做、什么时候必须停 | [docs/05-common-changes.md](docs/05-common-changes.md) |
|
||||
| 报错了先查什么 | [docs/06-troubleshooting.md](docs/06-troubleshooting.md) |
|
||||
| 怎样部署、检查、备份和回退 | [docs/10-deployment-and-operations.md](docs/10-deployment-and-operations.md) |
|
||||
| 建单、实施、验收、归档流程 | [docs/01-workflow.md](docs/01-workflow.md) |
|
||||
|
||||
`docs/` 是 Wiki 的只读镜像,不是编辑入口。修改长期文档的顺序见 [项目档案](docs/00-project-profile.md) 的“文档状态”。
|
||||
|
||||
+60
-40
@@ -2,8 +2,8 @@
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Project-Profile
|
||||
wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Project-Profile.-
|
||||
wiki_revision: ad9b705ba045f033379b6daea4a3575702d0eb35
|
||||
synchronized_at: 2026-08-20T03:31:03Z
|
||||
wiki_revision: 3bc33f550a53d7cab30684fca2eb5a2c42866090
|
||||
synchronized_at: 2026-08-20T07:01:02Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
# 项目档案
|
||||
@@ -39,37 +39,53 @@ synchronized_at: 2026-08-20T03:31:03Z
|
||||
|
||||
## 子项目与交付单元
|
||||
|
||||
| 子项目 / 交付单元 | 职责 | 技术栈 | 构建与测试 | 版本与发布方式 | 规则入口 | 共享边界 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `internal/core` 核心域库 | provider 调用、选路、生成编排、队列、存储、加密、SSRF 防护 | Go 1.22+、GORM | `go build ./...`;`go test ./internal/...` | 不单独发布,随两个二进制发布 | 根 `AGENTS.md` | 被 portal 与 admin 共用;**不 import gin、不 import go-admin** |
|
||||
| `portal` 用户端二进制 | 用户 Web 界面、JSON API、API Key 端点,MVP 阶段内嵌 worker | Go + Gin + html/template + HTMX + Alpine + Tailwind | `go build ./portal/...`;`go test ./portal/...` | 跟随 `main` 分支构建单二进制 | 根 `AGENTS.md` | 依赖 `internal/core`,与 admin 共用同一 MySQL 库 |
|
||||
| `admin` 管理端二进制 | Provider/Model/路由池/记录/用户与点数管理 | Go + go-admin(Gin + GORM + Casbin + JWT)+ go-admin-ui(Vue 2) | `go build ./admin/...` | 跟随 `main` 分支构建 | 根 `AGENTS.md` | 依赖 `internal/core`;不得绕过核心域直接实现生成逻辑 |
|
||||
| `migrations` 数据库迁移 | 业务表结构演进 | golang-migrate(SQL 文件) | `migrate up` / `migrate down` | 随代码提交,按序号前进 | 根 `AGENTS.md` | 表结构是三个交付单元的唯一共享契约 |
|
||||
| 子项目 / 交付单元 | 职责 | 技术栈与版本 | 构建与测试 | 发布方式 | 共享边界 |
|
||||
|---|---|---|---|---|---|
|
||||
| `internal/core` 核心域库 | 领域模型、provider 协议抽象、生成编排、队列状态、加密接口 | Go 1.26.5、GORM、标准库 | `go test ./internal/core/...` | 不单独发布 | **不得 import Gin、go-admin、gobreaker 或 imaging** |
|
||||
| `internal/platform` 基础设施适配 | 出站 HTTP/SSRF、熔断、图片处理、存储等接口实现 | 标准库、sony/gobreaker、disintegration/imaging | `go test ./internal/platform/...` | 随服务二进制发布 | 通过接口注入 core,不反向污染领域层 |
|
||||
| `portal` 用户端二进制 | 会话、页面/HTMX 片段、JSON API,MVP 阶段内嵌 worker | Gin、html/template、HTMX、Alpine、Tailwind | `go test ./portal/...` | 单二进制 | 依赖 core 与 platform;提交链路不得调用上游 |
|
||||
| `admin` 管理端后端 | Provider/Model/路由池/记录/用户与点数管理 | 固定提交的 go-admin(Gin、GORM、Casbin、JWT) | `go test ./admin/...` | 独立二进制 | 依赖 core;不得复制生成逻辑 |
|
||||
| `admin-ui` 管理端前端 | go-admin-ui CRUD 与少量定制页 | Vue 3.5.41、Element Plus 2.14.4、Vue CLI 5.0.9 | `pnpm install --frozen-lockfile`、`pnpm build:prod` | 静态产物 | 代码生成器只在隔离开发环境使用 |
|
||||
| `migrations` 数据库迁移 | 业务表、`sys_*` 基线和菜单/API 配置的全部生产演进 | golang-migrate SQL | up/down 隔离库验证 | 随版本发布 | **生产数据库结构的唯一事实来源** |
|
||||
|
||||
共享契约的唯一事实来源是 `migrations/` 中的 SQL 与 [业务规则与术语](Business-Rules-and-Glossary.-);不得在多处维护互不确认的表结构描述。MVP-0 只要求 `internal/core` 与 `portal` 可运行,`admin` 与 `migrations` 的完整形态在后续阶段补齐。
|
||||
管理员 `sys_user` 与终端用户 `users` 分表。MVP-0 只要求 core、platform、portal 和必要迁移可运行;admin/admin-ui 在 MVP-1 接入,但生产所需 `sys_*` 初始结构和配置仍必须先转成版本化 SQL。任何构建、CI 或部署都不得依赖 `D:\github\goadmin` 的绝对路径。
|
||||
|
||||
## 技术栈与运行环境
|
||||
|
||||
### 固定来源基线
|
||||
|
||||
| 项目 | 本地审查来源 | 固定提交 | 关键约束 |
|
||||
|---|---|---|---|
|
||||
| go-admin | `D:\github\goadmin\go-admin` | `f06540883b41d03782bb6b2c4150f298f328c6b6` | `go.mod` 要求 Go 1.26.5 |
|
||||
| go-admin-ui | `D:\github\goadmin\go-admin-ui` | `67d393d713877572fab0b897296a4c1d525fc81d` | Vue 3.5.41、Element Plus 2.14.4、Vue CLI 5.0.9、Node >=22、`packageManager=pnpm@9.15.1` |
|
||||
| go-admin-doc | `D:\github\goadmin\go-admin-doc` | `424855aacf6905f3fde860c3331385cb25529a0d` | 只作固定版本说明参考 |
|
||||
|
||||
这些路径用于审查和导入来源,不是运行依赖。首次接入必须把需要的脚手架/前端代码纳入 chorus 仓库或把 Go 依赖锁定到可复现版本,并在实现工单记录来源提交、导入范围和本地修改。
|
||||
|
||||
### 运行栈
|
||||
|
||||
| 部分 | 技术 | 说明 |
|
||||
|---|---|---|
|
||||
| HTTP 框架 | Gin | 与 go-admin 一致,减少两端框架分裂 |
|
||||
| ORM 与迁移 | GORM + golang-migrate | GORM 只做查询,**生产不使用 AutoMigrate** |
|
||||
| 数据库 | MySQL 8.0 | 单库;队列用 `SELECT … FOR UPDATE SKIP LOCKED` |
|
||||
| 任务队列 | 无 Redis / 无 MQ | 租约 + 心跳轮询,少一个运行组件 |
|
||||
| 管理端 | go-admin-team/go-admin + go-admin-ui | 自带 RBAC / JWT / 代码生成器 |
|
||||
| 用户端会话 | alexedwards/scs | 无需 Redis |
|
||||
| 熔断 | sony/gobreaker | 多 provider 故障转移,MVP-0 之后启用 |
|
||||
| 缩略图 | disintegration/imaging | 纯 Go,无 CGO |
|
||||
| 限流 | ulule/limiter | 用户维度令牌桶 |
|
||||
| 上游调用 | 手写 net/http 客户端 | 不用 go-openai SDK:多 provider 在多图上传、`extra_body` 透传、`b64_json` vs `url` 上差异大,强类型 SDK 反而挡路 |
|
||||
| 用户端前端 | html/template + HTMX 2.x + Alpine 3.x + Tailwind 4.x(standalone CLI) | 无 npm、无构建链、无 SPA |
|
||||
| 开发环境 | Windows + PowerShell + Git;MySQL 8.0 本地或容器 | Harness 工具需 Python 3 标准库 |
|
||||
| Go | 1.26.5 | 以固定 go-admin 的 `go.mod` 为最低统一版本,不再使用原文档的 Go 1.22+ |
|
||||
| HTTP | Gin | portal 与 admin 共用生态,但 core 不依赖 Gin |
|
||||
| ORM 与迁移 | GORM + golang-migrate | GORM 负责运行期持久化;生产禁止 AutoMigrate |
|
||||
| 数据库 | MySQL 8.0 | 单库;队列依赖 `SELECT … FOR UPDATE SKIP LOCKED` |
|
||||
| 队列 | MySQL 租约 | 不引入 Redis/MQ;租约 token + CAS 防止陈旧 worker 提交 |
|
||||
| 管理端 | 固定 go-admin + go-admin-ui | schema-first 生成 CRUD;生产关闭 dev-tools |
|
||||
| 用户端会话 | alexedwards/scs | Cookie 会话,不复用管理员 JWT |
|
||||
| 平台适配 | gobreaker、imaging | 只能位于 `internal/platform` |
|
||||
| 限流 | ulule/limiter | MVP-2 用户维度令牌桶 |
|
||||
| 上游调用 | `net/http` 适配器 | 支持多图、`extra_body`、URL/Base64 差异,并实施 SSRF 钩子 |
|
||||
| 用户端 UI | html/template + HTMX 2.x + Alpine 3.x + Tailwind 4.x standalone | portal 不需要 Node;admin-ui 构建需要 Node/pnpm |
|
||||
| 开发环境 | Windows + PowerShell + Git;MySQL 8.0 本地或容器 | Harness 使用 Python 3 标准库 |
|
||||
|
||||
### 建设基线评估结论
|
||||
### AutoMigrate 与代码生成结论
|
||||
|
||||
- 管理端**采用开源基线** go-admin(MIT):已具备 RBAC、JWT、审计和代码生成器,Provider/Model/用户/点数等 CRUD 可近似零成本产出,定制范围可控。已知代价:go-admin-ui 属 vue-element-admin 系(Vue 2 已 EOL),定制页超过 3 个时须重新评估是否改为自建服务端渲染后台。
|
||||
- 核心生成域**从零开发**:cmhub 是 Django,语言已变,能带走的是设计(provider 抽象、任务租约、SSRF 校验、密钥加密)而非代码;现成 Go 系 LLM 网关(如各类 one-api 变体)与本项目在图生图多图上传、图片角色规则和自有点数展示上差异大,改造与跟随上游成本高于自建约 200 行 provider 层。
|
||||
- 基础组件按需复用:gobreaker、imaging、limiter、scs 均为单一职责库,各自记录用途、版本和可替换方案。
|
||||
- go-admin 固定提交的 `cmd/migrate` 和模板中确有 AutoMigrate;它适合帮助理解脚手架初始化结构,但不是可审查、可回退的生产迁移。
|
||||
- 如确需研究该初始化结果,只能由人工在**隔离、可丢弃、无生产数据**的开发数据库中单独运行,并把得到的结构差异整理为 `migrations/*.up.sql` 与 `*.down.sql`。
|
||||
- go-admin-ui 的生成器先从数据库导入已有表到 `sys_tables/sys_columns`,再生成 Go/Vue 文件;因此标准流程是先执行版本化 SQL,再导入和生成。
|
||||
- 生成器的“生成迁移脚本”主要写菜单、权限与 API 配置的 Go 迁移代码,并不是业务表 DDL,也没有本项目要求的可逆性和幂等证据;这些配置必须人工转换为可逆 SQL。
|
||||
- dev-tools 路由包含写文件和写菜单的能力,固定提交中只有 JWT 保护且被 Casbin 排除,生产构建不得注册、代理或暴露这些路由。
|
||||
|
||||
## 阅读入口
|
||||
|
||||
@@ -101,24 +117,28 @@ synchronized_at: 2026-08-20T03:31:03Z
|
||||
|
||||
| 目录 | 职责 | 不应放入 |
|
||||
|---|---|---|
|
||||
| `internal/core/` | 与框架无关的核心域 | gin、go-admin 依赖,HTTP 处理,模板 |
|
||||
| `portal/` | 用户端 Gin 服务、页面模板与静态资源 | 上游调用与选路逻辑(属于 core) |
|
||||
| `admin/` | go-admin 脚手架与业务 app | 绕过 core 的生成实现 |
|
||||
| `migrations/` | golang-migrate SQL 文件 | 测试数据、一次性修数据脚本 |
|
||||
| `docs/` | 核心长期文档(Wiki 初始化后为只读镜像) | 人工直接维护的最终事实(初始化后) |
|
||||
| `docs/task/` | 人工按需导出的任务归档快照 | 讨论过程和临时方案 |
|
||||
| `prototypes/` | 按工单和版本保存的 HTML 审核快照 | 凭据、个人信息、生产数据 |
|
||||
| `dev_scripts/` | Harness 检查与 Wiki 同步工具 | 产品功能代码 |
|
||||
| `tests/` | Harness 工具测试(Go 测试与被测代码同目录) | 生产数据 |
|
||||
| `internal/core/` | 领域模型、协议接口、编排与状态规则 | Gin、go-admin、gobreaker、imaging、HTTP handler |
|
||||
| `internal/platform/` | HTTP/SSRF、熔断、图片、存储等适配器 | 页面、管理端 CRUD、业务状态机复制 |
|
||||
| `portal/` | 用户端 Gin、模板、静态资源、worker 组装 | 直接实现 provider 选路和上游协议 |
|
||||
| `admin/` | go-admin 后端及 `app/chorus/` | 绕过 core 的生成实现、生产代码生成路由 |
|
||||
| `admin-ui/` | 固定 go-admin-ui 导入代码与定制页 | 运行时依赖 `D:\github\goadmin` |
|
||||
| `migrations/` | 全部生产表结构与配置种子 SQL | 测试数据、AutoMigrate、不可逆一次性修数 |
|
||||
| `docs/` | 核心长期文档的 Wiki 只读镜像 | 人工直接维护的最终事实 |
|
||||
| `docs/task/` | 人工按需导出的任务归档快照 | 讨论过程和默认自动导出 |
|
||||
| `prototypes/` | 按工单/版本保存 HTML 审核快照 | 凭据、个人信息、生产数据 |
|
||||
| `dev_scripts/` | DevHarness 工具 | 产品业务脚本 |
|
||||
| `tests/` | Harness 测试;Go 测试与代码同目录 | 生产数据 |
|
||||
|
||||
## 环境、配置与凭据
|
||||
|
||||
- 核心 Wiki 同步配置:`wiki-docs.json`;Gitea 地址可用 `GITEA_URL` 覆盖。
|
||||
- Gitea PAT 仅通过 `GITEA_TOKEN` 或 MCP 安全配置提供,不写入仓库。
|
||||
- 数据库连接串通过 `CHORUS_DSN` 环境变量提供,不写入仓库。
|
||||
- Provider 的 `api_key` 使用 AES-GCM 加密落库,主密钥来自环境变量 `CHORUS_MASTER_KEY`,须预留轮换方案;明文密钥不得进入日志、工单、Wiki 或截图。
|
||||
- 生成物默认落本地文件系统(路径由配置指定),`storage` 从第一天就是接口,预留 S3/OSS。
|
||||
- 测试只使用构造数据和 mock 上游,不使用生产数据和真实上游密钥。
|
||||
- 核心 Wiki 同步配置是 `wiki-docs.json`;Gitea 地址可用 `GITEA_URL` 覆盖。
|
||||
- Gitea PAT 仅从进程环境、MCP 安全配置或本机未跟踪的环境文件加载,不写入仓库、工单或 Wiki。
|
||||
- 数据库连接串由 `CHORUS_DSN` 提供。
|
||||
- Provider `api_key` 使用 AES-GCM 加密落库;密文同时记录非敏感 `key_id`,主密钥从环境/密钥管理系统取得。轮换按“新 key_id 写入、后台重加密、验证后退役旧密钥”执行,不原地猜测密钥版本。
|
||||
- 会话签名/加密密钥与 Provider 主密钥分离,生产 Cookie 必须启用 `Secure`、`HttpOnly` 和合适的 `SameSite`。
|
||||
- 生成物默认落受保护的本地目录,不能直接把真实路径暴露为公开静态 URL;访问先校验用户归属。storage 从第一天是接口,预留 S3/OSS。
|
||||
- 测试只用构造数据与 mock 上游;真实连通性检查必须是管理员明确触发的单次低成本动作,并有审计和冷却。
|
||||
- 上传数量、单文件/总大小、允许 MIME、像素上限、上游超时、租约时长、保留期和限流阈值均为配置。未确认生产值前保持部署门禁,不由 Agent 臆造。
|
||||
|
||||
## 项目专用验收要求
|
||||
|
||||
|
||||
@@ -2,109 +2,127 @@
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Architecture-and-Code-Map
|
||||
wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Architecture-and-Code-Map.-
|
||||
wiki_revision: e2a808803b76303549ea374700d971dfa20f2fdd
|
||||
synchronized_at: 2026-08-20T03:29:24Z
|
||||
wiki_revision: b1268df4bd1d7e4fc6ca0a418da7215e61a4af91
|
||||
synchronized_at: 2026-08-20T07:01:09Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
# 架构与代码地图
|
||||
|
||||
## 项目定位
|
||||
|
||||
chorus 是一个独立运行的 Go 服务,对外提供两种生成能力:
|
||||
chorus 是独立运行的 Go 生图生文服务:
|
||||
|
||||
- **图生图**:提示词 + 一张或多张原图 → 新图;
|
||||
- **文生文**:提示词 → 文本。
|
||||
|
||||
上游是各家兼容 OpenAI 规范的模型服务。chorus 负责:接收请求、合成最终提示词、把任务放入队列、由 worker 选择一个可用上游执行、落盘结果与缩略图、把状态反馈给页面。管理端负责配置上游、组织路由池和查看记录。
|
||||
|
||||
一个仓库、两个二进制、一个共享库:
|
||||
portal 只负责接收、校验、持久化和展示;worker 才能调用上游。admin 配置 Provider/Model、路由池和查看记录。一个仓库包含两个二进制、一个纯领域核心和基础设施适配层:
|
||||
|
||||
```text
|
||||
chorus/
|
||||
├── go.mod
|
||||
├── internal/core/ ★ 核心域,两端共享,不依赖 gin / go-admin
|
||||
│ ├── model/ GORM 模型(业务表)
|
||||
│ ├── provider/ chat / images / images_edits / gemini 四种实现
|
||||
│ ├── router/ 选路、加权、gobreaker 熔断、故障转移
|
||||
│ ├── generate/ prompt 合成、调用编排、落盘、缩略图
|
||||
│ ├── queue/ SKIP LOCKED 租约、心跳、重试
|
||||
│ ├── storage/ 本地 FS + S3 接口
|
||||
│ ├── crypto/ provider api_key AES-GCM
|
||||
│ └── security/ SSRF 拦截(DialContext 钩子)
|
||||
├── admin/ go-admin 脚手架,业务 app 在 app/chorus/
|
||||
├── admin-ui/ go-admin-ui,加自定义页面
|
||||
├── portal/ ★ 用户端:独立 Gin
|
||||
│ ├── handler/ 页面 + JSON API + openapi(API Key)
|
||||
│ └── web/{templates,static}
|
||||
└── migrations/ golang-migrate(业务表;sys_* 交给 go-admin 初始化)
|
||||
├── internal/core/ 领域模型、接口、编排和状态规则;仅 GORM + 标准库
|
||||
│ ├── model/
|
||||
│ ├── provider/ 协议接口与 chat/images/images_edits/gemini 领域语义
|
||||
│ ├── router/ 候选、retryable 与选路规则
|
||||
│ ├── generate/ prompt 合成和生成编排
|
||||
│ ├── queue/ 租约、CAS、重试状态
|
||||
│ ├── storage/ 存储接口
|
||||
│ └── crypto/ 密钥加密接口
|
||||
├── internal/platform/ HTTP/SSRF、gobreaker、imaging、本地/S3 适配
|
||||
├── portal/ 用户页面、HTMX 片段、JSON API、worker 组装
|
||||
├── admin/ 固定 go-admin 后端,业务 app 在 app/chorus/
|
||||
├── admin-ui/ 固定 go-admin-ui 导入代码与定制页
|
||||
└── migrations/ 所有生产表、sys_* 基线和配置种子
|
||||
```
|
||||
|
||||
固定来源和提交见 [项目档案](Project-Profile.-)。`D:\github\goadmin` 只用于来源审查和首次导入,运行、CI 与部署不得依赖该路径。
|
||||
|
||||
## MVP-0 的最小形态
|
||||
|
||||
MVP-0 的唯一目标是**让全流程跑通**,因此只实现上面结构的一条竖切:
|
||||
|
||||
| 目录 | MVP-0 实现 | 暂缓 |
|
||||
|---|---|---|
|
||||
| `internal/core/model` | `users`、`providers`、`provider_models`、`generations`、`generation_inputs`、`generation_outputs` | 点数、路由池、健康、审计表 |
|
||||
| `internal/core/provider` | `chat` 与 `images_edits` 两种实现 | `images`、`gemini` |
|
||||
| `internal/core/router` | 直接取唯一启用的 provider_model | 加权选路、熔断、故障转移 |
|
||||
| `internal/core/generate` | prompt 合成、调用、落盘、缩略图 | 模板三层覆盖的管理端层 |
|
||||
| `internal/core/queue` | `SKIP LOCKED` 取任务 + 租约 + 失败置错 | 心跳续租、多次重试、清理任务 |
|
||||
| `internal/core/crypto`、`security` | 从第一天就有:AES-GCM、SSRF 拦截 | 密钥轮换 |
|
||||
| `portal` | 登录、双栏首页、提交生成、HTMX 轮询卡片、结果展示 | API Key 端点、限流、无限滚动分页 |
|
||||
| `admin` | 暂不接入,provider 配置用迁移或一次性命令写入 | 全部管理端页面 |
|
||||
| `internal/core/model` | `users`、`providers`、`provider_models`、`prompt_templates`、`generations`、输入/输出 | 点数、路由池、审计 |
|
||||
| `internal/core/provider` | `chat`、`images_edits` | `images`、`gemini` |
|
||||
| `internal/core/router` | 唯一启用 ProviderModel | 加权、熔断、故障转移 |
|
||||
| `internal/core/queue` | SKIP LOCKED、租约 token/owner、CAS 完成、失败落错 | 心跳续租、多次跨 Provider 重试、清理 |
|
||||
| `internal/platform` | SSRF 安全 HTTP、本地存储、缩略图 | gobreaker、对象存储 |
|
||||
| `portal` | 种子用户登录、桌面双栏/移动单列、提交、轮询、结果 | 公开注册、点数/API Key 导航、无限滚动、移动抽屉 |
|
||||
| `admin/admin-ui` | 不接入;配置由可重复种子命令完成 | MVP-1 schema-first CRUD 与定制页 |
|
||||
|
||||
暂缓项都在 [产品需求总览](Product-Requirements-Overview.-) 中登记为后续阶段,不是被取消的需求。
|
||||
MVP-0 第一张上传图自动作为 `primary`,其余为 `reference`;不提供角色编辑、备注和拖拽。默认中性模板的具体措辞仍是实现前确认门禁,不能硬编码猜测。
|
||||
|
||||
## 代码地图
|
||||
|
||||
| 想改什么 | 从哪个文件开始读 | 相关测试 |
|
||||
| 想改什么 | 从哪里开始读 | 必要验证 |
|
||||
|---|---|---|
|
||||
| 页面长什么样 | `portal/web/templates/` | 手工浏览器检查 + `prototypes/` 审核快照 |
|
||||
| 提交生成的入参与校验 | `portal/handler/generate.go` | `portal/handler/generate_test.go` |
|
||||
| 生成卡片轮询与停止条件 | `portal/handler/card.go` 与对应模板 | 同上 |
|
||||
| 最终提示词怎么拼出来 | `internal/core/generate/prompt.go` | `internal/core/generate/prompt_test.go` |
|
||||
| 怎么调上游、怎么解析响应 | `internal/core/provider/` 下各实现 | 各实现的 `_test.go`,用 mock HTTP 服务 |
|
||||
| 失败了换不换下一家 | `internal/core/router/retryable.go` | `internal/core/router/retryable_test.go` |
|
||||
| 任务怎么被取走和重试 | `internal/core/queue/` | `internal/core/queue/queue_test.go`(需 MySQL) |
|
||||
| 结果文件放哪 | `internal/core/storage/` | `internal/core/storage/local_test.go` |
|
||||
| 表结构 | `migrations/` 中序号最大的 SQL | 迁移 up/down 各跑一次 |
|
||||
| 页面、状态和响应式 | `portal/web/templates/`、`portal/web/static/` | 浏览器 E2E + 已确认原型 |
|
||||
| 提交校验/幂等 | `portal/handler/generate.go` | handler 测试、跨用户测试 |
|
||||
| HTMX 卡片轮询 | `portal/handler/card.go` | 终态停止、网络错误、认证失效 |
|
||||
| prompt 合成 | `internal/core/generate/prompt.go` | 默认模板、图片顺序与落库 |
|
||||
| 上游协议 | `internal/core/provider/` | mock 429/5xx/超时/连接/400/401/策略拒绝 |
|
||||
| SSRF/重定向/结果 URL | `internal/platform/http/` | DNS、IPv4/IPv6、redirect、proxy 测试 |
|
||||
| 选路与 retryable | `internal/core/router/` | 四类红线用例 |
|
||||
| 队列和陈旧 worker | `internal/core/queue/` | MySQL 并发、租约过期、CAS |
|
||||
| 文件与缩略图 | `internal/platform/storage/` | 临时文件、原子 rename、权限 |
|
||||
| 表结构 | `migrations/` 最新 SQL | 隔离 MySQL up/down |
|
||||
| 管理端 CRUD | `admin/app/chorus/`、`admin-ui/src/views/` | 生成差异审查、生产路由检查 |
|
||||
|
||||
文件名是 MVP-0 的规划命名;实现工单如需调整,必须同步更新本表。
|
||||
文件名是规划入口;实现工单调整后同步本页。
|
||||
|
||||
## 两条主要执行路径
|
||||
|
||||
### 路径一:用户提交生成(同步部分)
|
||||
### 用户提交生成(同步)
|
||||
|
||||
```text
|
||||
浏览器表单 / HTMX 提交
|
||||
→ portal/handler/generate.go:校验、保存上传原图、写 generations(status=pending) 与 generation_inputs
|
||||
→ 立即返回一张 loading 卡片(带 hx-trigger="every 2s")
|
||||
表单/HTMX/JSON 请求
|
||||
→ 认证、CSRF(浏览器)、上传与业务校验
|
||||
→ 保存输入文件
|
||||
→ 事务写 generations(status=pending)、generation_inputs
|
||||
→ 以 (user_id, idempotency_key) 保证幂等
|
||||
→ 立即返回 loading 卡片或 202 JSON
|
||||
```
|
||||
|
||||
提交链路**不调用上游**,也不阻塞等待结果。任何在这一步做上游调用的改动都属于架构变更,必须先建单确认。
|
||||
同步链路不得调用上游,不校验点数或配额,也不得把文件系统真实路径返回给浏览器。HTMX 请求返回可替换的片段;JSON 请求返回稳定错误码与字段级错误,认证失效明确返回 401/登录引导,不能用空白片段吞错。
|
||||
|
||||
### 路径二:worker 执行生成(异步部分)
|
||||
### worker 执行生成(异步)
|
||||
|
||||
```text
|
||||
queue:SELECT … FOR UPDATE SKIP LOCKED 取一条 pending,写 lease_until,置 running
|
||||
→ generate:合成 rendered_prompt(管理端模板 → 用户 role_rule → 每张图 role/note)
|
||||
→ router:挑选候选 provider_model(MVP-0 只有一个)
|
||||
→ provider:调用上游,得到图片字节或文本
|
||||
→ storage:落盘原图/结果图,生成 256px 缩略图
|
||||
→ 写 generation_outputs,generations 置 succeeded/failed,记录 attempts 与 latency_ms
|
||||
→ 下一次轮询请求返回不带 hx-trigger 的成品卡片,轮询自动停止
|
||||
事务内 SKIP LOCKED 选 pending 或租约已过期的 running
|
||||
→ 原子写 running、lease_owner、lease_token、lease_until,attempt_count + 1
|
||||
→ 合成 rendered_prompt 并落库
|
||||
→ 选 ProviderModel,使用安全 HTTP 适配器调用
|
||||
→ 结果写临时文件,校验后原子 rename;图片生成缩略图
|
||||
→ 用 id + lease_token + status=running 做 CAS
|
||||
→ 成功写 outputs/status=succeeded;失败写 status=failed/error/attempts
|
||||
```
|
||||
|
||||
worker 跑在 portal 进程内(goroutine pool + 优雅退出),不放进 go-admin 的 `app/jobs`,避免管理端重启影响生成。
|
||||
状态对外始终是 `pending → running → succeeded|failed`。过期任务由新 worker 在一次原子更新中替换租约,**不把状态退回 pending**。旧 worker 的 token 已失效,最终 CAS 必须影响 0 行,不能覆盖新 worker 结果。租约时长必须大于上游请求超时、下载/存储和安全余量之和;具体值是部署前配置门禁。
|
||||
|
||||
`attempts` 与 `attempt_count` 在同一事务/原子更新中追加,错误内容先分类、脱敏和截断。worker 收到退出信号后停止取新任务,等待在途任务到设定期限;无法完成时让租约自然过期,不能无条件提交。
|
||||
|
||||
## 管理端数据库与代码生成
|
||||
|
||||
固定 go-admin-ui 的实际流程是:
|
||||
|
||||
```text
|
||||
编写并验证可逆 SQL
|
||||
→ 在隔离开发库执行 up
|
||||
→ /db/tables/page 读取现有表
|
||||
→ /sys/tables/info 导入 sys_tables/sys_columns 元数据
|
||||
→ 配置、预览、生成 Go/Vue CRUD
|
||||
→ 人工审查路径、权限、字段和差异
|
||||
→ 菜单/API 配置改写为可逆 SQL
|
||||
→ 重新从空库执行全部 migrations 验证
|
||||
```
|
||||
|
||||
go-admin 的 AutoMigrate 只能在隔离、可丢弃数据库中用于研究固定提交的初始化结构,不能嵌入应用启动或部署。go-admin-ui 的“生成迁移脚本”不是业务表 DDL;`/gen/todb` 会直接改菜单,`/gen/toproject` 会写源码。dev-tools 路由在固定提交中仅有 JWT 且绕过 Casbin,生产必须通过构建/路由注册检查确保不存在,不能只在菜单中隐藏。
|
||||
|
||||
## 不可破坏的边界
|
||||
|
||||
- `internal/core` **不 import gin、不 import go-admin**,只依赖 GORM 和标准库。管理端换框架或用户端换渲染方式时,核心不需要改。
|
||||
- 管理员与终端用户**物理隔离在两张表**:管理端用 go-admin 的 `sys_user` + JWT + Casbin,用户端用 `users` + session cookie(scs),程序调用用 API Key 哈希比对同样落在 `users`。不得把两类身份合并到一张表。
|
||||
- **生成链路完全不碰点数**:点数只读展示,无扣费、无退款、无余额校验、无配额。cmhub 的 `precharge_call / refund_call_points / mark_call_success` 三段式事务全部不移植。
|
||||
- **`retryable` 判定是全局最容易出错的地方**:`429 / 5xx / 超时 / 连接错误` 换下一家;`400 / 401 / 内容策略拒绝` 立即返回不换家。写反了,一个参数错误会把所有 provider 的配额烧一遍。
|
||||
- **SSRF 校验不能省**:provider `base_url` 由管理员填写,是打内网的直接入口。必须在 **DNS 解析后、连接前**用 `DialContext` 钩子拦截私网与回环地址,防 DNS rebinding。
|
||||
- **生产不使用 GORM AutoMigrate**:表结构只经 `migrations/` 演进。
|
||||
- **`rendered_prompt` 必须落库**,`generations.attempts` 必须记录每次尝试的 provider、错误和耗时;这两项是排障的唯一依据。
|
||||
- 结果图必须同步生成缩略图(`generation_outputs.thumb_path`),预览列表不得直接加载原图。
|
||||
- `internal/core` 只依赖 GORM 和标准库;Gin、go-admin、gobreaker、imaging 都在外层。
|
||||
- 管理员 `sys_user` 与终端用户 `users` 分表,不复用密码、Cookie、JWT 或 API Key。
|
||||
- 生成链路不读写点数:无扣费、退款、余额校验或每日配额。
|
||||
- `429/5xx/超时/连接错误` 才换下一家;`400/401/内容策略拒绝` 立即返回。
|
||||
- 所有 Provider 请求、重定向和上游返回 URL 下载都必须经过 DNS 解析后、连接前的 DialContext 拦截;代理环境不能绕开检查。
|
||||
- 生产数据库只接受 `migrations/`,不执行 AutoMigrate。
|
||||
- `rendered_prompt`、`attempts`、失败 `error_code/error_message` 必须持久化。
|
||||
- 输出文件先写临时文件并原子落位;图片必须有缩略图。原图、结果和缩略图访问均校验用户归属。
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Business-Rules-and-Glossary
|
||||
wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Business-Rules-and-Glossary.-
|
||||
wiki_revision: 311d04cdb64461caa1d40a2e9943a58811d947bf
|
||||
synchronized_at: 2026-08-20T03:29:26Z
|
||||
wiki_revision: 1d0fc6a192ac3235477f6033b3f760d6630f12f7
|
||||
synchronized_at: 2026-08-20T07:01:13Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
# 业务规则与术语
|
||||
@@ -12,72 +12,81 @@ synchronized_at: 2026-08-20T03:29:26Z
|
||||
|
||||
| 术语 | 含义 |
|
||||
|---|---|
|
||||
| **Provider** | 一个上游服务商配置:`base_url`、加密后的 `api_key`、`api_type`、权重、启停。默认只支持 OpenAI 规范。 |
|
||||
| **api_type** | provider 的调用形态,四种:`chat`(文生文)、`images`(纯文生图)、`images_edits`(图生图,多图上传)、`gemini`。 |
|
||||
| **ProviderModel** | 某个 provider 下的一个具体模型:`model_id`、能力(text / image / vision)、`extra_body`、超时。选路的最小单位。 |
|
||||
| **RoutePool(路由池)** | 管理端勾选的一组 ProviderModel,按 `kind`(text / image)分类,用于均衡与故障转移。 |
|
||||
| **Generation(生成记录)** | 一次生成请求的完整记录,图生图与文生文**统一在一张表**,区别只在 `kind`。 |
|
||||
| **kind** | 生成类型:`text`(文生文)或 `image`(图生图)。 |
|
||||
| **role(图片角色)** | 每张上传原图的身份:`primary`(主体图)或 `reference`(参考图)。默认第一张为主体,其余为参考。 |
|
||||
| **role_rule(角色规则)** | 描述“第几张图是什么”的一段文字。默认值来自管理端模板,用户端可编辑可清空。 |
|
||||
| **rendered_prompt** | 实际发给上游的完整内容,由角色规则、每张图的 role 与 note、用户提示词合成。 |
|
||||
| **attempts** | 本次生成的故障转移链路:`[{provider_model_id, err, ms}, …]`。 |
|
||||
| **租约(lease)** | worker 取走任务后写入的 `lease_until`;过期未完成的任务可被其他 worker 重新取走。 |
|
||||
| **点数(point)** | 用户余额,**只做展示**。余额存在 chorus 自有表,由管理端发放。 |
|
||||
| **Provider** | 上游服务商账号配置,只持有名称、`base_url`、AES-GCM 密文 API Key、`key_id`、启停和审计信息。 |
|
||||
| **ProviderModel** | Provider 下可选的模型绑定,是选路最小单位;持有 `model_id`、`api_type`、能力、`extra_body`、超时、权重和启停。 |
|
||||
| **api_type** | 模型绑定的调用形态:`chat`、`images`、`images_edits`、`gemini`;不属于 Provider。 |
|
||||
| **RoutePool** | 按 text/image 组织的一组 ProviderModel,用于加权和故障转移。 |
|
||||
| **Generation** | 一次图或文生成;统一在 `generations`,以 `kind=text|image` 区分。 |
|
||||
| **rendered_prompt** | 实际发给上游的完整提示词,提交后必须持久化。 |
|
||||
| **attempts** | 脱敏、限长的尝试链路,至少包含 ProviderModel、分类错误、耗时和时间。 |
|
||||
| **租约** | `lease_owner + lease_token + lease_until`;token 是最终写入 CAS 的所有权凭据。 |
|
||||
| **幂等键** | 用户作用域内标识同一次提交;唯一约束为 `(user_id,idempotency_key)`。 |
|
||||
| **prompt template** | 按 kind 配置的默认提示词模板;MVP-0 必须有数据配置,具体中性措辞待确认。 |
|
||||
| **图片角色** | 第一张默认 primary,其余 reference;MVP-1 才允许编辑、备注和排序。 |
|
||||
| **点数** | 用户余额,只读展示,不参与生成判断或事务。 |
|
||||
|
||||
## 工单状态
|
||||
|
||||
沿用 DevHarness:待确认、已确认、开发中、待验收、已交付、已停止。状态含义与流转规则见 [开发工作流](Development-Workflow.-) 和 [产品需求总览](Product-Requirements-Overview.-)。
|
||||
单元任务状态沿用 DevHarness:**待确认、待实施、进行中、阻塞、待验收、已完成**。需求总览中的长期需求状态是另一套:**待确认、已确认、开发中、待验收、已交付、已停止**。两者不得混写;详细流转见 [开发工作流](Development-Workflow.-) 和 [产品需求总览](Product-Requirements-Overview.-)。
|
||||
|
||||
## 稳定业务规则
|
||||
|
||||
### 生成流程
|
||||
### 数据与约束
|
||||
|
||||
1. 提交生成是**异步**的:接口立即返回,页面插入 loading 占位卡片,完成后原地替换为缩略图。提交链路不得同步调用上游。
|
||||
2. `generations.status` 状态机:`pending → running → succeeded | failed`。只允许沿箭头前进,不允许从终态回到 `running`;重试通过新的 attempt 记录在同一条记录上,`attempt_count` 递增。
|
||||
3. 租约到期是唯一允许把 `running` 拉回可取走状态的机制,且必须写入失败原因或递增 `attempt_count`,不得静默重放。
|
||||
4. 一次生成的 `rendered_prompt`、`attempts`、`latency_ms` 必须落库,失败时还要写 `error_code` 与 `error_message`。
|
||||
5. 成功的图片结果必须同时产出 256px 缩略图;没有缩略图的输出视为未完成。
|
||||
6. `idempotency_key` 相同的提交视为同一次生成,不重复创建记录。
|
||||
1. `users.email`(或最终选定的登录名)规范化后唯一;管理员仍使用独立 `sys_user`。
|
||||
2. ProviderModel 至少以 Provider、模型标识和 api_type 形成不重复绑定;所有外键、删除行为和软删除策略在首个迁移工单明确,不依赖 GORM 自动推断。
|
||||
3. `generations` 至少有 `(status,lease_until,created_at)` 取任务索引、`(user_id,created_at)` 历史索引和 `(user_id,idempotency_key)` 唯一约束。
|
||||
4. 输入、输出、attempt 与 generation 的关系使用显式外键/索引;生产表、`sys_*` 基线、菜单和 API 种子都由可逆 SQL 管理。
|
||||
5. `extra_body` 只允许 JSON 对象并设置大小上限;不能覆盖认证、目标 URL、模型标识和安全超时等保留字段。
|
||||
|
||||
### 选路与失败处理
|
||||
### 生成与租约
|
||||
|
||||
7. 候选来自路由池中启用且未熔断的 ProviderModel,按 `weight` 平滑加权后打乱顺序。
|
||||
8. 故障转移最多尝试 `maxFailover` 家(默认 3),每次尝试都要记入 `attempts`。
|
||||
9. `retryable` 判定:`429`、`5xx`、超时、连接错误 → 换下一家;`400`、`401`、内容策略拒绝 → 立即返回,不换家。这条规则写反的代价是烧光所有 provider 配额。
|
||||
10. 连续 N 次失败打开熔断,冷却后半开试探(gobreaker 默认语义)。熔断中的候选不参与选路。
|
||||
11. 按 provider 分池限流,避免单家并发超限触发 429。
|
||||
6. 提交异步返回;同步链路只校验、保存输入和写 pending,不调用上游。
|
||||
7. 状态机为 `pending → running → succeeded|failed`,终态不可回退。租约过期的 running 在原子认领中只替换 owner/token/until,不回写 pending。
|
||||
8. 最终写入必须同时匹配 generation id、`status=running` 和当前 lease_token;陈旧 worker 更新 0 行后丢弃结果并清理自己的临时文件。
|
||||
9. lease 时长大于“上游超时 + 下载/存储上限 + 安全余量”;续租若实现也必须校验 token。具体生产值在部署前确认。
|
||||
10. attempt_count、attempts 和租约更新保持原子一致。每次失败写分类后的 `error_code`、脱敏且截断的 `error_message`。
|
||||
11. 相同用户和 idempotency_key 返回原任务,不重复保存上传或创建任务;不同用户可以使用相同键。
|
||||
12. 图片成功结果必须有原结果和 256px 缩略图;文件先写同文件系统临时路径,校验完成后原子 rename。
|
||||
|
||||
### 提示词与图片角色
|
||||
### 选路与上游
|
||||
|
||||
12. 角色规则三层可覆盖,后一层覆盖前一层:管理端 `prompt_templates`(按 kind 区分,可多套)→ 用户端「高级设置」可编辑文本框(可改、可清空)→ 与每张图的 `role` / `note` 合成,最终写入 `rendered_prompt`。
|
||||
13. 用户提交的 `role_rule` 为空时使用管理端默认模板;不得把角色规则硬编码在代码里。
|
||||
14. 每张上传图可选填一句备注(例如“参考这张的配色”),备注参与提示词合成。
|
||||
13. MVP-0 直接取唯一启用的 ProviderModel。MVP-1 候选来自启用且未熔断的路由池,最多尝试可配置的 `maxFailover`。
|
||||
14. `429`、`5xx`、超时、连接错误可换下一家;`400`、`401`、内容策略拒绝立即失败。修改必须覆盖四类单元测试。
|
||||
15. 所有请求禁用不受控环境代理,或保证代理连接同样经过目标校验;每次 DNS 解析后、Dial 前拒绝回环、私网、链路本地、组播、未指定等 IPv4/IPv6 地址。
|
||||
16. 每次重定向重新验证 scheme、host、解析地址和端口并限制次数;上游响应中的结果 URL 按同一策略下载。只允许明确的 http/https,不允许凭 URL 绕过。
|
||||
17. 真实连通性测试由管理员明确点击,单次低成本调用,记录操作者/目标/结果并设置冷却;自动测试和日常调试只用 mock。
|
||||
|
||||
### 点数与限流
|
||||
### 提示词与上传
|
||||
|
||||
15. 点数**只读展示**:生成链路不做扣费、退款、余额校验,也没有每日配额。
|
||||
16. 管理端调整余额时**必须在同一事务内写 `point_ledger` 并记 `operator_id`**。虽不涉真钱,可追溯是底线。
|
||||
17. 去掉配额后的刹车是限流:用户维度令牌桶 + 同一用户并发生成数上限(默认 2),防止单人打满所有 provider 配额。
|
||||
18. MVP-0 从 `prompt_templates` 读取 kind 默认模板;第一张图 primary、其余 reference,按上传顺序合成并落 `rendered_prompt`。
|
||||
19. MVP-1 才增加用户 role_rule 覆盖、每图 role/note 和排序;覆盖顺序为管理端默认 → 用户覆盖 → 图片角色/备注。
|
||||
20. 默认模板具体措辞未确认前不得实现;不得沿用 cmhub 的电商专用文案冒充通用默认。
|
||||
21. 上传必须同时限制文件数、单文件/总大小、允许 MIME、实际解码格式和像素数,拒绝扩展名伪装与解压炸弹。阈值均可配置,生产值未确认前不能上线。
|
||||
|
||||
### 安全与审计
|
||||
### 身份、安全和文件
|
||||
|
||||
18. provider 的 `api_key` 必须 AES-GCM 加密落库,主密钥来自环境变量;明文不得进入日志、响应、工单或 Wiki。
|
||||
19. 所有对 provider `base_url` 的出站请求必须经过 SSRF 拦截(DNS 解析后、连接前)。
|
||||
20. 管理端的配置变更写 `config_audit_logs`,记录操作者、动作、目标和变更内容。
|
||||
21. 管理员(`sys_user`)与终端用户(`users`)分表,互不混用凭据。
|
||||
22. MVP-0 只提供受控种子/引导用户,不开放注册;未来注册、找回密码、邮件验证和反滥用需独立设计。
|
||||
23. 密码使用当前批准的强哈希方案及参数并支持升级;登录按账号和来源限速,响应不泄露账号是否存在。
|
||||
24. 浏览器写操作验证 CSRF;会话 Cookie 使用 `Secure`、`HttpOnly`、合适的 `SameSite`,登录后轮换 session id,登出使其失效。
|
||||
25. 原图、结果和缩略图不作为无鉴权公开静态目录;每次访问校验 generation 的用户归属。JSON/HTMX 均不得跨用户查询。
|
||||
26. Provider API Key 用 AES-GCM 加密,保存 key_id;明文不进日志、响应、工单、Wiki、原型或截图。轮换必须可区分旧/新密钥并可回退。
|
||||
27. 管理端配置、真实连通性测试、点数调整和密钥轮换写审计日志;错误和 attempts 只保留排障需要的信息。
|
||||
|
||||
### 明确不做
|
||||
### 点数与范围
|
||||
|
||||
计费扣点、充值与支付回调、汇率、软件授权与设备绑定、内容审核、cmshopee 专用端点、每日生成配额。这些不是“以后再说”,而是本服务的范围之外;有需要时须先建 Epic 重新确认。
|
||||
28. 点数只读展示,生成链路无扣费、退款、余额校验或配额。MVP-0 隐藏点数和 API Key 导航。
|
||||
29. 管理端未来调整余额时,同一事务写 `point_ledger` 和 operator_id。
|
||||
30. 明确不做:计费扣点、充值/支付、汇率、授权/设备绑定、内容审核、cmshopee 专用端点、每日生成配额。新增必须先建 Epic。
|
||||
|
||||
## 新项目需要补充什么
|
||||
|
||||
以下内容尚未确认,实施到对应阶段前必须由负责人拍板,不得由 Agent 假设:
|
||||
以下值尚未确认,实施或上线前必须由负责人确认,Agent 不得猜测:
|
||||
|
||||
- **角色规则默认模板的措辞**:cmhub 那段是电商专用文案,作为 chorus 默认模板是否改为中性表述,取决于目标用户。
|
||||
- **点数来源**:当前定为自有表 + 管理端发放;若要与 cmhub 账本打通,需另设计同步或子账户方案。
|
||||
- **生成物保留期**:本地磁盘增长很快,保留期与清理策略必须在上线前定。
|
||||
- **注册赠送点数**:是否开启、赠送多少,做成一条系统配置。
|
||||
- **限流具体阈值**:令牌桶速率、并发上限的生产取值。
|
||||
- **上游具体名单**:首个可用于联调的 provider、模型和额度来源。
|
||||
- 默认中性 prompt template 的准确措辞和变量格式;
|
||||
- 上传数量、单文件/总大小、MIME、像素上限;
|
||||
- 上游/存储超时、lease 时长、安全余量、worker 数和最大重试;
|
||||
- 首个 Provider/模型、真实连通性检查的最小请求与冷却时间;
|
||||
- 生成物保留期、备份范围、磁盘告警和清理策略;
|
||||
- 生产限流阈值及未来是否开放注册;
|
||||
- 点数来源目前锁定为自有表与管理端发放;若与 cmhub 对接须另建 Epic。
|
||||
|
||||
@@ -2,122 +2,147 @@
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Local-Development-and-Verification
|
||||
wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Local-Development-and-Verification.-
|
||||
wiki_revision: 47b4993d26695f8513c4b3212781b6c6f4c6540c
|
||||
synchronized_at: 2026-08-20T03:29:28Z
|
||||
wiki_revision: 4cb7c477267bfc28c8582e275e02e20acfc2c83e
|
||||
synchronized_at: 2026-08-20T07:01:16Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
# 本地开发与验证
|
||||
|
||||
本页覆盖两类工作:**文档与流程**(现在就能跑)和 **Go 服务**(MVP-0 实现工单落地后可跑)。标注“MVP-0 落地后”的步骤在代码存在前会失败,这是预期的。
|
||||
本页区分“现在可执行的文档流程”和“产品代码落地后可执行的 Go/前端/数据库流程”。代码不存在或本机版本不满足时必须如实记录,不把预期当作通过。
|
||||
|
||||
## 环境要求
|
||||
|
||||
| 项 | 要求 | 检查命令 |
|
||||
| 项 | 目标要求 | 检查命令 |
|
||||
|---|---|---|
|
||||
| Git | 任意近期版本 | `git --version` |
|
||||
| Python 3 | 仅标准库,供 Harness 工具使用 | `python --version` |
|
||||
| Go | 1.22 或更高 | `go version` |
|
||||
| MySQL | 8.0(本地或容器),需支持 `SKIP LOCKED` | `mysql --version` |
|
||||
| golang-migrate | 命令行工具 | `migrate -version` |
|
||||
| Tailwind CLI | standalone 可执行文件,无需 npm | `tailwindcss --help` |
|
||||
| Git | 近期版本 | `git --version` |
|
||||
| Python | Python 3 标准库 | `python --version` |
|
||||
| Go | **1.26.5** | `go version` |
|
||||
| MySQL | 8.0,支持 SKIP LOCKED | `mysql --version` |
|
||||
| golang-migrate | 可执行文件 | `migrate -version` |
|
||||
| Node | >=22,仅 admin-ui | `node --version` |
|
||||
| pnpm | 9.15.1,由 packageManager/Corepack 固定 | `pnpm --version` |
|
||||
| Tailwind | standalone,仅 portal 样式 | `tailwindcss --help` |
|
||||
|
||||
不需要 Node.js、Redis 或消息队列。用到的环境变量:
|
||||
2026-08-20 盘点:本机 Go 1.23.0、MySQL 5.7.38、未安装 migrate,不能用于产品构建或迁移验收;Node 22.22.1 可用,但当前全局 pnpm 11.21.0,应按 admin-ui 的 `packageManager` 使用 pnpm 9.15.1。升级环境必须单独完成,不伪造验证结果。
|
||||
|
||||
| 变量 | 用途 | 缺失后果 |
|
||||
|---|---|---|
|
||||
| `CHORUS_DSN` | MySQL 连接串 | 服务无法启动 |
|
||||
| `CHORUS_MASTER_KEY` | provider api_key 的 AES-GCM 主密钥 | 无法解密上游密钥,生成全部失败 |
|
||||
| `GITEA_TOKEN` | Wiki 同步与工单操作 | 只影响文档同步,不影响服务 |
|
||||
固定来源:
|
||||
|
||||
凭据只通过环境变量提供,不写入仓库。
|
||||
- `D:\github\goadmin\go-admin` @ `f06540883b41d03782bb6b2c4150f298f328c6b6`
|
||||
- `D:\github\goadmin\go-admin-ui` @ `67d393d713877572fab0b897296a4c1d525fc81d`
|
||||
- `D:\github\goadmin\go-admin-doc` @ `424855aacf6905f3fde860c3331385cb25529a0d`
|
||||
|
||||
只从这些固定提交审查/导入,不直接在其工作区开发 chorus,也不让 chorus 构建依赖绝对路径。
|
||||
|
||||
主要环境变量:
|
||||
|
||||
| 变量 | 用途 |
|
||||
|---|---|
|
||||
| `CHORUS_DSN` | MySQL 连接 |
|
||||
| `CHORUS_MASTER_KEY` / 对应 key ring 配置 | Provider Key AES-GCM 解密与轮换 |
|
||||
| `CHORUS_SESSION_KEY` | portal 会话,必须与主密钥分离 |
|
||||
| `CHORUS_STORAGE_ROOT` | 受保护生成物目录 |
|
||||
| `CHORUS_ENV` | `development/test/production`,生产强制安全开关 |
|
||||
| `GITEA_TOKEN` | Wiki/工单操作,不影响产品服务 |
|
||||
|
||||
## 第一次运行
|
||||
|
||||
### 1. 检查工作区与文档结构
|
||||
### 1. 文档与工作区
|
||||
|
||||
```powershell
|
||||
git status --short --branch
|
||||
python dev_scripts/harness.py check --strict
|
||||
python -m unittest discover -s tests -v
|
||||
$env:GITEA_URL = "https://git.ilapage.cn"
|
||||
python dev_scripts/harness.py sync --check
|
||||
```
|
||||
|
||||
预期:分支干净、输出“DevHarness 检查通过”、所有测试通过。
|
||||
### 2. 准备隔离数据库(产品代码落地后)
|
||||
|
||||
### 2. 准备数据库(MVP-0 落地后)
|
||||
创建两个无生产数据的库:一个跑应用迁移测试,一个用于 go-admin 结构研究/代码生成。两者不得指向共享或生产实例。
|
||||
|
||||
```powershell
|
||||
mysql -u root -p -e "CREATE DATABASE chorus DEFAULT CHARSET utf8mb4;"
|
||||
$env:CHORUS_DSN = "user:pass@tcp(127.0.0.1:3306)/chorus?parseTime=true&charset=utf8mb4"
|
||||
migrate -path migrations -database "mysql://$env:CHORUS_DSN" up
|
||||
mysql -u root -p -e "CREATE DATABASE chorus_test DEFAULT CHARSET utf8mb4;"
|
||||
mysql -u root -p -e "CREATE DATABASE chorus_codegen DEFAULT CHARSET utf8mb4;"
|
||||
$env:CHORUS_DSN = "user:pass@tcp(127.0.0.1:3306)/chorus_test?parseTime=true&charset=utf8mb4"
|
||||
migrate -path migrations -database "<golang-migrate MySQL URL>" up
|
||||
migrate -path migrations -database "<golang-migrate MySQL URL>" down
|
||||
migrate -path migrations -database "<golang-migrate MySQL URL>" up
|
||||
```
|
||||
|
||||
预期:迁移版本前进,`generations` 等表已创建。
|
||||
连接串格式必须按 golang-migrate 的 MySQL driver 验证,不把 GORM DSN 直接拼成未经验证的 URL。每个迁移先 up、down、再 up;从空库重放全部迁移才算通过。
|
||||
|
||||
### 3. 配置一个上游(MVP-0 落地后)
|
||||
### 3. go-admin schema-first 生成(MVP-1)
|
||||
|
||||
MVP-0 阶段管理端尚未接入,用一次性命令写入一个 provider 与 provider_model:
|
||||
1. 先在 `chorus_codegen` 执行同一套版本化 SQL。
|
||||
2. 启动仅限本机的 admin/admin-ui 开发实例。
|
||||
3. 从数据库表列表导入业务表元数据,配置并预览生成结果。
|
||||
4. 生成 Go/Vue 文件后检查输出路径、字段类型、敏感字段、权限和重复代码。
|
||||
5. 不直接采用“生成迁移脚本”或 `/gen/todb` 的副作用;把菜单/API 配置整理成带 down 的 SQL。
|
||||
6. 清空隔离库,从 migrations 重放并重新生成/构建。
|
||||
7. 生产构建验证 dev-tools 路由不存在;仅隐藏菜单不算关闭。
|
||||
|
||||
```powershell
|
||||
$env:CHORUS_MASTER_KEY = "<32 字节 base64 主密钥>"
|
||||
go run ./cmd/seedprovider --name dev --base-url https://<上游> --api-type chat --model <模型名>
|
||||
```
|
||||
go-admin AutoMigrate 不进入以上标准流程。如确需对照固定提交初始化结构,人工在额外可丢弃库运行一次,导出结构差异后销毁;禁止连接共享/生产库,也禁止把该命令放入应用启动、容器入口或部署脚本。
|
||||
|
||||
预期:命令输出新建的 provider_model id;数据库中 `api_key_enc` 是密文而非明文。
|
||||
### 4. 配置 mock 上游与种子用户(MVP-0)
|
||||
|
||||
### 4. 启动服务并走通一次生成(MVP-0 落地后)
|
||||
MVP-0 不开放注册。使用可重复、不会在日志输出密钥的种子命令建立测试用户、默认 prompt template、Provider 和 ProviderModel。默认先连接 mock HTTP 上游,覆盖文本、图片、429、5xx、超时、连接错误、400、401 和内容策略拒绝。
|
||||
|
||||
真实 Provider 的连通性检查只能由操作者明确触发一次低成本请求,并核对审计与冷却;不能作为自动测试或反复调试方式。
|
||||
|
||||
### 5. 启动 portal(MVP-0)
|
||||
|
||||
```powershell
|
||||
go run ./portal
|
||||
```
|
||||
|
||||
预期日志包含监听端口与“worker started”。然后在浏览器打开 `http://127.0.0.1:8080`:
|
||||
浏览器验证:
|
||||
|
||||
1. 注册或用种子用户登录,进入双栏首页;
|
||||
2. 切到「文生文」,输入一句提示词,点生成;
|
||||
3. 左栏立即出现 loading 卡片,右栏可见状态;
|
||||
4. 数秒后卡片原地替换为结果,轮询停止(响应中不再有 `hx-trigger`);
|
||||
5. 切到「图生图」,上传 1~2 张图,标记主体图与参考图,再生成一次;
|
||||
6. 数据库中该条 `generations` 的 `status=succeeded`,`rendered_prompt` 非空,`attempts` 有一条记录,`generation_outputs.thumb_path` 有值。
|
||||
|
||||
**第 6 步是 MVP-0 的验收口径**:页面看到结果只是表象,落库字段齐全才算流程真正跑通。
|
||||
1. 用种子用户登录,不应出现公开注册链接;
|
||||
2. 桌面双栏、375px 移动单列均可完成文本和图片提交;
|
||||
3. 第一张图自动 primary,其余 reference,MVP-0 没有角色编辑/拖拽;
|
||||
4. 提交后立即显示 queued/running,终态停止轮询;
|
||||
5. 文本、图片、失败、网络轮询错误和认证过期都有明确界面;
|
||||
6. 数据库中 `rendered_prompt`、`attempts`、latency 或失败 error 字段齐全;
|
||||
7. 输出通过鉴权访问,不能猜 URL 读取其他用户文件。
|
||||
|
||||
## 常用调试方式
|
||||
|
||||
| 症状 | 先看哪里 |
|
||||
| 症状 | 先看 |
|
||||
|---|---|
|
||||
| 卡片一直转圈 | `generations.status`。仍是 `pending` 说明 worker 没取到任务;`running` 且 `lease_until` 已过期说明 worker 崩了或卡在上游 |
|
||||
| 结果不对但没报错 | `generations.rendered_prompt`——多数“模型不听话”其实是提示词合成错了 |
|
||||
| 生成失败 | `generations.attempts` 与 `error_message`,能看出是哪一家、什么错误、耗时多少 |
|
||||
| 上游连不上 | 先确认不是 SSRF 拦截命中(日志中的拦截记录),再查 `base_url` 与密钥解密 |
|
||||
| 页面样式丢失 | Tailwind CLI 是否在 watch,`portal/web/static/` 产物是否存在 |
|
||||
| pending 不动 | worker 是否启动、取任务索引和数据库版本 |
|
||||
| running 租约过期 | lease_owner/token/until、上游超时、陈旧 worker CAS |
|
||||
| 成功但内容不对 | rendered_prompt 与 prompt template 版本 |
|
||||
| 400/401 尝试多家 | retryable 实现错误 |
|
||||
| 文件存在但页面 403/404 | 用户归属、原子落位、缩略图记录 |
|
||||
| 上游连不上 | SSRF 日志、DNS/IPv6、redirect、proxy 和 base_url |
|
||||
| 管理端生成异常 | 是否先迁移再导表,是否误把菜单脚本当业务 DDL |
|
||||
| 样式丢失 | portal Tailwind 产物或 admin-ui pnpm/Vue CLI 构建 |
|
||||
|
||||
调试上游时用 mock HTTP 服务代替真实上游:`internal/core/provider` 的测试已提供可复用的 mock,能构造 429、超时、400 和内容拒绝四种响应。不要用真实额度反复试错。
|
||||
不要在日志、SQL、工单或 Wiki 中贴 API Key、Cookie、真实用户输入和生产文件。
|
||||
|
||||
单条 SQL 快速定位:
|
||||
## 测试策略
|
||||
|
||||
```sql
|
||||
SELECT id, kind, status, provider_model_id, attempt_count, error_code, latency_ms
|
||||
FROM generations ORDER BY id DESC LIMIT 10;
|
||||
```
|
||||
- 迁移:空 MySQL 8 隔离库逐个/全量 up-down-up,验证索引、外键、唯一约束和种子幂等。
|
||||
- 队列:并发认领、租约过期重新认领、旧 token 最终写 0 行、优雅退出、attempt 原子性。
|
||||
- Provider:全部 retryable 类别使用 mock,不消耗真实额度。
|
||||
- SSRF:IPv4/IPv6 私网、DNS rebinding、redirect 链、环境代理、结果 URL。
|
||||
- 安全:密码/session/CSRF、登录节流、跨用户任务与文件、错误脱敏、密钥轮换。
|
||||
- 浏览器:HTMX 动态状态、终态停止、认证过期、网络错误;375/768/1024,无主区域横向滚动,键盘和 reduced-motion。
|
||||
- 供应链:Go/Node 依赖锁定、漏洞和许可证检查按实现工单确定命令。
|
||||
|
||||
## 完成修改前
|
||||
|
||||
按顺序执行,全部通过才算完成:
|
||||
|
||||
```powershell
|
||||
git status --short --branch
|
||||
go build ./...
|
||||
go vet ./...
|
||||
go test ./...
|
||||
pnpm --dir admin-ui install --frozen-lockfile
|
||||
pnpm --dir admin-ui build:prod
|
||||
python dev_scripts/harness.py check --strict
|
||||
python -m unittest discover -s tests -v
|
||||
python dev_scripts/harness.py sync --check
|
||||
git diff --check
|
||||
```
|
||||
|
||||
另外确认:
|
||||
|
||||
- 改了表结构 → 有对应 `migrations/` 文件,且 up 与 down 各跑过一次;
|
||||
- 改了 `retryable`、SSRF、加解密 → 有针对性单元测试;
|
||||
- 改了页面结构、流程、状态、权限或异常处理 → 有 `prototypes/<工单号>/<版本>/index.html` 审核快照并已确认;
|
||||
- 改了入口、命令、业务规则或排错方式 → 已评估对 `docs/` 的影响;
|
||||
- 未执行或无法覆盖的验证 → 已写进工单,不得默认“应该没问题”。
|
||||
只运行受当前范围影响且实际存在的产品命令;不存在或因工具版本不能执行的项写入工单。涉及迁移、安全、队列、权限或 UI 时,还必须执行上面的专项验证并记录结果。
|
||||
|
||||
+69
-54
@@ -2,100 +2,115 @@
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Common-Changes
|
||||
wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Common-Changes.-
|
||||
wiki_revision: becf3fdead14674cb74a7a48c4792c06eb9d454b
|
||||
synchronized_at: 2026-08-20T03:29:30Z
|
||||
wiki_revision: ec313fafcc8df6060e650e3d9ddf95be8d7eb63f
|
||||
synchronized_at: 2026-08-20T07:01:20Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
# 常见修改指南
|
||||
|
||||
本页面向接手简单维护的初级程序员。每一类修改都给出改哪里、验证什么、什么时候必须停下来。
|
||||
本页面向接手维护的初级程序员。先判断风险,再按工单范围修改;数据库、权限、密钥、SSRF、队列和生产代码生成都不是“顺手调整”。
|
||||
|
||||
## 风险分级
|
||||
|
||||
| 级别 | 例子 | 处理方式 |
|
||||
|---|---|---|
|
||||
| **低** | 页面显示文案、注释、文档措辞、日志文本 | 可以直接改,跑最小验证 |
|
||||
| **中** | 现有页面的样式与布局微调、新增一个只读字段展示、增加一条日志 | 建单,按本页步骤改,跑完整验证 |
|
||||
| **高** | 选路与 `retryable` 判定、熔断参数、SSRF 校验、密钥加解密、数据库迁移、队列租约、身份与权限、点数余额写入、删除数据或不可逆操作 | **停止**,交给 Agent 分析并等待人工确认方案 |
|
||||
| 低 | 不改变含义/布局/可访问性的显示文案,注释,文档措辞 | 最小验证;符合豁免时可不建单 |
|
||||
| 中 | 已确认页面的小样式、只读字段、普通日志、mock Provider 配置 | 建单并运行受影响测试 |
|
||||
| 高 | retryable、SSRF、加密/轮换、迁移、租约/CAS、身份权限、点数写入、删除/清理 | 停止,Agent 分析并等待人工确认 |
|
||||
|
||||
判断不了级别时按高风险处理。“看起来只改一行”不是低风险的理由——`retryable` 的一行就能烧光所有上游配额。
|
||||
风险按影响判断,不按行数判断。`internal/core` 新增 Gin/go-admin/gobreaker/imaging import 也是高风险边界破坏。
|
||||
|
||||
## 修改用户端显示文案
|
||||
|
||||
1. 在 `portal/web/templates/` 中定位文案所在模板。
|
||||
2. 只改用户看到的文字,不要顺手改变量名、模板名、CSS 类名或字段名。
|
||||
3. 验证:`go build ./...`,启动 `go run ./portal`,在浏览器确认该页面文字与布局。
|
||||
1. 在 `portal/web/templates/` 定位文案。
|
||||
2. 不改模板名、字段、国际化键、流程暗示、布局或可访问性。
|
||||
3. 启动 portal,在 375/768/1024 宽度检查文字不溢出、不遮挡且状态含义准确。
|
||||
|
||||
纯显示文案且不改变语义、流程、权限、状态、接口、布局或可访问性时不需要建单,但仍要做最小界面检查。改的是错误提示的**含义**、按钮的**行为暗示**或任何影响用户判断的措辞时,按中风险建单处理。
|
||||
错误提示、按钮含义或会影响用户判断的文字不是纯显示豁免,必须建单。
|
||||
|
||||
## 修改角色规则默认模板
|
||||
## 修改 prompt template
|
||||
|
||||
角色规则不是代码常量,是数据:改 `prompt_templates` 表中对应 `kind` 的默认模板,或通过管理端页面修改。
|
||||
默认模板是 `prompt_templates` 数据,不是 Go 常量:
|
||||
|
||||
- **不要**把角色规则写回 Go 代码里——三层覆盖(管理端模板 → 用户端可编辑框 → 每张图 role/note)是已确认的设计。
|
||||
- 改完用一次真实生成验证,检查 `generations.rendered_prompt` 是否如预期。
|
||||
- MVP-0 只读取 kind 默认模板,第一张图 primary、其余 reference;
|
||||
- 中性默认措辞尚未确认,不能自行沿用 cmhub 电商文案;
|
||||
- MVP-1 才允许用户 role_rule、每图 role/note 和排序;
|
||||
- 修改后优先用 mock 生成并核对 `rendered_prompt`,真实上游只做一次经授权的低成本验收。
|
||||
|
||||
## 增加或调整一个上游 Provider
|
||||
## 增加或调整 ProviderModel
|
||||
|
||||
1. 优先通过管理端页面配置(管理端接入前用种子命令,见 [本地开发与验证](Local-Development-and-Verification.-))。
|
||||
2. 确认 `api_type` 选对:文生文用 `chat`,图生图用 `images_edits`。
|
||||
3. 保存后用「连通性测试」打一次真实请求验证;没有该按钮时手工提交一次生成。
|
||||
4. `api_key` 只填进配置,不要出现在工单、截图或日志里。
|
||||
1. Provider 只配置 `base_url` 和加密 Key;在 ProviderModel 选择 `api_type`、模型、能力、`extra_body` 和超时。
|
||||
2. 验证 URL 会经过 DNS/DialContext、redirect、IPv6、代理和结果 URL 防护。
|
||||
3. 自动验证使用 mock。管理端“连通性测试”必须由操作者点击、单次低成本、带审计和冷却。
|
||||
4. API Key 不得出现在参数回显、响应、日志、截图、工单或 Wiki。
|
||||
|
||||
新增的是一种**上游协议形态**(而非一家新服务商)时属于高风险:需要在 `internal/core/provider/` 新增实现,必须建单并补齐 mock 测试。
|
||||
新增协议形态必须建高风险工单,补齐 retryable 与 SSRF 测试。
|
||||
|
||||
## 数据库与 go-admin 代码生成
|
||||
|
||||
标准流程:
|
||||
|
||||
```text
|
||||
写 migrations up/down → 空 MySQL 8 up/down/up
|
||||
→ 隔离 codegen 库执行 up → go-admin-ui 导入已有表
|
||||
→ 配置/预览/生成 → 人工审查
|
||||
→ 菜单/API 配置转成可逆 SQL → 从空库全量重放
|
||||
```
|
||||
|
||||
- 不在应用启动、部署或生产执行 AutoMigrate。
|
||||
- AutoMigrate 只可由人工在额外可丢弃数据库研究固定提交结构,用完销毁。
|
||||
- “生成迁移脚本”不是业务 DDL;`/gen/todb` 会改菜单,`/gen/toproject` 会写源码。
|
||||
- 生成文件不是可信输入:检查敏感字段、权限、路由、路径、重复代码和无关格式化。
|
||||
- 生产必须不注册 dev-tools 路由,隐藏菜单不够。
|
||||
|
||||
## 修改 Wiki 文案
|
||||
|
||||
长期文档的事实来源是 Gitea Wiki,`docs/` 是只读镜像(Wiki 初始化完成后生效)。
|
||||
长期文档以 Gitea Wiki 为事实来源:
|
||||
|
||||
```text
|
||||
修改 Wiki → 读取确认 → 导出 docs → 校验差异 → 提交镜像
|
||||
修改 Wiki → 在线回读 revision → sync 导出 docs → sync --check → 提交
|
||||
```
|
||||
|
||||
```powershell
|
||||
$env:GITEA_URL = "https://git.ilapage.cn"
|
||||
python dev_scripts/harness.py sync
|
||||
python dev_scripts/harness.py sync --check
|
||||
```
|
||||
|
||||
不要直接编辑带 `generated: true` 头的本地文件。Wiki 尚未初始化的阶段允许直接改 `docs/`,但必须在工单说明,并在初始化时把内容搬到 Wiki。
|
||||
不得直接编辑带 `generated: true` 的镜像。新建项目专用页面时更新 `wiki-docs.json` 和 Home 导航;若要把它提升为所有项目强制核心文档,再同步修改 Harness 与成功/失败测试。
|
||||
|
||||
## 调整 Harness 检查
|
||||
|
||||
`dev_scripts/harness.py` 的 `check` 子命令负责校验必需文件、核心文档章节和工单模板字段。
|
||||
1. 修改 `CORE_DOCUMENT_REQUIREMENTS`、`REQUIRED_FILES` 或模板字段。
|
||||
2. 同时补成功和失败用例。
|
||||
3. 执行:
|
||||
|
||||
1. 修改 `CORE_DOCUMENT_REQUIREMENTS`、`REQUIRED_FILES` 或模板字段列表。
|
||||
2. 在 `tests/` 中同时补充**成功用例和失败用例**——只有成功用例的检查等于没有检查。
|
||||
3. 验证:
|
||||
```powershell
|
||||
python -m unittest discover -s tests -v
|
||||
python dev_scripts/harness.py check --strict
|
||||
```
|
||||
|
||||
```powershell
|
||||
python -m unittest discover -s tests -v
|
||||
python dev_scripts/harness.py check --strict
|
||||
```
|
||||
|
||||
新增核心文档时必须同时更新 `wiki-docs.json` 映射、`docs/README.md` 导航和 Harness 检查,三者缺一会导致同步或检查失败。
|
||||
不要为了让检查变绿而削弱安全、Wiki 主源或任务归档边界。
|
||||
|
||||
## 看懂 Agent 的修改
|
||||
|
||||
审查 Agent 提交时按这个顺序看:
|
||||
|
||||
1. **范围**:改动文件是否都落在工单声明的范围内?出现工单没提到的文件要问清楚。
|
||||
2. **边界**:`internal/core` 里有没有混进 gin 或 go-admin 的 import?生成链路有没有碰点数?
|
||||
3. **状态机**:`generations.status` 的写入是否只沿 `pending → running → succeeded | failed` 前进?
|
||||
4. **失败路径**:`retryable` 的分支有没有写反?失败时 `attempts`、`error_code`、`error_message` 是否都落库?
|
||||
5. **迁移**:表结构变更有没有对应 SQL 文件?down 能不能跑?
|
||||
6. **测试**:新增逻辑有没有测试,测试是否覆盖失败分支而不只是happy path。
|
||||
7. **文档**:入口、命令、规则变了,`docs/` 是否同步评估过。
|
||||
|
||||
任何一条答不上来就退回让 Agent 解释,不要凭“测试过了”放行。
|
||||
1. **范围**:文件与工单一致,无无关重构。
|
||||
2. **依赖**:core 只含 GORM/标准库;platform 通过接口注入。
|
||||
3. **同步链路**:提交不调用上游、不读写点数。
|
||||
4. **状态**:不把 running 退回 pending;最终写入校验 lease_token,旧 worker 更新 0 行。
|
||||
5. **错误**:retryable 四类正确,attempts/error 脱敏落库。
|
||||
6. **安全**:请求/redirect/result URL 都走 SSRF;文件访问校验用户归属;浏览器写操作有 CSRF。
|
||||
7. **迁移**:所有表和配置都有 up/down、空库验证,无 AutoMigrate。
|
||||
8. **生成代码**:差异已人工审查,生产 dev-tools 不存在。
|
||||
9. **UI**:完整状态、375/768/1024、键盘、焦点、44px、reduced-motion。
|
||||
10. **证据**:测试是真实执行结果,未验证项写入工单。
|
||||
|
||||
## 必须停止的情况
|
||||
|
||||
遇到以下情况停止修改,交给 Agent 分析并等待人工确认:
|
||||
|
||||
- 需要改选路、熔断、`retryable`、SSRF、加解密、队列租约中的任意一项;
|
||||
- 需要新增或修改数据库迁移;
|
||||
- 需要触碰身份认证、权限或 API Key;
|
||||
- 需要写入点数余额或 `point_ledger`;
|
||||
- 需要删除数据、清理生成物或执行任何不可逆操作;
|
||||
- 修改会改变已确认原型的页面结构、流程、状态、权限或异常处理;
|
||||
- 你不确定这属于哪一级风险。
|
||||
- 修改 retryable、熔断、SSRF、密钥、租约/CAS、身份权限或迁移;
|
||||
- 执行 AutoMigrate、`/gen/todb`、生产代码生成或手工改共享/生产库;
|
||||
- 写点数、删除数据/文件、运行清理任务或做不可逆回退;
|
||||
- 调整上传/超时/租约/保留期限等未确认生产阈值;
|
||||
- 改变已确认原型的结构、流程、状态、权限或异常处理;
|
||||
- 真实上游测试可能反复消耗额度;
|
||||
- 无法判断风险或同一位置两次仍无根因。
|
||||
|
||||
+81
-53
@@ -2,86 +2,114 @@
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Troubleshooting
|
||||
wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Troubleshooting
|
||||
wiki_revision: 9400aa641e95bccd3903efb6dd935c06a59048e8
|
||||
synchronized_at: 2026-08-20T03:29:32Z
|
||||
wiki_revision: d37761a10ea18c62e0cd0680e65d9b462d79b4f5
|
||||
synchronized_at: 2026-08-20T07:01:24Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
# 故障排查
|
||||
|
||||
## 排查顺序
|
||||
|
||||
遇到问题按下面的顺序走,**不要跳步,也不要直接重置工作区或覆盖本地文档**。
|
||||
|
||||
### 1. 确认环境与工作区
|
||||
### 1. 环境与工作区
|
||||
|
||||
```powershell
|
||||
git status --short --branch
|
||||
go version
|
||||
mysql --version
|
||||
migrate -version
|
||||
node --version
|
||||
pnpm --version
|
||||
```
|
||||
|
||||
工作区有不属于当前任务的修改时先处理干净,否则后面的判断都不可靠。
|
||||
产品构建目标为 Go 1.26.5、MySQL 8.0、Node >=22、pnpm 9.15.1。工具缺失或版本不符先记录环境问题;不要通过降低文档版本或改代码绕过。
|
||||
|
||||
### 2. 判断问题落在哪一段
|
||||
### 2. 判断同步还是异步
|
||||
|
||||
chorus 的请求分成同步提交和异步执行两段,绝大多数问题只在其中一段:
|
||||
|
||||
| 现象 | 大概率在哪段 |
|
||||
| 现象 | 首查 |
|
||||
|---|---|
|
||||
| 点了生成没反应、报 4xx | 同步段(`portal/handler`) |
|
||||
| 卡片出现了但一直转圈 | 异步段(queue / worker) |
|
||||
| 结果出来了但内容不对 | 提示词合成(`generate/prompt.go`) |
|
||||
| 结果失败并有错误码 | provider 调用或选路 |
|
||||
| 提交 4xx、没有卡片 | portal 认证/CSRF/上传/幂等 |
|
||||
| 卡片出现但一直 queued | worker、MySQL 版本、取任务索引 |
|
||||
| running 超时 | lease owner/token/until、上游和存储超时 |
|
||||
| succeeded 但无内容 | output 记录、原子文件、鉴权/缩略图 |
|
||||
| 内容不符 | rendered_prompt 与模板 |
|
||||
| 管理端 CRUD 不符 | migrations、导表元数据和生成差异 |
|
||||
|
||||
### 3. 查库,不要靠猜
|
||||
### 3. 查库但不手工改状态
|
||||
|
||||
```sql
|
||||
SELECT id, kind, status, provider_model_id, attempt_count,
|
||||
error_code, error_message, lease_until, latency_ms, created_at, finished_at
|
||||
FROM generations ORDER BY id DESC LIMIT 10;
|
||||
SELECT id, user_id, kind, status, provider_model_id, attempt_count,
|
||||
error_code, lease_owner, lease_token, lease_until,
|
||||
latency_ms, created_at, finished_at
|
||||
FROM generations
|
||||
ORDER BY id DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
| 观察到 | 含义与下一步 |
|
||||
- pending 不动:worker 未启动、数据库不支持 SKIP LOCKED 或索引/事务错误。
|
||||
- running 且租约过期:新 worker 应直接原子替换 token,不应先改回 pending。
|
||||
- 同一任务出现互相覆盖:检查最终 UPDATE 是否匹配当前 lease_token;陈旧 worker 应影响 0 行。
|
||||
- 400/401/策略拒绝却有多 Provider attempts:retryable 写反。
|
||||
- 429/5xx/超时/连接错误没有故障转移(MVP-1):候选池、上限或熔断状态错误。
|
||||
- failed 没有 error,或 succeeded 没有 rendered_prompt/attempts:违反持久化红线。
|
||||
|
||||
`lease_token` 属运行控制信息,不在对外响应或普通日志完整输出。排查记录使用任务 id 和脱敏摘要。
|
||||
|
||||
### 4. 用 mock 隔离上游
|
||||
|
||||
用 mock 构造成功、429、5xx、超时、连接错误、400、401、内容拒绝和恶意结果 URL。mock 正常而真实异常,再查 ProviderModel、网络和上游;禁止用真实额度循环试错。真实连通性只通过受审计且有冷却的按钮触发。
|
||||
|
||||
### 5. SSRF 与网络
|
||||
|
||||
依次检查:
|
||||
|
||||
1. base_url scheme/host/port;
|
||||
2. DNS 的每个 IPv4/IPv6 结果;
|
||||
3. DialContext 是否拒绝私网、回环、链路本地、组播和未指定地址;
|
||||
4. 每次 redirect 是否重新校验并限制次数;
|
||||
5. 环境代理是否绕开安全 Dial;
|
||||
6. 上游返回的结果 URL 是否复用相同客户端。
|
||||
|
||||
拦截私网是正确行为,不能为联调关闭。需要访问内部 mock 时把 mock 与测试进程放在专用测试网络,并使用测试专用注入点,不在生产配置放行私网。
|
||||
|
||||
### 6. 文件和权限
|
||||
|
||||
- 检查临时文件与最终目录是否同文件系统,rename 是否原子;
|
||||
- 检查失败/陈旧 worker 是否只清理自己创建的临时文件;
|
||||
- 检查数据库 output 与实际文件一致;
|
||||
- 403 多为用户归属校验,404 多为记录/文件不一致;
|
||||
- 不得临时把存储目录改成无鉴权静态目录。
|
||||
|
||||
### 7. go-admin 与代码生成
|
||||
|
||||
| 现象 | 处理 |
|
||||
|---|---|
|
||||
| `status=pending` 且长时间不动 | worker 没起来或没连上库;看 portal 启动日志有没有 “worker started” |
|
||||
| `status=running` 且 `lease_until` 已过期 | worker 崩了或卡在上游;查上游超时设置与进程日志 |
|
||||
| `status=failed`,`error_code` 是 429/5xx | 上游限流或故障;看 `attempts` 是否按预期换了下一家 |
|
||||
| `status=failed`,`error_code` 是 400/401 | 参数或密钥问题;**不应该**发生故障转移,若 `attempts` 有多条说明 `retryable` 写反了 |
|
||||
| `status=succeeded` 但页面无图 | 查 `generation_outputs` 的 `path` 与 `thumb_path`,再查存储目录权限 |
|
||||
| `rendered_prompt` 与预期不符 | 角色规则模板或三层覆盖顺序的问题,不是模型的问题 |
|
||||
| 生成器看不到表 | 先确认隔离库已执行 migrations,再导入元数据 |
|
||||
| “迁移脚本”没有建表 | 它主要生成菜单/API 配置,不是业务 DDL |
|
||||
| 生成后菜单立刻变化 | 可能调用了副作用 `/gen/todb`;停止并恢复隔离库 |
|
||||
| 生成文件落错目录 | 检查固定提交的 generator 配置,人工审查差异 |
|
||||
| 生产能访问 dev-tools | 高风险发布阻断;下线路由而非只隐藏菜单 |
|
||||
| 启动时表结构变化 | 检查是否误接 AutoMigrate,立即停止部署 |
|
||||
|
||||
### 4. 隔离上游
|
||||
|
||||
用 mock 上游重跑一次(`internal/core/provider` 的测试 mock 可构造 429、超时、400、内容拒绝)。mock 下正常、真实上游异常,问题在配置或网络;mock 下同样失败,问题在代码。
|
||||
|
||||
不要拿真实额度反复试错。
|
||||
|
||||
### 5. 检查出站是否被 SSRF 拦截
|
||||
|
||||
`base_url` 指向私网、回环地址或解析结果落在私网时会被 `internal/core/security` 拦截,表现是“连不上但网络看起来正常”。日志中有拦截记录。**拦截生效是正确行为**,要改的是配置,不是拦截规则。
|
||||
|
||||
### 6. 文档与 Harness 相关问题
|
||||
### 8. 文档与 Harness
|
||||
|
||||
| 症状 | 处理 |
|
||||
|---|---|
|
||||
| `harness.py check --strict` 报缺少章节 | 按提示补回该标题,标题文字必须完全一致 |
|
||||
| `harness.py check --strict` 报“项目档案仍有未填写内容” | `docs/00-project-profile.md` 里还有 `<填写` 占位符 |
|
||||
| `sync --check` 报不一致 | 先确认是 Wiki 更新了还是本地被手工改了;本地被改过要恢复后重新同步 |
|
||||
| `sync` 中止并提示存在未提交修改 | 已映射镜像有未提交改动,先提交或还原 |
|
||||
| Wiki 页面读不到、没有 revision | 停止初始化或同步,等 Gitea 恢复后从首个失败页面继续 |
|
||||
| strict 缺章节 | 恢复精确标题 |
|
||||
| sync --check 不一致 | 先判断 Wiki 是否更新;不要直接改镜像 |
|
||||
| sync 因本地镜像改动停止 | 保留用户改动,查清来源后处理 |
|
||||
| Wiki 无 revision | 停止同步,从失败页面继续 |
|
||||
| API 401 | 从安全环境重新加载有效 PAT,不打印令牌 |
|
||||
| MCP 连接失败 | 在工单记录原因后回退 API |
|
||||
|
||||
### 7. 仍未定位
|
||||
### 9. 仍未定位
|
||||
|
||||
在工单中记录:最小复现步骤、`generations` 的相关行、`attempts` 内容、相关日志片段(**去掉密钥**)、已排除的可能性。然后交给 Agent 分析。
|
||||
在工单记录最小复现、任务 id、脱敏 attempts/error、租约时间线、相关日志和已排除项。不得粘贴 API Key、Cookie、真实 prompt、用户文件或生产数据。
|
||||
|
||||
## 必须停止的情况
|
||||
|
||||
以下情况立即停止自行处理,交给 Agent 分析并等待人工确认:
|
||||
|
||||
- 需要修改 `retryable`、熔断参数、选路逻辑或 SSRF 规则;
|
||||
- 需要修改数据库迁移,或需要手工改生产/共享库中的数据;
|
||||
- 需要触碰密钥、加解密、身份认证、权限或 API Key;
|
||||
- 需要写入点数余额或 `point_ledger`;
|
||||
- 需要删除生成物、清理表数据或任何不可逆操作;
|
||||
- 需要绕过失败重跑任务(可能造成重复计费或重复生成);
|
||||
- 日志或报错中出现疑似泄露的密钥、个人数据或生产数据——先停止传播,不要把原文贴进工单或 Wiki;
|
||||
- 你已经在同一处试了两次仍不确定根因。
|
||||
- 需要修改 retryable、SSRF、密钥、租约、迁移、身份或权限;
|
||||
- 需要手工改共享/生产库、回退状态、删除数据或文件;
|
||||
- 想通过 AutoMigrate、关闭 SSRF、公开存储或暴露 dev-tools 临时绕过;
|
||||
- 需要反复真实上游调用;
|
||||
- 日志疑似泄密或包含个人/生产数据;
|
||||
- 同一阻塞尝试两次仍不能确认根因。
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Product-Requirements-Overview
|
||||
wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Product-Requirements-Overview.-
|
||||
wiki_revision: 33319b0f3d34796ade13e4146f28cc5e5eb6d1d1
|
||||
synchronized_at: 2026-08-20T03:29:42Z
|
||||
wiki_revision: 4c7c9352f77e5972be4fa51f0b86025d9d08ea53
|
||||
synchronized_at: 2026-08-20T07:01:33Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
# 产品需求总览
|
||||
@@ -29,107 +29,135 @@ synchronized_at: 2026-08-20T03:29:42Z
|
||||
|
||||
## 当前需求索引
|
||||
|
||||
chorus 尚未建立 Gitea 工单,下表的“实施工单”列在建单后填入编号。全部需求当前状态均为**待确认或已确认但未开工**。
|
||||
工单 [#1](https://git.ilapage.cn/OPC/chorus/issues/1) 正在更新本项目长期技术基线,不代表产品功能已经进入开发。产品功能仍需按下表分别完成设计门禁并建立单元工单。
|
||||
|
||||
| 需求领域 | 用户与场景 | 状态 | 版本 / MVP | 详细说明 | 实施工单 | 原型 | 验收入口 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 核心生成域与单上游打通 | 用户提交提示词(+原图)得到结果;先跑通再谈可用性 | 已确认,未开工 | MVP-0 | [架构与代码地图](Architecture-and-Code-Map.-)、[业务规则](Business-Rules-and-Glossary.-) | 待建单 | 无(非 UI 需求,用架构与数据设计代替) | 待建单 |
|
||||
| 用户端生成页(双栏 + 异步卡片) | 终端用户在浏览器完成一次生成并看到结果 | 已确认,未开工 | MVP-0 | 本页“用户端布局”一节、[架构与代码地图](Architecture-and-Code-Map.-) | 待建单 | **待制作**:新页面,需 HTML 审核快照并经确认 | 待建单 |
|
||||
| 多 Provider 路由池与故障转移 | 单一上游失效时服务仍可用 | 已确认,未开工 | MVP-1 | [业务规则](Business-Rules-and-Glossary.-) 选路与失败处理 | 待建单 | 无(非 UI 需求) | 待建单 |
|
||||
| 管理端配置与记录查看 | 运营配置 Provider/Model、勾选组池、查看生成记录 | 已确认,未开工 | MVP-1 | 本页“管理端页面”一节 | 待建单 | 代码生成器页面无需原型;三个定制页需低保真图 | 待建单 |
|
||||
| 图片角色规则可编辑 | 用户按自己的场景描述“第几张图是什么” | 已确认,未开工 | MVP-1 | [业务规则](Business-Rules-and-Glossary.-) 提示词与图片角色 | 待建单 | 属现有页面新增组件,需组件状态说明 | 待建单 |
|
||||
| 点数只读展示与管理端发放 | 用户看到余额;管理员发放并留痕 | 已确认,未开工 | MVP-2 | [业务规则](Business-Rules-and-Glossary.-) 点数与限流 | 待建单 | 沿用现有设计体系 | 待建单 |
|
||||
| API Key 与程序化调用 | 外部程序按 API Key 调用生成 | 已确认,未开工 | MVP-2 | 本页“程序化调用”一节 | 待建单 | 无(接口需求,用 API 设计代替) | 待建单 |
|
||||
| 限流与生成物保留期 | 防止单人打满上游配额;控制磁盘增长 | 待确认(阈值与保留期未定) | MVP-2 | [业务规则](Business-Rules-and-Glossary.-)“新项目需要补充什么” | 待建单 | 无 | 待建单 |
|
||||
| 需求领域 | 用户与场景 | 需求状态 | MVP | 详细说明 | 实施工单 | 设计证据 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 核心生成域与单上游 | 用户提交提示词/原图得到结果 | 已确认 | MVP-0 | [架构](Architecture-and-Code-Map.-)、[业务规则](Business-Rules-and-Glossary.-) | 待建 | 架构/数据/状态设计 |
|
||||
| 用户端生成页 | 种子用户登录并完成一次异步生成 | 已确认 | MVP-0 | 本页“用户端布局与状态” | 待建 | 新页面 HTML 原型待制作/确认 |
|
||||
| 默认 prompt template | 系统以数据配置而非硬编码合成提示词 | 待确认(具体措辞) | MVP-0 | [业务规则](Business-Rules-and-Glossary.-) | 待建 | 文案与变量设计待确认 |
|
||||
| 多 Provider 路由与故障转移 | 单上游故障时继续服务 | 已确认 | MVP-1 | 业务规则“选路与上游” | 待建 | 架构/状态设计 |
|
||||
| 管理端配置与记录 | 运营配置模型、路由池并排障 | 已确认 | MVP-1 | 本页“管理端页面” | 待建 | 复用型/定制页原型待确认 |
|
||||
| 图片角色编辑 | 用户编辑 role_rule、角色、备注与顺序 | 已确认 | MVP-1 | 业务规则“提示词与上传” | 待建 | 组件状态与键盘交互原型 |
|
||||
| 点数只读与发放 | 用户看余额、管理员发放留痕 | 已确认 | MVP-2 | 业务规则“点数与范围” | 待建 | 原型待确认 |
|
||||
| API Key 与程序调用 | 外部程序提交与查询任务 | 已确认 | MVP-2 | 本页“程序化调用” | 待建 | API/权限设计 |
|
||||
| 限流、保留期、公开注册 | 治理滥用和磁盘;决定是否开放用户获取 | 待确认(阈值/流程) | MVP-2 或后续 | 业务规则“需要补充什么” | 待建 | 安全/运维/认证设计 |
|
||||
|
||||
### MVP 分期
|
||||
|
||||
分期原则:**先让一条数据从提交走到结果全程可运行**,再补可用性,最后补运营与治理能力。前一期没跑通不进入下一期。
|
||||
原则:先完成可观测、可恢复、安全的单上游闭环,再增加可用性与运营能力,最后开放治理能力。前一期的验收门禁未通过,不进入下一期。
|
||||
|
||||
#### MVP-0:跑通全流程的最小闭环(当前阶段)
|
||||
#### MVP-0:最小闭环
|
||||
|
||||
范围:
|
||||
|
||||
1. 迁移建表:`users`、`providers`、`provider_models`、`generations`、`generation_inputs`、`generation_outputs`;
|
||||
2. `internal/core`:`chat` 与 `images_edits` 两种 provider 实现、AES-GCM 密钥加解密、SSRF 拦截、本地存储、缩略图;
|
||||
3. 队列:`SELECT … FOR UPDATE SKIP LOCKED` 取任务 + 租约 + 失败置错;
|
||||
4. 提示词合成:角色规则 + 每张图 role/note + 用户提示词 → `rendered_prompt` 落库;
|
||||
5. `portal`:登录、双栏首页、提交生成、HTMX 每 2 秒轮询卡片、成品卡片不带 `hx-trigger` 从而自动停止轮询;
|
||||
6. worker 跑在 portal 进程内,支持优雅退出;
|
||||
7. 上游配置用一次性种子命令写入,管理端不接入。
|
||||
1. 可逆迁移:`users`、`providers`、`provider_models`、`prompt_templates`、`generations`、`generation_inputs`、`generation_outputs`,含明确索引、外键和唯一约束。
|
||||
2. core 仅使用 GORM/标准库;platform 提供安全 HTTP、本地存储、缩略图和 AES-GCM 实现。
|
||||
3. `chat` 与 `images_edits`;唯一启用 ProviderModel,不做多家故障转移。
|
||||
4. SKIP LOCKED、lease owner/token/until、过期 running 原子重领、最终 CAS、attempt/error 落库。
|
||||
5. `prompt_templates` 提供已确认的默认配置;第一张图片自动 primary,其余 reference。
|
||||
6. portal 只提供受控种子用户登录、桌面双栏与移动单列、异步提交、HTMX 轮询和鉴权结果访问。
|
||||
7. worker 在 portal 内运行并优雅退出;配置使用可重复种子命令,自动测试连接 mock 上游。
|
||||
|
||||
非目标(属于后续期,不是被取消):加权选路、熔断、故障转移、`images` 与 `gemini` 两种 api_type、管理端全部页面、API Key、点数、限流、无限滚动分页、清理任务。
|
||||
MVP-0 非目标:
|
||||
|
||||
验收口径(缺一不算通过):
|
||||
- 公开注册、找回密码和邮件验证;
|
||||
- 图片角色/备注编辑与拖拽排序;
|
||||
- 点数和 API Key 导航/功能;
|
||||
- 多 Provider、熔断、故障转移、`images`、`gemini`;
|
||||
- admin/admin-ui、无限滚动、移动抽屉、限流、清理任务。
|
||||
|
||||
- [ ] 文生文与图生图各成功一次,浏览器可见结果;
|
||||
- [ ] 对应 `generations` 行的 `status=succeeded`、`rendered_prompt` 非空、`attempts` 有记录、`latency_ms` 有值;
|
||||
- [ ] 图生图结果的 `generation_outputs.thumb_path` 有值且缩略图可访问;
|
||||
- [ ] 上游返回 400 时该条记录 `status=failed`、`error_code`/`error_message` 落库,且**没有**发生故障转移;
|
||||
- [ ] 上游超时时租约到期后任务可被重新取走,`attempt_count` 递增;
|
||||
- [ ] provider 的 `api_key` 在库中是密文;
|
||||
- [ ] `base_url` 指向私网时被 SSRF 拦截并有日志;
|
||||
- [ ] `go build ./...`、`go vet ./...`、`go test ./...` 全部通过。
|
||||
验收:
|
||||
|
||||
#### MVP-1:可用性与运营入口
|
||||
- [ ] 文本和图片各成功一次;pending/running/终态界面正确且终态停止轮询。
|
||||
- [ ] `rendered_prompt`、`attempts`、latency 或失败 error 字段完整。
|
||||
- [ ] 相同用户幂等重放不创建第二条任务;不同用户不可读取对方任务或文件。
|
||||
- [ ] 过期 running 被新 token 原子重领,旧 worker 最终写入影响 0 行且不覆盖结果。
|
||||
- [ ] 400/401/策略拒绝立即失败;429/5xx/超时/连接错误分类正确(MVP-0 不换家)。
|
||||
- [ ] 私网、IPv6、redirect、代理和恶意结果 URL 被 SSRF 策略覆盖。
|
||||
- [ ] API Key 密文含 key_id,日志/响应无明文;浏览器写请求有 CSRF,会话安全。
|
||||
- [ ] 图片输出通过鉴权访问且有缩略图;文件原子落位。
|
||||
- [ ] 375/768/1024 无主区域横向滚动,所有主要操作可用键盘和 44px 触控目标完成。
|
||||
- [ ] 迁移在空 MySQL 8 完成 up/down/up;Go 构建、vet、测试和浏览器 E2E 通过。
|
||||
|
||||
路由池与加权选路、gobreaker 熔断与故障转移(含 `retryable` 判定的完整测试)、`images` 与 `gemini` 实现、go-admin 接入与代码生成器 CRUD、三个定制页(路由池编排、Provider 健康看板、生成记录详情)、Provider 连通性测试按钮、角色规则模板三层覆盖的管理端层、生成列表无限滚动。
|
||||
#### MVP-1:可用性与运营
|
||||
|
||||
#### MVP-2:治理与开放能力
|
||||
- 路由池、加权、gobreaker 与最多 N 家故障转移,完整 retryable 测试;
|
||||
- `images`、`gemini`;
|
||||
- 固定 go-admin/go-admin-ui 的 schema-first CRUD 和三个定制页;
|
||||
- 图片 role_rule、role/note、拖拽及键盘等价排序;
|
||||
- 生成历史无限滚动;
|
||||
- 受审计、有冷却的 Provider 单次真实连通性测试。
|
||||
|
||||
API Key 管理与 openapi 端点、点数只读展示与管理端发放(含 `point_ledger` 事务)、用户维度令牌桶与并发上限、按 provider 分池限流、配置审计日志、生成物保留期清理任务、移动端抽屉布局。
|
||||
#### MVP-2:治理与开放
|
||||
|
||||
- API Key/openapi、点数只读与发放账本;
|
||||
- 用户和 Provider 维度限流、配置审计;
|
||||
- 经确认的保留期与清理任务;
|
||||
- 移动端历史抽屉;
|
||||
- 公开注册只有在注册、验证、找回、反滥用和赠送策略单独确认后才进入范围,不能默认随 MVP-2 开放。
|
||||
|
||||
### 已锁定决策
|
||||
|
||||
以下决策已确认,不在实施阶段重新讨论;要改必须先建 Epic:
|
||||
1. Go 重写,只迁移设计,不复用 cmhub Django 代码。
|
||||
2. 管理端来源固定为:
|
||||
- go-admin `f06540883b41d03782bb6b2c4150f298f328c6b6`
|
||||
- go-admin-ui `67d393d713877572fab0b897296a4c1d525fc81d`
|
||||
- go-admin-doc `424855aacf6905f3fde860c3331385cb25529a0d`
|
||||
3. go-admin-ui 实际是 Vue 3.5.41 + Element Plus 2.14.4 + Vue CLI 5.0.9,不按 Vue 2/Vite 设计。
|
||||
4. 生产表结构、`sys_*` 和菜单/API 种子全部经可逆 migrations;AutoMigrate 仅可在隔离可丢弃库作研究参考。
|
||||
5. 管理端代码生成采用“SQL → 隔离库 → 导表 → 预览/生成 → 人工审查 → 配置转可逆 SQL”;生产无 dev-tools。
|
||||
6. 用户端采用 html/template + HTMX + Alpine + Tailwind;不做 SPA。
|
||||
7. core 只依赖 GORM/标准库,gobreaker/imaging 在 platform。
|
||||
8. 不引入 Redis/MQ,使用 MySQL 8 SKIP LOCKED + token/CAS。
|
||||
9. 点数只读,不参与生成;管理员和终端用户分表。
|
||||
10. 生成同步提交不调用上游,所有上游调用只在 worker。
|
||||
11. `api_type` 和协议能力归 ProviderModel,不归 Provider。
|
||||
|
||||
1. 用 Go 重写,不做 Django 代码抽取;带走的是设计(provider 抽象、任务租约、SSRF 校验、密钥加密),不是代码。
|
||||
2. 管理端用 go-admin + go-admin-ui。
|
||||
3. 用户端不用前端框架:html/template 服务端渲染 + HTMX + Alpine.js + Tailwind。
|
||||
4. 点数只做展示:无扣费、无退款、无余额校验、无配额;余额来自自有表,管理端发放。
|
||||
5. 图片角色规则从硬编码改为用户端可编辑提交,默认值来自管理端模板。
|
||||
6. 上游默认只支持 OpenAI 规范;可勾选多个 provider 的 model 做均衡 + 故障转移。
|
||||
7. 不引入 Redis / MQ,任务队列用 MySQL 8.0 `SELECT … FOR UPDATE SKIP LOCKED`。
|
||||
### 用户端布局与状态
|
||||
|
||||
### 用户端布局
|
||||
桌面(>=1024px)采用历史列表 + 主工作区双栏;平板可收窄历史区;MVP-0 手机使用单列顺序布局,不依赖尚未实现的抽屉。首屏必须能看到当前结果/状态和输入动作,固定元素不能遮住滚动内容。
|
||||
|
||||
参考 ChatGPT 的双栏结构:
|
||||
MVP-0 导航只显示已实现的生成入口、必要账户操作和退出;点数/API Key 不显示空入口。图片区只显示上传顺序,第一张自动主体,其余参考,不展示不可用的角色/拖拽控件。
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ Logo 生成 记录 [点数] [API Key] [头像▾] │ ← 导航栏
|
||||
├──────────────┬───────────────────────────────────────┤
|
||||
│ 预览图列表 │ 结果展示区 │
|
||||
│ ┌──┐┌──┐ │ (大图 / 生成的文本,可下载、可复制) │
|
||||
│ └──┘└──┘ │ │
|
||||
│ ┌──┐┌──┐ ├───────────────────────────────────────┤
|
||||
│ └──┘└──┘ │ [图生图 | 文生文] 切换 │
|
||||
│ 无限滚动 │ [+ 上传原图,缩略图条,可排序/标角色] │
|
||||
│ │ ┌─────────────────────────┐ ┌──────┐ │
|
||||
│ │ │ 提示词输入框(多行) │ │ 生成 │ │
|
||||
│ │ └─────────────────────────┘ └──────┘ │
|
||||
└──────────────┴───────────────────────────────────────┘
|
||||
```
|
||||
| 状态 | 必须呈现 |
|
||||
|---|---|
|
||||
| 首次/空历史 | 可直接开始的输入区和简洁空状态,不伪造示例结果 |
|
||||
| 上传校验失败 | 对应文件、明确原因、修正方式;焦点到首个错误 |
|
||||
| pending | “排队中”,可区分尚未执行 |
|
||||
| running | “生成中”,轮询更新但布局不跳动 |
|
||||
| succeeded image | 缩略图、查看/下载;原图访问经鉴权 |
|
||||
| succeeded text | 可读文本和复制动作,复制结果有反馈 |
|
||||
| failed | 脱敏错误、是否可重试的明确操作 |
|
||||
| 轮询网络错误 | 保留当前内容,提示重试,不把任务误标 failed |
|
||||
| 认证过期 | 停止轮询,引导重新登录,登录后返回原上下文 |
|
||||
| 历史到底 | 明确结束,不持续显示 loading |
|
||||
|
||||
- 左侧是本人已生成结果的预览列表,点击查看大图;右侧是结果展示 + 类型切换 + 上传区 + 提示词 + 生成按钮。
|
||||
- 导航栏含用户信息、点数余额(只读)、API Key 管理入口。
|
||||
- 生成为异步:提交后左侧插入 loading 占位卡片,完成后原地替换为缩略图。
|
||||
- 每张上传图带角色下拉(主体图 / 参考图,默认第一张主体、其余参考),可拖拽排序,可选填一句备注。
|
||||
- 移动端左栏折叠为抽屉(MVP-2)。
|
||||
交互验收:
|
||||
|
||||
三个核心交互的实现方式:无限滚动用 `hx-trigger="revealed"`;任务轮询用 `hx-trigger="every 2s"`,完成后返回不带该属性的成品卡片即自动停止;上传区、角色排序和 tab 切换用 Alpine 的 `x-data` 管局部状态,拖拽可加 SortableJS。
|
||||
- 375、768、1024px 和桌面宽屏均无不合理横向滚动、遮挡和文字溢出;
|
||||
- 交互目标至少 44×44px,输入有持久 label,错误不只靠颜色;
|
||||
- 图标按钮使用既有 Lucide/Element Plus 图标并有可访问名称;
|
||||
- 所有功能可键盘操作;MVP-1 拖拽提供上移/下移等价操作;
|
||||
- 路由/片段替换后管理焦点,状态更新使用合适的 `aria-live`,toast 不抢焦点;
|
||||
- 动效尊重 `prefers-reduced-motion`,loading 不造成布局位移;
|
||||
- 页面实现前必须完成版本化 HTML 原型并由用户确认状态、响应式、权限和异常覆盖。
|
||||
|
||||
### 管理端页面
|
||||
|
||||
- **代码生成器直出**:`providers`、`provider_models`、`prompt_templates`、`point_accounts`、`users`、`api_keys`。
|
||||
- **需手写的三个定制页**:路由池编排(候选 model 穿梭框 + 权重滑块 + 拖拽排序)、Provider 健康看板(24h 成功率 / p95 延迟 / 熔断状态 / 最近错误)、生成记录详情(原图、结果图、`rendered_prompt`、`attempts` 故障转移链路)。
|
||||
- **Provider 连通性测试**按钮:保存后一键打真实请求验证,省掉大量配置调试时间。
|
||||
代码生成基于固定 go-admin-ui 的既有表导入:
|
||||
|
||||
定制页超过三个时必须重新评估是否放弃 go-admin-ui 改为自建服务端渲染后台。
|
||||
- CRUD:providers、provider_models、prompt_templates、users;point_accounts/api_keys 到对应 MVP 再生成。
|
||||
- 定制页:路由池编排、Provider 健康、生成记录详情。
|
||||
- 标准 CRUD 可用一个明确复用固定 go-admin 视觉/交互的代表性原型覆盖同类页面;定制页分别覆盖加载、空、错误、权限和边界状态。
|
||||
- 生成记录默认不展示 API Key、完整敏感错误或不必要的真实用户输入。
|
||||
- 定制页数量明显超过三个时重新评估维护成本,但不以先前错误的前端版本判断作为依据。
|
||||
|
||||
Provider 连通性测试是明确的运营动作:保存配置后由授权管理员点击,发出一次最小低成本请求,显示脱敏结果,记录审计并限制冷却;它不是保存时自动触发,也不用于 CI。
|
||||
|
||||
### 程序化调用
|
||||
|
||||
外部程序用 API Key(哈希比对,身份落在 `users`)调用 `portal` 的 openapi 端点提交生成与查询状态。接口形态在 MVP-2 的设计工单中确定,届时本节补链接。
|
||||
MVP-2 的 API Key 存哈希、身份仍属于 `users`。提交、查询、幂等、错误码、限流和跨用户授权在独立 API 设计工单确认;浏览器 Cookie/CSRF 与 API Key 认证链不得混用。
|
||||
|
||||
### 明确不做
|
||||
|
||||
@@ -137,12 +165,12 @@ API Key 管理与 openapi 端点、点数只读展示与管理端发放(含 `p
|
||||
|
||||
### 风险与待定
|
||||
|
||||
- 项目已定名 `chorus`(原方案代号 `cmgen` 作废)。
|
||||
- go-admin 社区活跃度与 Vue 2 EOL:定制页越多升级成本越高。
|
||||
- 选 MySQL 是为走 go-admin 主路径;代价是 `capabilities` 这类数组字段只能用 JSON 列或关联表表达,查询不如 Postgres `TEXT[]` 直接。
|
||||
- 角色规则默认模板的通用性:cmhub 那段是电商专用文案,是否改中性表述待定。
|
||||
- 点数来源:当前定为自有表 + 管理端发放;与 cmhub 账本打通需另设计。
|
||||
- 生成物存储增长:本地磁盘会涨得很快,保留期策略要在上线前定。
|
||||
- 默认中性 prompt template 的准确措辞和变量格式;
|
||||
- 上传数量/大小/MIME/像素、超时/lease/worker、限流的生产值;
|
||||
- 首个真实 Provider/模型和连通性测试最小请求;
|
||||
- 生成物保留期、备份、磁盘告警、RPO/RTO;
|
||||
- 未来公开注册及账号验证/找回/反滥用流程;
|
||||
- 固定 go-admin 版本后续升级策略;升级必须重新审查生成器权限和技术栈,不能静默漂移。
|
||||
|
||||
## 登记规则
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
<!-- gitea-wiki-mirror:start -->
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Deployment-and-Operations
|
||||
wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Deployment-and-Operations.-
|
||||
wiki_revision: 040f4c2c3e16def91fe612fa09cf0a6aa7288445
|
||||
synchronized_at: 2026-08-20T07:01:36Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
# 部署与运维
|
||||
|
||||
## 本页用途
|
||||
|
||||
本页定义 chorus 常驻服务的生产部署基线。当前尚无产品代码和真实生产环境,命令是后续实现必须满足的运维契约;域名、容量、保留期和阈值在首发工单确认后补充,不能由 Agent 猜测。
|
||||
|
||||
## 安全边界
|
||||
|
||||
- 生产只部署已审核制品和 `migrations/`;不从 `D:\github\goadmin` 构建,不运行 AutoMigrate。
|
||||
- dev-tools 生成路由、`/gen/todb` 和写源码能力不得注册或被 nginx 代理;仅隐藏菜单不合格。
|
||||
- DSN、会话密钥、Provider 主密钥/key ring、TLS 私钥只来自受控环境文件或密钥管理系统,权限最小化,不写入 Git/Wiki/日志。
|
||||
- 原图、结果和缩略图保存在受保护目录,通过 portal 鉴权读取,不由 nginx 直接暴露。
|
||||
- 数据迁移、回退、删除、清理和密钥轮换均是高风险操作,必须有工单、备份和人工确认。
|
||||
|
||||
## 服务概览
|
||||
|
||||
```text
|
||||
Internet
|
||||
→ nginx/TLS
|
||||
→ chorus-portal 127.0.0.1:8080(页面、HTMX、JSON、MVP 内嵌 worker)
|
||||
→ chorus-admin 127.0.0.1:8081(MVP-1)
|
||||
→ /admin-ui 静态构建产物(MVP-1)
|
||||
portal/admin → MySQL 8.0
|
||||
portal worker → Provider(安全 DialContext)
|
||||
portal → /var/lib/chorus/storage(或后续对象存储适配)
|
||||
```
|
||||
|
||||
| 项目 | 基线 |
|
||||
|---|---|
|
||||
| 运行账号 | 独立无登录 `chorus` 用户 |
|
||||
| 制品目录 | `/opt/chorus/releases/<commit>/`,`/opt/chorus/current` 指向当前版本 |
|
||||
| 配置 | `/etc/chorus/chorus.env`,仅运行账号可读 |
|
||||
| 持久数据 | `/var/lib/chorus/storage`,不随版本目录切换 |
|
||||
| 日志 | journald 或平台日志;结构化且脱敏 |
|
||||
| 进程托管 | systemd:`chorus-portal.service`、`chorus-admin.service` |
|
||||
| 数据库 | MySQL 8.0,独立最小权限账号 |
|
||||
| 构建 | CI 使用 Go 1.26.5;admin-ui 使用 Node >=22 + pnpm 9.15.1 |
|
||||
|
||||
## 配置与凭据
|
||||
|
||||
| 配置 | 用途 | 敏感 |
|
||||
|---|---|---|
|
||||
| `CHORUS_ENV=production` | 启用生产安全门禁 | 否 |
|
||||
| `CHORUS_DSN` | MySQL | 是 |
|
||||
| `CHORUS_MASTER_KEY` / key ring | Provider Key 加解密与轮换 | 是 |
|
||||
| `CHORUS_SESSION_KEY` | portal 会话 | 是 |
|
||||
| `CHORUS_STORAGE_ROOT` | 生成物目录 | 否 |
|
||||
| 上游/存储超时、lease、worker 数 | 执行与优雅退出 | 否 |
|
||||
| 上传和限流阈值 | 数量、大小、MIME、像素、请求频率 | 否 |
|
||||
| 保留期与磁盘告警 | 清理和容量保护 | 否 |
|
||||
|
||||
生产启动必须校验必需配置、密钥长度/key_id、目录权限、超时与 lease 关系;缺失或不安全时 fail closed。`CHORUS_ENV=production` 下若检测到 AutoMigrate 或 dev-tools 注册必须拒绝启动。
|
||||
|
||||
## 制品构建
|
||||
|
||||
在干净 CI 环境按固定提交来源导入后的 chorus 代码构建:
|
||||
|
||||
```bash
|
||||
go version
|
||||
go test ./...
|
||||
go vet ./...
|
||||
go build -trimpath -o dist/chorus-portal ./portal
|
||||
go build -trimpath -o dist/chorus-admin ./admin
|
||||
corepack pnpm --dir admin-ui install --frozen-lockfile
|
||||
corepack pnpm --dir admin-ui build:prod
|
||||
```
|
||||
|
||||
MVP-0 没有 admin/admin-ui 时跳过对应命令并在发布记录说明。制品记录 chorus commit、Go module 校验、admin-ui lockfile 和固定 go-admin 来源提交。
|
||||
|
||||
## 首次部署与升级顺序
|
||||
|
||||
1. 记录当前/目标 commit、迁移版本、制品校验值和回退条件。
|
||||
2. 备份 MySQL,并验证备份可读取;确认持久存储快照/备份策略。
|
||||
3. 把制品解压到新的 release 目录,不原位覆盖当前版本。
|
||||
4. 用相同 MySQL 8 版本的隔离库执行全部 migration up/down/up 验证。
|
||||
5. 停止取新任务并等待在途 worker 到优雅退出期限;旧 worker 未结束时不得启动会竞争同一租约的错误版本。
|
||||
6. 在生产执行 `migrate ... up`;检查版本,禁止任何 AutoMigrate。
|
||||
7. 原子切换 `current`,启动/重启 portal 和 admin。
|
||||
8. 执行健康、就绪、登录、提交 mock/受控测试和权限检查。
|
||||
9. 验证生产访问 dev-tools 路径为 404/未注册,并核对路由清单。
|
||||
10. 观察错误率、租约过期、磁盘和数据库连接;满足观察窗口后完成发布记录。
|
||||
|
||||
## 健康与就绪
|
||||
|
||||
应用必须提供:
|
||||
|
||||
- `/healthz`:进程存活,不依赖上游;
|
||||
- `/readyz`:必需配置有效、MySQL 可用、迁移版本匹配、存储可写、生产 dev-tools 未注册;
|
||||
- admin 健康路径不得泄露版本、DSN、路由或密钥。
|
||||
|
||||
```bash
|
||||
systemctl status chorus-portal
|
||||
curl --fail http://127.0.0.1:8080/healthz
|
||||
curl --fail http://127.0.0.1:8080/readyz
|
||||
journalctl -u chorus-portal -n 100 --no-pager
|
||||
```
|
||||
|
||||
MVP-1 同样检查 admin 8081。外部检查经 TLS/nginx 访问,不把内部端口暴露公网。
|
||||
|
||||
## worker 优雅退出
|
||||
|
||||
- 收到 TERM 后先停止认领新任务;
|
||||
- 在途任务只在 lease_token 仍有效时提交;
|
||||
- systemd 的停止等待时间大于应用优雅退出期限;
|
||||
- 到期未完成时退出,让租约自然过期,新 worker 原子重领;
|
||||
- 不把 running 改回 pending,不无条件标 failed,不删除其他 worker 文件。
|
||||
|
||||
## 存储、备份与监控
|
||||
|
||||
必须备份 MySQL 和仍在保留期内的生成物元数据/文件,并定期做恢复演练。至少监控:
|
||||
|
||||
- MySQL 连接、慢查询、迁移版本;
|
||||
- pending 数/最老等待时间、running 租约过期数、失败率;
|
||||
- Provider 429/5xx/超时和熔断状态;
|
||||
- 存储总量、可用空间、临时文件增长、缩略图失败;
|
||||
- 登录失败/节流、SSRF 拦截和越权拒绝;
|
||||
- portal/admin 健康与重启次数。
|
||||
|
||||
保留期、备份频率、RPO/RTO、磁盘阈值尚未确认,是生产上线门禁。清理任务不得在确认前启用。
|
||||
|
||||
## 回滚
|
||||
|
||||
- 优先使用向后兼容迁移并回滚二进制;先停止新任务,再切回上一 release,重启并执行健康检查。
|
||||
- 不能仅通过切回代码撤销数据库变更。需要 down 或恢复备份时先评估数据丢失,取得人工确认。
|
||||
- 若新版本已写入旧版本无法识别的数据,停止回滚并采用前滚修复或已演练的备份恢复方案。
|
||||
- 回滚后检查 migration version、陈旧 worker CAS、任务重复、文件一致性和 dev-tools 路由。
|
||||
- 每次真实发布前至少在隔离环境演练相同升级与回滚步骤。
|
||||
|
||||
## 已知限制
|
||||
|
||||
- 尚未完成真实 Linux/systemd/nginx 部署、备份恢复和回滚演练;
|
||||
- 高可用、多机 worker、对象存储和滚动升级尚未设计;
|
||||
- 生产域名、TLS、容量、保留期、告警、RPO/RTO 和具体超时/lease 阈值待首发部署工单确认;
|
||||
- 在这些门禁完成前,本页不能作为“已验证可上线”的证据。
|
||||
+40
-57
@@ -2,90 +2,74 @@
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Home
|
||||
wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Home
|
||||
wiki_revision: 202cba82085a11c32c439aa84d6b57e8e0347456
|
||||
synchronized_at: 2026-08-20T03:31:00Z
|
||||
wiki_revision: 3d6efad79056ff85f7532e28a90c80f2c710c332
|
||||
synchronized_at: 2026-08-20T07:00:57Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
# chorus 文档中心
|
||||
|
||||
chorus 是从 cmhub 抽出、用 Go 重写的独立生图生文服务:用户提交提示词(可带原图)得到新图或文本,运营在管理端配置上游模型并查看生成记录。开发过程遵循 DevHarness——Gitea 工单管理任务、Wiki 管理长期文档、Git 记录代码变更、人由人确认与验收。
|
||||
chorus 是从 cmhub 设计中抽取并用 Go 重写的独立生图生文服务:用户提交提示词(可带原图)得到图片或文本,运营在管理端配置上游并查看生成记录。Gitea 工单记录任务过程,Wiki 是长期文档事实来源,Git 记录代码与镜像。
|
||||
|
||||
当前处于 **MVP-0:跑通全流程的最小闭环**。目标不是把功能做全,而是先让一条数据从提交走到结果全程可运行、可验证。
|
||||
当前处于 **MVP-0:安全、可观测地跑通单上游闭环**。文档基线由工单 [#1](https://git.ilapage.cn/OPC/chorus/issues/1) 更新;这不表示产品功能已经开工。
|
||||
|
||||
## 第一次阅读
|
||||
|
||||
建议按以下顺序,用 10~20 分钟建立整体认识:
|
||||
|
||||
1. [项目档案](Project-Profile.-):项目目标、技术栈、环境、命令和目录边界。
|
||||
2. [产品需求总览](Product-Requirements-Overview.-):MVP 分期、已锁定决策、验收口径。
|
||||
3. [架构与代码地图](Architecture-and-Code-Map.-):两条执行路径、从哪个文件开始读。
|
||||
4. [业务规则与术语](Business-Rules-and-Glossary.-):Provider、路由池、生成状态机和不能破坏的规则。
|
||||
5. [本地开发与验证](Local-Development-and-Verification.-):怎样跑起来、怎样确认真的跑通了。
|
||||
6. [常见修改指南](Common-Changes.-):简单修改的步骤和停止条件。
|
||||
7. [故障排查](Troubleshooting):出错时按什么顺序查。
|
||||
8. [开发工作流](Development-Workflow.-):完整建单、实施、验收和归档流程。
|
||||
1. [项目档案](Project-Profile.-):固定 go-admin 来源、工具链、目录和安全边界。
|
||||
2. [产品需求总览](Product-Requirements-Overview.-):MVP 分期、UI 状态和验收。
|
||||
3. [架构与代码地图](Architecture-and-Code-Map.-):同步/worker 路径、租约 CAS、代码生成边界。
|
||||
4. [业务规则与术语](Business-Rules-and-Glossary.-):数据、状态、SSRF、身份和排除范围。
|
||||
5. [本地开发与验证](Local-Development-and-Verification.-):目标环境、隔离库、测试策略。
|
||||
6. [常见修改指南](Common-Changes.-):修改步骤和停止条件。
|
||||
7. [故障排查](Troubleshooting):按证据定位环境、队列、上游和生成器问题。
|
||||
8. [部署与运维](Deployment-and-Operations.-):生产迁移、服务、健康、备份和回退。
|
||||
9. [开发工作流](Development-Workflow.-):建单、确认、实施、验收与归档。
|
||||
|
||||
## 五分钟开始
|
||||
|
||||
在仓库根目录执行:
|
||||
|
||||
```powershell
|
||||
git status --short --branch
|
||||
python dev_scripts/harness.py check --strict
|
||||
python -m unittest discover -s tests -v
|
||||
$env:GITEA_URL = "https://git.ilapage.cn"
|
||||
python dev_scripts/harness.py sync --check
|
||||
```
|
||||
|
||||
预期结果:
|
||||
|
||||
- 工作区没有不属于当前任务的修改;
|
||||
- 输出“DevHarness 检查通过”;
|
||||
- 所有测试通过。
|
||||
|
||||
MVP-0 的实现工单落地后,再加上:
|
||||
|
||||
```powershell
|
||||
go build ./...
|
||||
go vet ./...
|
||||
go test ./...
|
||||
go run ./portal
|
||||
```
|
||||
|
||||
预期:编译通过、测试通过、服务监听 `:8080` 且日志显示 worker 已启动。完整的“走通一次生成”步骤见[本地开发与验证](Local-Development-and-Verification.-)。
|
||||
|
||||
如果失败,先看[故障排查](Troubleshooting),不要直接重置工作区或覆盖本地文档。
|
||||
MVP-0 产品代码落地且环境达到 Go 1.26.5/MySQL 8.0 后,再执行 `go build ./...`、`go vet ./...`、`go test ./...` 和 `go run ./portal`。当前本机版本差距见本地开发页,不能把无法执行的命令记为通过。
|
||||
|
||||
## 简单修改从哪里开始
|
||||
|
||||
| 想做什么 | 先读哪里 | 主要验证 |
|
||||
| 想做什么 | 先读 | 主要验证 |
|
||||
|---|---|---|
|
||||
| 改用户端页面文案 | Common-Changes、`portal/web/templates/` | 浏览器最小界面检查 |
|
||||
| 改角色规则默认模板 | Business-Rules、`prompt_templates` 表 | 一次真实生成,核对 `rendered_prompt` |
|
||||
| 加一个上游 Provider | Common-Changes | 连通性测试或一次真实生成 |
|
||||
| 看懂一次生成为什么失败 | Troubleshooting | `generations.attempts` 与 `error_message` |
|
||||
| 改文档 | 对应 Wiki 页面、Common-Changes | Wiki 同步检查 |
|
||||
| 查看或更新产品需求 | Product-Requirements-Overview | 状态、链接和事实来源核对 |
|
||||
| 增加 Harness 检查 | `dev_scripts/harness.py` | 成功与失败用例都要有 |
|
||||
| 改纯显示文案 | Common-Changes、模板 | 375/768/1024 最小界面检查 |
|
||||
| 改默认 prompt template | Business-Rules | mock 生成并核对 rendered_prompt |
|
||||
| 加 ProviderModel | Common-Changes | mock 协议 + SSRF;真实测试仅单次人工触发 |
|
||||
| 排查生成失败 | Troubleshooting | attempts/error/lease 时间线 |
|
||||
| 使用 go-admin 生成 CRUD | Architecture、Local-Development | migration → 隔离库 → 导表 → 生成差异审查 |
|
||||
| 改长期文档 | 对应 Wiki、Common-Changes | 回读 revision + sync --check |
|
||||
| 部署或回退 | Deployment-and-Operations | 迁移、健康、dev-tools、备份证据 |
|
||||
| 更新需求 | Product-Requirements | 状态、工单、原型和验收入口 |
|
||||
|
||||
选路与 `retryable` 判定、熔断、SSRF、密钥加解密、数据库迁移、队列租约、权限、点数写入、删除数据或不可逆操作**不属于简单修改**,必须停止并交给 Agent 分析、等待人工确认。
|
||||
retryable、SSRF、密钥、迁移、租约/CAS、身份权限、点数写入、AutoMigrate、代码生成副作用、删除/清理和不可逆操作都不是简单修改。
|
||||
|
||||
## 事实来源
|
||||
|
||||
| 信息 | 事实来源 |
|
||||
|---|---|
|
||||
| 任务状态、讨论、阻塞、验收过程 | Gitea 工单 |
|
||||
| 长期产品需求的统一导航和状态 | Product-Requirements-Overview |
|
||||
| 架构、业务规则、开发规范、操作手册、交付文档、任务归档 | Gitea Wiki |
|
||||
| 数据库表结构(跨交付单元的共享契约) | `migrations/` 中的 SQL |
|
||||
| 源码和与特定代码版本强绑定的文档 | Git 仓库 |
|
||||
| 已确认的界面与交互 | `prototypes/<工单号>/<版本>/index.html` |
|
||||
| 核心长期文档的离线浏览副本 | Git 仓库中的 `docs/` Wiki 镜像 |
|
||||
| 单元任务状态、讨论、阻塞和验收 | Gitea 工单 |
|
||||
| 长期需求导航 | Product-Requirements-Overview |
|
||||
| 架构、规则、开发、排错、部署、归档 | Gitea Wiki |
|
||||
| 生产数据库结构和配置种子 | `migrations/` 可逆 SQL |
|
||||
| 源码和固定版本资料 | chorus Git;外部来源提交记录在 Project-Profile |
|
||||
| 已确认界面与交互 | `prototypes/<工单号>/<版本>/index.html` |
|
||||
| 离线核心文档 | Git 中的 `docs/` Wiki 只读镜像 |
|
||||
|
||||
线上 Wiki 已于 2026-08-20 初始化完成,`docs/` 已转为只读镜像。长期文档必须先改 Wiki、读取确认,再导出镜像。
|
||||
`D:\github\goadmin` 是已审查来源,不是运行时事实来源。线上 Wiki 已初始化;长期文档必须先改 Wiki、回读 revision,再同步镜像。
|
||||
|
||||
## 项目入口
|
||||
|
||||
- [Gitea 工单](https://git.ilapage.cn/OPC/chorus/issues)
|
||||
- [产品需求总览](Product-Requirements-Overview.-)
|
||||
- [部署与运维](Deployment-and-Operations.-)
|
||||
- [代码仓库](https://git.ilapage.cn/OPC/chorus)
|
||||
- [新项目文档初始化](New-Project-Documentation-Setup.-)
|
||||
- [交付文档指南](Delivery-Documentation-Guide.-)
|
||||
@@ -94,11 +78,10 @@ go run ./portal
|
||||
## 同步原则
|
||||
|
||||
```text
|
||||
修改 Wiki → 读取确认 → 导出 docs → 校验差异 → 提交镜像
|
||||
修改 Wiki → 在线回读 revision → 导出 docs → 校验差异 → 提交镜像
|
||||
```
|
||||
|
||||
- 核心页面与本地路径通过 `wiki-docs.json` 显式映射;普通同步不处理任务归档。
|
||||
- 镜像头记录来源页面、Wiki revision 和同步时间;带 `generated: true` 的文件不得手工编辑。
|
||||
- 已映射镜像存在未提交修改时同步必须停止。
|
||||
- 页面删除、重命名和映射变更必须人工确认。
|
||||
- 凭据、个人数据和生产数据不得进入 Wiki、镜像、原型或工单。
|
||||
- `wiki-docs.json` 显式映射核心和项目专用页面,普通同步不处理任务归档。
|
||||
- 带 `generated: true` 的文件不得手工编辑;有未提交镜像修改时同步停止。
|
||||
- 页面删除、重命名和事实源变化必须先更新工单并确认。
|
||||
- 凭据、个人数据、真实 prompt、生成文件和生产数据不得进入文档、工单或原型。
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
{ "page": "New-Project-Documentation-Setup", "path": "docs/07-new-project-documentation-setup.md" },
|
||||
{ "page": "Existing-Project-Adoption-Guide", "path": "docs/08-existing-project-adoption.md" },
|
||||
{ "page": "Product-Requirements-Overview", "path": "docs/09-product-requirements-overview.md" },
|
||||
{ "page": "Deployment-and-Operations", "path": "docs/10-deployment-and-operations.md" },
|
||||
{ "page": "Delivery-Documentation-Guide", "path": "docs/delivery/README.md" },
|
||||
{ "page": "Audience-Document-Template", "path": "docs/delivery/audience-document-template.md" },
|
||||
{ "page": "Deployment-Template", "path": "docs/templates/deployment.md" },
|
||||
|
||||
Reference in New Issue
Block a user