refactor(*): 平台架构 - 拆分后端领域模块并同步插件协议与模型能力

This commit is contained in:
ddCat
2026-09-11 14:02:49 +08:00
parent 0945115e53
commit 7ce967df0f
644 changed files with 62731 additions and 2320 deletions

View File

@@ -13,8 +13,8 @@ description: 从产品功能快速定位主要代码入口
| --- | --- | --- |
| 首页与导航 | `web/src/pages/home/`、`web/src/layouts/user-layout.tsx` | `components/layout/`、`router.tsx` |
| 创作工作台 | `web/src/pages/create/index.tsx` | `creation-workspace.tsx`、`creation-conversations.ts`、`creation-types.ts`、`creation-assets.ts`、`creation-references.ts` |
| 登录注册 | `web/src/pages/auth/` | `stores/use-user-store.ts`、`services/api/auth.ts`、后端 `service/linuxdo.go` |
| 积分与计费 | `web/src/pages/wallet/`、`pages/admin/components/platform-finance-panel.tsx` | `services/api/wallet.ts`、后端 `service/finance.go`、`repository/finance.go` |
| 登录注册 | `web/src/pages/auth/` | `stores/use-user-store.ts`、`services/api/auth.ts`、后端 `internal/auth/` |
| 积分与计费 | `web/src/pages/wallet/`、`pages/admin/components/platform-finance-panel.tsx` | `services/api/wallet.ts`、后端 `internal/app/finance.go`、`repository/finance.go` |
| 任务中心 | `web/src/pages/tasks/` | `services/api/task-center.ts`、`services/api/task-text-stream.ts` |
| 我的素材 | `web/src/pages/assets/` | `stores/use-asset-store.ts`、`services/image-storage.ts` |
| 技能库 | `web/src/pages/skills/` | `services/api/skills.ts`、`services/skill-runtime.ts`、`components/skills/skill-runtime-picker.tsx` |
@@ -51,7 +51,7 @@ description: 从产品功能快速定位主要代码入口
| 任务结果回填 | `web/src/lib/canvas/canvas-generation-task-sync.ts` |
| 本地持久化状态 | `web/src/stores/canvas/use-canvas-store.ts` |
| Agent 会话状态 | `web/src/stores/canvas/use-canvas-agent-store.ts` |
| 官方提示词目录与客户端起始稿 | `backend/internal/service/prompt_template.go`、`web/src/lib/prompts/`、`web/src/lib/canvas/canvas-agent-protocol.ts` |
| 官方提示词目录与客户端起始稿 | `backend/internal/prompts/`、`web/src/lib/prompts/`、`web/src/lib/canvas/canvas-agent-protocol.ts` |
## 前端 HTTP 与产品壳
@@ -64,29 +64,49 @@ description: 从产品功能快速定位主要代码入口
## 后端调用链
```text
HTTP 请求 -> handler -> service -> repository/model -> SQLite/PostgreSQL 或资源存储
|
-> provider/outbound -> 模型上游
HTTP 请求 -> handler -> service(稳定导入面 / 别名)-> app(组合根)-> 业务域包 -> repository/model
|
-> protocol / payment / generation / outbound
```
| 功能 | Handler | Service / Repository |
`internal/service` 只再导出 `internal/app` 的类型、常量和包级函数,handler/cmd 仍 import `internal/service`。组合根和尚未拆出的业务实现在 `internal/app`。已抽出的域包不得 import `service` 或 `app`(防回环)。共享错误码与 ID/字符串工具在 `internal/kernel`。
### 目标业务域
| 域包 | 职责 | 现状 |
| --- | --- | --- |
| `service` | HTTP 稳定导入面 | **已变薄**:仅 `aliases_*.go` / `doc.go`(4 个文件) |
| `app` | 组合根、跨域编排、尚未拆出的业务实现 | **承接原 service 实现**:`provider_*`、`task_*`、`project_*`、OSS/配额、渠道、管理后台等 |
| `platform` | 运行时策略、功能开关、限流/并发协调、worker 与读缓存 | **已抽出**:`internal/platform`;`app/platform_bridge.go` 委托 |
| `kernel` | 结构化错误、错误码、ID 与通用字符串/配额换算 | **已抽出**:`internal/kernel` |
| `generation` | 协议注册表和生成域边界 | **部分抽出**:`internal/generation` 提供协议注册入口;`provider_*` 执行簇仍在 `app` |
| `outbound` | 出站 URL/Header 校验、SSRF 防护、HTTP 客户端 | **已抽出**:`internal/outbound` |
| `skills` | 技能库、GitHub/ZIP 包、内置技能种子、后台同步 | **已抽出**:`internal/skills` |
| `prompts` | 提示词模板、用户定制、风格档案、JSON 抽取 | **已抽出**:`internal/prompts` |
| `auth` | 登录注册、OAuth、邮件验证码、LibTV/TapNow 导入 | **已抽出**:`internal/auth` |
| `canvas` | 画布公开分享、媒体引用守卫、用户画布/素材库同步 | **已抽出**:`internal/canvas` |
| `assets` | 资源 URL/引用解析、流式交付类型 | **部分抽出**:`internal/assets`;OSS/配额/转码仍在 `app` |
组合根原则:`service.New`(委托 `app.New`)组装各域服务;coordinator 不得再持有整个 `*Service` 回灌域包。
| 功能 | Handler | Service / 域包 / Repository |
| --- | --- | --- |
| 路由与中间件 | `handler/api.go`、`handler/routes.go` | `RegisterCanvasAPI` 挂到 `/api`。合同见 `docs/content/docs/backend/http-api.mdx`、`handler/openapi.yaml`。 |
| 用户和管理员 | `handler/auth.go` | `service/auth.go`、`service/admin.go` |
| 管理后台批量操作 | `handler/auth.go` | `service/admin.go`、`repository/admin_audit.go`、`repository/analytics.go` |
| 管理后台存储资源 | `handler/admin_storage.go` | `service/admin_storage.go`、`service/admin_storage_delete.go`、`repository/admin_storage.go`、`repository/admin_storage_delete.go` |
| 资源与请求策略 | `handler/auth.go`、`handler/security.go` | `service/runtime_policy.go`、`service/upload_quota.go`、`service/storage_quota.go`、`service/coordination.go` |
| 模型响应拦截 | `handler/response_interception.go`、`handler/custom_proxy.go` | `service/response_interception.go`、`repository/repository.go` |
| 异步任务、文本 SSE 与 Agent | `handler/routes.go` | `service/service.go`、`service/provider.go`(调度)及 `provider_text.go` / `provider_image.go` / `provider_video.go` / `provider_audio.go` / `provider_http_client.go` / `provider_protocol.go`、`service/task_text_stream.go`、`service/text_replay.go` |
| 用户画布与素材 | `handler/user_data.go` | `service/user_data.go`、`service/user_asset_document.go` |
| 文件和 OSS | `handler/routes.go` | `service/resource.go`、`service/settings.go` |
| 画布分享 | `handler/canvas_share.go` | `service/canvas_share.go` |
| 技能库 | `handler/skills.go` | `service/skills.go`、`repository/skills.go` |
| 提示词模板 | `handler/auth.go`、`handler/user_data.go` | `service/prompt_template.go`、`service/prompt_template_defaults.go` |
| 用户风格库 | `handler/style_profile.go` | `service/style_profile.go`、`repository/style_profile.go` |
| 用量分析 | `handler/admin_analytics.go` | `service/analytics.go`、`repository/analytics.go` |
| 用户和管理员 | `handler/auth.go` | `internal/auth`、`internal/app/admin.go`(经 `service` 别名) |
| 管理后台批量操作 | `handler/auth.go` | `internal/app/admin.go`、`repository/admin_audit.go`、`repository/analytics.go` |
| 管理后台存储资源 | `handler/admin_storage.go` | `internal/app/admin_storage.go`、`repository/admin_storage.go` |
| 资源与请求策略 | `handler/auth.go`、`handler/security.go` | `internal/platform`、`internal/app/upload_quota.go`、`storage_quota.go`、`coordination.go` |
| 模型响应拦截 | `handler/response_interception.go`、`handler/custom_proxy.go` | `internal/app/response_interception.go`、`repository/repository.go` |
| 异步任务、文本 SSE 与 Agent | `handler/routes.go` | `internal/app` 组合根(`provider_*` 仍在此)+ `internal/generation` + `internal/outbound` + `task_text_stream.go`、`text_replay.go` |
| 用户画布与素材 | `handler/user_data.go` | `internal/canvas` |
| 文件和 OSS | `handler/routes.go` | `internal/app/resource.go`、`settings.go` |
| 画布分享 | `handler/canvas_share.go` | `internal/canvas` |
| 技能库 | `handler/skills.go` | `internal/skills`、`repository/skills.go` |
| 提示词模板 | `handler/auth.go`、`handler/user_data.go` | `internal/prompts` |
| 用户风格库 | `handler/style_profile.go` | `internal/prompts`、`repository/style_profile.go` |
| 用量分析 | `handler/admin_analytics.go` | `internal/app/analytics.go`、`repository/analytics.go` |
登录恢复仅初始化当前账号缓存与增量同步会话,不再自动读取 `GET /api/user-data/snapshot`。画布库通过分页摘要加载,打开、导出、重命名时按 ID 读取完整画布,引用素材通过批量 ID 接口读取;旧快照接口仍保留供显式调用。实现见 `service/user_data_page.go` 和前端 `services/user-data-sync.ts`。用户库素材写入(`UpsertUserAsset` / `ReplaceUserAssets`)在 `user_asset_document.go` 校验 SPA 合同:`kind`、`coverUrl`、`tags` 字符串数组,以及按类型完整的 `data`。不完整 payload 在入库时拒绝;GET 仍返回已保存的原始 JSON。上游异构 JSON 的可选字符串仍走 `stringField`;任务 ID 等关键字段走 `firstJSONString`,数字 ID 不再被当成“缺字段”。
登录恢复仅初始化当前账号缓存与增量同步会话,不再自动读取 `GET /api/user-data/snapshot`。画布库通过分页摘要加载,打开、导出、重命名时按 ID 读取完整画布,引用素材通过批量 ID 接口读取;旧快照接口仍保留供显式调用。实现见 `internal/canvas/user_data_page.go` 和前端 `services/user-data-sync.ts`。用户库素材写入(`UpsertUserAsset` / `ReplaceUserAssets`)在 `internal/canvas/user_asset_document.go` 校验 SPA 合同:`kind`、`coverUrl`、`tags` 字符串数组,以及按类型完整的 `data`。不完整 payload 在入库时拒绝;GET 仍返回已保存的原始 JSON。上游异构 JSON 的可选字符串仍走 `stringField`;任务 ID 等关键字段走 `firstJSONString`,数字 ID 不再被当成“缺字段”。
后端托管的文本生成通过 `GET /api/tasks/:id/text-events` 返回 `progress`、`delta` 和 `terminal` 事件。`delta` 的递增事件 ID 是断线续传游标,前端使用 `?after=<id>` 恢复;分镜任务复用该通道实时接收阶段和进度,但分镜结构和 Agent 工具参数仍在完整响应结束并校验通过后原子应用,不执行半截 JSON。

View File

@@ -5,6 +5,28 @@ description: 当前项目已实现功能的手工验证清单与未确认边界
# 待测试
## 后端业务域拆包
- `internal/service` 只保留别名(4 个文件)。组合根和未拆出实现在 `internal/app`。已抽出域:`kernel`、`platform`、`skills`、`prompts`、`auth`、`canvas`、`assets`、`outbound`、`generation`。handler 仍只打 `*service.Service`。
- [x] `cd backend && go test ./internal/kernel/ ./internal/platform/ ./internal/skills/ ./internal/prompts/ ./internal/auth/ ./internal/canvas/ ./internal/assets/ ./internal/handler/ -count=1`
- [x] `cd backend && go build ./internal/service/ ./internal/handler/ ./cmd/server/`
- [ ] 登录后打开技能库:列表、加入、GitHub/ZIP 安装、内置技能正文仍可用。
- [ ] 管理后台提示词模板、用户风格库、画布按项目画风生成仍走原接口。
- [ ] 注册、登录、邮件验证码、密码重置、Linux.do 登录仍走原 Cookie 接口。
- [ ] 公开画布分享链接仍能打开,分享资源 URL 不泄露密钥和任务字段。
- [ ] 登录后画布库分页、按 ID 读素材、上传/替换用户素材仍走原接口。
## 固定分辨率图片模型
- 比例协议未配置质量参数时,图像设置按管理员启用的尺寸档位显示;未启用的档位仍隐藏。
- 选中档位会写入 `1k` / `2k` / `4k`;Gemini 图片由官方插件 `google-gemini-image` 映射为上游 `1K` / `2K` / `4K`,业务层不再手写组包。插件优先读 `quality`,画布残留的视频 `vquality`(如 `720`)不能盖掉 4K。
- [ ] 选 4K 后抓上游请求,确认 `generationConfig.imageConfig.imageSize` 为 `"4K"`,且没有 `candidateCount` 被误写成视频清晰度 `720`。
- 前台模型从系统模型同步 `imageSize` 预设和 `aspect_ratio` 参数;旧前台快照会从供应线路补回档位,不再把 1K/4K 压成只有 1K。未启用的 1K 不能因为协议默认 `low/medium/high` 出现。未声明 `quality` 的前台模型不能因选 4K 档位报“不支持参数 生成质量”。
- [ ] 系统模型启用 1K+4K 全尺寸后,打开对应前台模型:画布图像设置应同时出现 1K 和 4K。
- [ ] 保存同时启用 1K 和 4K 全尺寸、且未配置质量参数的模型后,画布图像设置应同时出现 1K 和 4K,切换档位后参考像素跟着变。
- [ ] 保存仅启用 4K 的模型后,在画布重新选择该模型,确认只显示 4K,切换比例和重新打开面板仍保持 4K。
- [ ] 实际生成后确认上游模型输出分辨率;面板参考尺寸不保证上游实际像素。
## HTTP API 错误码与 OpenAPI
- 路径仍是 `/api`。不做 `/api/v1` 双挂;前后端一起发布。
@@ -86,11 +108,33 @@ description: 当前项目已实现功能的手工验证清单与未确认边界
- [ ] 任务中心删除本机记录:确认框主题正确,只删本机记录不删素材。
- [ ] 管理后台渠道「设置排序」在无条目时显示 EmptyState;插件中心清空筛选后的空态可点「清除筛选」。
## 后端 Service 域边界试点(generation / outbound)
- 目标:横向按业务域拆包;handler 仍依赖 `*service.Service` 组合根,域包禁止 import `service`。
- 本轮已落地:`internal/outbound`(SSRF/出站 HTTP)、`internal/generation`(Engine + Deps 端口 + 官方协议注册表运行时)。`service` 通过薄包装再导出。
- `provider_*` 执行簇仍在 `service`(与编排水合/计费耦合深),后续按 Engine 端口继续迁入 generation。
- [x] `go build ./internal/generation/ ./internal/outbound/ ./internal/service/`
- [x] `go test ./internal/generation/ ./internal/outbound/ ./internal/protocol/ -count=1`
- [x] `go test ./internal/service/ -count=1`:完整 service 回归。
- [ ] 任选一条图片或视频生成,确认官方插件路径与结果落库仍正常。
- [x] `go list` 确认 `internal/generation` 的 import 不含 `internal/service`。
## 官方图片 / 音频 InterfaceType 只走声明式插件
- `runImageTask` / `runAudioTask` 对白名单官方接口只走声明式插件:`ensureOfficialProtocolAdapter` → declarative;未安装时报「…插件未安装」,不再回退手写组包。
- 图片白名单:`openai-image`、`grok-image`、`volcengine-ark-image`、`volcengine-jimeng-image`、`gemini-image`。音频白名单:`openai-audio`、`async-audio`。
- 已从 Builtins 移除对应 host adapter;真相源为 `plugin-packages/*.yingce-plugin`(含新建 `openai-audio` / `async-audio`,以及重写的 openai-images / grok / ark / jimeng)。
- 表达式引擎补 `$divide`(配合 `$split` / `$toFloat`);同步音频支持 `BinaryPayload`。
- [x] `go test ./internal/protocol/ ./internal/service/ -count=1`:协议与 provider 回归。
- [ ] 用 OpenAI Images(generations + edits)、Grok Images、方舟 Seedream、即梦图片、Gemini Image 各跑一条,确认创建与结果落库走对应官方包。
- [ ] 用 `openai-audio` 同步 speech 与 `async-audio` 异步任务各跑一条,确认二进制 / 轮询结果可播放。
- [ ] 卸掉对应官方图片或音频插件后再提交同类型任务,应看到「…插件未安装」。
## 官方视频 InterfaceType 只走声明式插件
- `runVideoTask` 对已有官方插件的接口类型不再回退到手写协议。生产注入插件注册表;单测未注入时会补官方包。显式放入空注册表则报「…视频插件未安装」。
- 覆盖的接口:`minimax-video`、`gemini-veo`、`novita-video`、`newapi-channel-1`、`newapi-channel-2`、`xai-video`、`volcengine-ark-video`、`volcengine-jimeng-video`、`newapi`、`agnes-video`。任务恢复也只查询声明式 adapter,不再回退 `queryNewAPIChannel2VideoTask`。
- 仍保留的手写路径只有:空 InterfaceType 的 OpenAI 风格 multipart `/videos`、模型名含 `grok` 的 JSON `/videos`、BaseURL 含 `/api/plan/v3` 的 Ark Plan、模型名含 seedance 的 Seedance `/videos`。
- 仍保留的手写路径只有:BaseURL 含 `/api/plan/v3` 的 Ark Plan Seedance,以及模型名含 seedance 的 Seedance `/videos` 遗留;OpenAI multipart / Grok JSON 视频死代码已删。
- 插件契约与旧手写实现不一致处按插件为准:MiniMax 时长必须 4–15 秒且不再把 `720`/`1440p` 映射成 `768P`/`2K`;Channel 2 不再做 grok 单图/音频校验;xAI 结果 URL 不可达时不再回退 `/videos/{id}/content`;Channel 1 音频 `type` 跟随宿主 role(当前为 `reference_audio`)。
- [x] `go test ./internal/service/ ./internal/protocol/ -count=1`:通过。
- [ ] 用 MiniMax / Veo / Novita / NewAPI Channel 1+2 / xAI / 方舟 / 即梦视频各跑一条生成,确认创建、轮询、下载仍走对应官方插件路径。
@@ -1573,4 +1617,3 @@ v2(用户反馈首版难看后重做):菜单改用 antd Dropdown(placeme
- [ ] 多个绘图节点连接到生成节点后,参考栏显示各自最新缩略图;编辑并保存绘图后重新打开面板,缩略图随 revision 更新。
- [ ] 用拖动手柄或聚焦后按左右方向键调整参考顺序,标签编号和提示词中的 `@图片N` 同步更新;刷新画布后顺序保持,配置节点承接引用时同样有效。
- [ ] 明暗主题下拖动手柄、缩略图预览、删除按钮和横向滚动无错位;拖动引用不触发画布节点拖拽。