feat: 完善创作工作台与用户诊断支持 (#295)

* feat(canvas): 参考素材 - 增加移除按钮并删除对应连线

* feat(canvas): 完善节点面板交互与拖拽反馈

* fix(canvas): 画布交互与小地图 - 统一浮层层级并完善节点识别

* feat(prompt-optimizer): 增加 AI 提示词优化器及宿主接入

* feat(canvas): 画布 - 增加自动整理节点按钮并补充文档

* chore(web): 前端 - 修正提示词优化器格式

* chore(backend): 后端 - 修正 Go 格式检查

* feat(workspace): 完善诊断导出与本地排障支持

* chore(web): 格式化诊断导出文件

* fix(web-test): 兼容 Bun 测试运行时

* fix(web-test): 对齐模型目录协议断言
This commit is contained in:
QAyong authored and GitHub committed 2026-08-25 13:49:15 +08:00
1 parent 625a97560b
commit cf6c78d540
70 files changed
+6863 -1708

No files matched your search

@@ -0,0 +1,68 @@
---
title: 本地开发
description: 在宿主机启动影策前后端开发服务
---
# 本地开发
## 一键启动(Windows PowerShell)
在仓库根目录执行:
```powershell
.\scripts\start-local.ps1
```
脚本会:
- 使用 `.local/project-workbench-debug` 作为后端 SQLite 开发数据目录;
- 创建 `.local/cache/go-build` 和 `.local/cache/go-mod` 缓存目录;
- 首次启动时,如果 `web/node_modules` 不存在,执行 `bun install --frozen-lockfile`;
- 打开两个 PowerShell 窗口,分别运行后端和前端;
- 将前端 `/api` 请求代理到 `http://127.0.0.1:8080`。
启动后访问 <http://localhost:3000>。后端健康检查地址是 <http://127.0.0.1:8080/api/health>。
脚本使用固定的本地开发端口:前端 `3000`,后端 `8080`。如果端口被占用,脚本会先退出,不会关闭已有进程。请分别在两个开发窗口中按 `Ctrl+C` 停止服务。
如果 PowerShell 的执行策略阻止脚本运行,可以只对当前进程临时放行:
```powershell
Set-ExecutionPolicy -Scope Process Bypass
.\scripts\start-local.ps1
```
## 手工启动
后端:
```bash
mkdir -p .local/project-workbench-debug .local/cache/go-build .local/cache/go-mod
cd backend
CANVAS_BACKEND_ADDR=127.0.0.1:8080 \
CANVAS_BACKEND_DATA_DIR=../.local/project-workbench-debug \
GOCACHE=../.local/cache/go-build \
GOMODCACHE=../.local/cache/go-mod \
go run ./cmd/server
```
前端另开终端:
```bash
cd web
bun install
bun run dev
```
前端默认监听 `0.0.0.0:3000`,Vite 将 `/api` 和 OAuth 回调代理到 `127.0.0.1:8080`。如需修改代理目标,可设置 `VITE_API_PROXY_TARGET`。
## Docker 开发
需要容器化热更新时,在仓库根目录执行:
```bash
LOCAL_UID=$(id -u) LOCAL_GID=$(id -g) \
docker compose -f docker-compose.dev.yml up --build
```
Docker 开发编排也使用 `.local/project-workbench-debug`,并默认暴露前端 `3000` 和后端 `8080`。端口冲突时,可通过 `CANVAS_WEB_HOST_PORT` 和 `CANVAS_BACKEND_HOST_PORT` 调整宿主机端口。
+93
View File
@@ -0,0 +1,93 @@
---
title: 功能清单
description: 影策当前已实现的核心工作台能力与使用入口
---
# 功能清单
## AI 提示词优化器
提示词优化器是一个内置 AI 能力插件,用于把模糊的生图或生视频想法整理成可以直接使用的提示词。它复用宿主已经配置的文本模型和渠道,不在浏览器中保存或拼接第三方密钥。
### 使用入口
- 创作页 `/create` 的提示词输入框中,点击“优化”。
- 画布中的图片或视频节点提示词编辑器中,点击“优化”。
面板使用紧凑的聊天式交互,跟随当前输入框定位,支持拖动、边缘调整尺寸和窗口尺寸变化。打开与关闭不使用延迟动效,避免遮挡画布或让用户误以为请求卡住。
### 优化方式
| 方式 | 用途 |
| --- | --- |
| 扩展想法 | 从模糊想法补齐主体、场景、构图、光线、镜头等可执行细节。 |
| 精修已有提示词 | 尽量保留原意,只修正表达并补充必要细节。 |
| 强化风格 | 保留主体和动作,强化色彩、材质、光影与指定风格。 |
| 适配模型 | 按当前生成模型和协议调整提示词结构,避免使用未确认的参数语法。 |
| 结合参考 | 使用已连接的图片或文本补充外观、一致性、构图和关联关系。 |
面板底部可以单独选择优化所使用的文本模型;目标生成模型仍取当前图片或视频生成配置。两者职责不同:前者负责整理提示词,后者负责最终生成内容。
### 结果与安全边界
- 优化结果以结构化内容返回,包含正向提示词、规避项、修改说明、假设和可选备选版本。
- 结果可以继续编辑;只有点击“采用”才会回填当前输入框,并自动关闭面板。
- 请求支持流式反馈、取消和错误提示,不会在优化失败时覆盖原提示词。
- 参考素材只提交当前已连接且符合类型的内容;插件不自行访问素材库或后端密钥。
### 相关实现
- `web/src/lib/plugins/builtin/prompt-optimizer.ts`:插件清单、优化方式和模型适配策略。
- `web/src/components/canvas/canvas-prompt-optimizer-drawer.tsx`:聊天面板、拖动缩放和结果采用交互。
- `web/src/services/plugin-host.ts`:将插件文本请求接入宿主现有模型渠道。
- `web/src/pages/create/index.tsx`、`web/src/components/canvas/canvas-node-prompt-panel.tsx`:两个入口的接入。
## 用户诊断包导出
设置页的“问题诊断”入口和任务失败快捷入口支持导出脱敏诊断包,帮助普通用户提交问题时保留足够的排障上下文。
### 当前能力
- 浏览器内存中保留最近的全局错误、未处理 Promise、API 请求和任务创建事件。
- 后端按当前登录用户、时间范围、任务或项目聚合 `Task`、`TaskLog` 和 `ApiCallLog`。
- 下载前显示数据量预览,并生成包含清单、运行时信息和 JSONL 日志的 ZIP。
- 默认排除提示词、任务输入、API Key、Cookie、上游请求/响应体和 URL 查询参数。
### 使用入口
- 设置页:`/settings?section=diagnostics`。
- 失败任务详情中的“导出诊断包”按钮会自动带入任务和项目范围。
相关设计和接口边界见[用户诊断包设计](../../../design/user-diagnostic-bundle.mdx)。
## Eagle 外部素材库
内置 Eagle 插件可以从用户配置的 Eagle 服务读取素材、文件夹和标签,并将素材映射为工作台可引用的外部资源。
### 当前能力
- 支持按文件夹、关键词和素材类型查询 Eagle 素材。
- 素材没有文件夹或标签时,仍返回稳定的空集合,工作台可以继续展示和引用素材。
- 保留素材的文件夹路径、标签、扩展名、尺寸、大小和修改时间等排障与展示信息。
### 相关实现
- `backend/internal/service/eagle.go`:Eagle 响应解析和集合字段归一化。
- `web/src/lib/plugins/builtin/eagle.ts`:外部素材映射、预览和引用处理。
## 画布节点自动整理
画布底部的“自动整理节点”按钮用于快速改善节点排布,适合处理节点散落、间距不一致或流程方向不清晰的画布。
### 使用规则
- 选中了两个或以上节点时,只整理选中的可移动节点;没有选中节点时,整理所有顶层可移动节点。
- 候选节点存在连线时,按连线依赖关系进行流程布局;没有连线时,自动使用网格布局。
- 锁定节点、容器节点和折叠批次中的隐藏子节点不会被移动。
- 节点位置变化会进入现有画布历史记录,可使用“撤销”恢复整理前的布局,并继续由画布生命周期自动保存。
### 相关实现
- `web/src/components/canvas/canvas-zoom-controls.tsx`:底部视图控制 Dock 与“自动整理节点”入口。
- `web/src/pages/canvas/use-canvas-node-operations.ts`:候选节点筛选、流程/网格布局选择和位置提交。
- `web/src/lib/canvas/canvas-layout.ts`:流程、网格、行列排列和对齐算法。
+10 -2
View File
@@ -5,9 +5,9 @@ description: 影策网页插件系统的目标、扩展边界、插件合同与
# 插件系统设计
> 状态:已实现首版(Eagle 外部素材来源)
> 状态:已实现首版(Eagle 外部素材来源 + AI 提示词优化器)
>
> 当前版本已经包含 Eagle 专属素材管理页面、后端本地桥接、原始文件树浏览、文件读取和写入;第三方远程插件安装与完整 SDK 仍按后续阶段建设。
> 当前版本已经包含 Eagle 专属素材管理页面、后端本地桥接、原始文件树浏览、文件读取和写入,以及内置 `prompt-optimizer` AI 能力插件;第三方远程插件安装与完整 SDK 仍按后续阶段建设。
## 1. 背景与范围
@@ -62,6 +62,8 @@ description: 影策网页插件系统的目标、扩展边界、插件合同与
提供提示词处理、图片分析、批量生成、结果评分或模型能力适配。涉及真实 API Key 的请求必须经过宿主现有渠道中转合同或受控后端服务。
当前内置的 `prompt-optimizer` 插件用于在创作页和图片、视频节点的提示词编辑器中优化用户输入。它通过宿主的 `ai.text` 服务调用已配置的文本模型,不直接读取 API Key;结果先以可编辑内容展示,用户点击“采用”后才回填当前输入框。
### 4.4 素材与数据连接器
连接外部素材库、提示词库、知识库、云盘或项目管理工具,提供搜索、导入、上传、同步和来源追踪。
@@ -152,6 +154,12 @@ export interface PluginSetupApi {
写操作必须通过结构化指令进入宿主,不能直接修改 React 状态。所有写操作都要保留失败原因和用户可见反馈。
### 6.1 内置 AI 文本服务
声明 `ai.text` 权限的插件可以使用宿主提供的文本模型服务。请求包含消息、可选的工具定义、工具选择、取消信号和流式增量回调;宿主负责选择当前配置的模型、经过现有渠道中转并返回结构化工具调用结果。插件不得自行创建平行的 HTTP 客户端或把密钥写入请求正文、日志和 URL。
提示词优化器使用 `optimize_prompt` 工具返回以下结构:优化后的提示词、规避项、变化说明、假设和最多两个备选版本。`model-adapt` 模式会根据目标生成模型和协议选择适配策略,但不会臆造未确认的模型参数。
## 7. 生命周期
```text
+57 -1
View File
@@ -1,4 +1,38 @@
# 待测试清单
---
title: 待测试
description: 影策已实现功能的手工验证清单与未确认边界
---
# 待测试
## AI 提示词优化器
当前代码和生产构建已完成,但以下场景仍需要在真实配置和不同窗口尺寸下由用户确认:
- [x] `web` 前端 TypeScript 检查与生产构建通过。
- [x] 优化面板打开、关闭不再等待 Popover 入场或退出动效。
- [ ] 在 `/create` 中分别测试图片和视频模式的打开、发送、取消、失败重试和关闭。
- [ ] 在图片、视频画布节点中测试优化入口、结果采用和原提示词回填。
- [ ] 逐项测试五种优化方式,确认模式切换不会丢失当前输入。
- [ ] 测试优化模型切换,并确认目标生成模型与优化模型显示和请求职责分离。
- [ ] 测试已连接图片、文本参考,以及“结合参考”没有参考内容时的提示和禁用状态。
- [ ] 测试长提示词、长优化结果、滚动、拖动和四边缩放;确认窗口接近边缘时不会跑出视口。
- [ ] 测试浅色/深色主题和窄窗口布局,确认按钮、输入框和结果文本仍可读且可操作。
- [ ] 使用自定义文本渠道实际发送一次,确认渠道、模型和权限配置正确时不会误报“必须指定系统渠道和模型”。
用户确认上述场景后,再将对应条目移动到功能文档的已验证范围。
## 画布节点自动整理
自动整理入口和布局提交逻辑已完成,以下场景仍需要在实际画布中手工确认:
- [x] `web` 前端 TypeScript 检查与生产构建通过。
- [x] 画布底部视图控制中显示“自动整理节点”按钮。
- [ ] 无选中节点时,确认多个顶层节点会按连线流程布局;无连线时会按网格布局。
- [ ] 选中节点时,确认只移动选中的可移动节点,未选中节点保持原位。
- [ ] 确认锁定节点、容器节点和折叠批次隐藏子节点不会被移动。
- [ ] 点击“撤销”确认可以恢复整理前的位置,并确认刷新画布后布局仍能保存。
- [ ] 在浅色/深色主题、窄窗口和节点数量较多的画布中确认底部按钮可见、可操作且不遮挡主要内容。
## 分镜脚本资产关联
@@ -16,3 +50,25 @@
4. 零价 Token 任务在上游未返回 usage 时仍能以 `0` 结算;正价 Token 任务继续依赖 usage 结算。
5. 管理端提交任一负价格仍会被拒绝。
6. 未勾选价格已配置的默认零价模型仍不会向普通用户公开。
## 用户诊断包导出
当前实现已完成前后端链路、脱敏和设置页 UI,但仍需要真实环境验证下载链路和不同错误场景:
- [x] `web` 前端 TypeScript 检查与生产构建通过。
- [x] 诊断服务已覆盖脱敏、ZIP 结构和跨用户任务拒绝测试。
- [x] 设置页桌面布局和 680px 窄屏布局已完成浏览器预览。
- [ ] 登录后从设置页导出成功,确认 ZIP、诊断编号和文件名均可用。
- [ ] 从失败任务详情进入诊断页,确认 `taskId`、`projectId` 和对应日志范围正确带入。
- [ ] 后端未重启或接口未部署时,确认前端能显示可操作的错误提示,而不是原始 Blob/502 文案。
- [ ] 验证浅色/深色主题、15 分钟到 24 小时范围、长问题描述和导出中的 loading 状态。
## Eagle 素材库
- [x] 增加素材无文件夹、无标签时的空集合归一化单元测试。
- [ ] 使用真实 Eagle 服务验证无文件夹素材、无标签素材、文件夹路径和预览引用。
## Windows 本地开发
- [ ] 在一台依赖已安装和一台全新工作区分别运行 `.\scripts\start-local.ps1`。
- [ ] 验证端口被占用时脚本会安全退出,且不会关闭已有进程或使用正式开发数据目录。
+21 -5
View File
@@ -1,8 +1,8 @@
# 画布浮动控件设计沉淀:顶部操作区与底部 Dock
> 范围:无限画布的顶部项目操作区、底部画布工具 Dock,以及工作模式切换。
> 范围:无限画布的顶部项目操作区、底部画布工具 Dock、小地图,以及工作模式切换。
> 目标:让画布操作控件形成统一的浮动面板系统,并在不同主题和视口下保持稳定对齐。
> 状态:已实现首版,浏览器回归待确认。
> 状态:已实现首版,构建与当前画布回归已确认。
## 1. 设计方向
@@ -39,10 +39,24 @@
- `web/src/pages/canvas/canvas-project-top-bar.tsx`:顶部左右浮动面板,以及工作模式切换组件和下拉菜单。
- `web/src/pages/canvas/project.tsx`:将工作模式切换定位到画布右下角,并与底部控制使用同一安全区。
- `web/src/components/canvas/canvas-zoom-controls.tsx`:底部视图控制 Dock 的既有实现,作为浮动控件视觉基准。
- `web/src/components/canvas/canvas-mini-map.tsx`:小地图节点缩略图、标题标注和视口拖动预览。
- `web/src/components/canvas/canvas-overlay-layer.tsx`:画布浮层的激活层级管理,按点击和焦点提升当前浮层。
- `web/src/lib/canvas/canvas-aceternity-style.ts`:提供 `canvasDockStyle` 样式合同。
- `web/src/styles/globals.css`:维护顶部面板、控件状态和工作模式菜单的唯一样式规则。
## 5. 响应式与交互边界
## 5. 小地图信息表达
小地图是“定位与导航”视图,不复制完整节点内容,但必须让节点在缩略比例下仍然可识别:
- 图片节点优先复用 `storageKey`、预览地址或导入源生成缩略图;资源通过缓存组件延迟加载。
- 所有节点保留一个短标题标注。标题单行显示并限制最大宽度,过长时省略,悬停可查看完整名称。
- 非图片节点继续使用类型色块;图片缩略图加载失败时回退到类型色块,不伪造媒体内容。
- 可加载缩略图的图片节点不超过 24 个时显示缩略图;超过阈值后统一回退到色块,避免小地图成为媒体解码热点。
- 标注只覆盖在小地图上,不移动画布节点,也不做空间避让;折叠背板内的节点不重复显示。
底部“适应画布”命令的语义是把当前画布内容整体纳入视口,和小地图的导航职责分开:小地图用于定位,适应画布用于一次性恢复内容总览。
## 6. 响应式与交互边界
- 桌面端顶部左右面板保持同一顶部安全区;底部左右面板保持同一底部安全区。
- 窄视口沿用 `--canvas-inset-y + --space-16` 的底部避让规则,避免与移动端底部工具区域重叠。
@@ -50,15 +64,17 @@
- `prefers-reduced-motion` 下关闭或缩短面板和选项的进入动画。
- 本次只调整控件位置和视觉层级,不改变菜单命令、模式持久化和画布操作行为。
## 6. 验证与待回归
## 7. 验证与待回归
已完成:
- `git diff --check` 通过。
- Impeccable 受限扫描未发现本次新增的 UI 反模式。
- `web` 目录执行 `bun run build` 通过。
- 当前画布已确认小地图缩略图、标题标注和视口拖动区域正常显示。
待确认:
- 浅色/深色主题下检查顶部左右面板、底部三个 Dock 的边界和阴影一致性。
- 检查工作模式菜单在桌面、窄视口和底部工具栏打开时的对齐与遮挡关系。
- 本项目按规则未自动运行 TypeScript、构建或测试;提交前只做了静态检查。
- 仍需在更多节点数量、深色主题和极端缩放比例下补充回归;当前构建未覆盖后端集成测试。
+515
View File
@@ -0,0 +1,515 @@
# 用户诊断包设计
本文定义影策“导出诊断包”功能的产品流程、前端采集、后端聚合、链路关联、安全脱敏和实施边界。第一阶段 MVP 已实现安全下载脱敏诊断包;提示词、截图、自动上传和异步大包等增强能力仍按后续阶段规划。
## 目标
当普通用户遇到页面报错、任务失败、生成卡住或插件不可用时,不要求用户打开开发者工具或理解技术日志,只需一键导出诊断包并提交给支持人员。开发人员应能从诊断包还原以下链路:
```text
用户操作
-> 前端事件或异常
-> HTTP 请求
-> 后端业务任务
-> 模型上游请求
-> 响应或失败结果
```
第一阶段需要满足:
- 用户在任务失败后可以用不超过三步导出诊断包。
- 诊断包同时包含前端上下文和当前用户有权访问的后端记录。
- 开发人员可以通过关联 ID 查询一次操作涉及的请求、任务和上游调用。
- 所有内容默认脱敏;当前 MVP 不提供加入提示词、截图或其他敏感数据的选项。
- 单个诊断包有明确的时间、条数和体积上限,不能影响正常任务执行。
## 非目标
第一阶段不实现:
- 自动把诊断包上传到客服、工单或第三方错误平台。
- 全量采集浏览器控制台输出、键盘输入或用户操作录像。
- 导出服务器数据库、环境变量、进程日志或宿主机文件。
- 在管理后台直接渲染用户上传的任意 HTML 或其他不可信文件。
- 用诊断包代替服务器监控、告警和生产可观测性系统。
## 用户流程
### 通用入口
在“设置 -> 问题诊断”中提供“导出诊断包”。默认流程如下:
1. 自动选择最近 30 分钟。
2. 用户填写一句问题描述,例如“生成视频一直停在处理中”。
3. 用户点击“导出”,浏览器下载 ZIP 文件。
4. 页面展示诊断编号,用户将 ZIP 和编号一并提交给支持人员。
文件名示例:
```text
影策诊断包-20260825-143012-DIAG_8F3K2P.zip
```
### 失败任务快捷入口
当前实现先在任务中心失败详情提供“导出诊断包”快捷入口,并自动填入 `taskId` 和 `projectId`。任务创建前后自动扩展时间范围、通知入口和其他失败提示入口属于后续增强。
### 当前选项
当前 MVP 只提供必要控制:
- 时间范围:最近 15 分钟、30 分钟、1 小时或 24 小时;当前不提供自定义时间范围。
- 问题描述:最多 1000 个字符。
- 数据预览:预计包含的前端事件、任务记录和上游调用数量。
提示词、截图和可选项目元数据的开关属于后续阶段,当前不会写入诊断包。
界面应使用“问题描述”“诊断包”“问题编号”等普通用户能理解的词,不把 `traceId`、JSONL 或调用链作为主界面概念。
## 总体架构
第一阶段由浏览器提交短期前端事件,后端按当前登录用户聚合数据库记录,统一脱敏后同步生成 ZIP:
```text
前端诊断缓冲区
+ 当前页面、项目、任务上下文
+ 用户问题描述
|
v
POST /api/diagnostics/export
|
v
后端权限校验 -> 数据查询 -> 统一脱敏 -> 限制与截断 -> ZIP 二进制
|
v
浏览器下载诊断包
```
后端是诊断包内容和脱敏策略的最终边界。即使前端已经脱敏,后端仍需再次执行白名单过滤和敏感值清理。
## 关联 ID
诊断能力依赖统一的链路标识。建议区分以下四类 ID:
| 字段 | 生命周期 | 用途 |
| --- | --- | --- |
| `traceId` | 一次用户业务操作 | 串联一次生成、重试或导入涉及的多个 HTTP 请求。 |
| `requestId` | 单个 HTTP 请求 | 定位前端请求与 Gin 请求处理。 |
| `taskId` | 一个异步业务任务 | 关联任务状态、任务日志、计费和结果。 |
| `providerRequestId` | 一次上游模型调用 | 查询渠道供应商侧任务或请求。 |
典型链路为:
```text
clientEvent.traceId
-> request.requestId
-> task.taskId
-> apiCallLog.providerRequestId
```
后端增加请求中间件,为每个请求生成不可预测的 `requestId`,写入 Gin Context,并通过 `X-Request-ID` 响应头返回。客户端提供的 ID 只能在通过长度、字符集和格式校验后作为 `traceId` 使用,不能直接覆盖后端生成的 `requestId`。
一次生成操作由前端生成 `traceId`,通过 `X-Canvas-Trace-ID` 请求头传递。轮询、取消、重试等同一业务操作继续使用原 `traceId`;用户主动发起的新操作生成新值。
## 前端设计
### 模块边界
当前 MVP 实现:
```text
web/src/services/diagnostics/
client-diagnostics.ts
diagnostics-api.ts
```
- `client-diagnostics.ts`:环形缓冲区、全局错误/请求事件采集、上下文关联和浏览器侧脱敏。
- `diagnostics-api.ts`:预览、ZIP 导出请求和浏览器下载处理。
事件和导出参数类型暂时与对应模块 colocate;后续协议稳定、类型复用扩大后再拆分独立文件。
页面入口可以放在设置页面的私有组件目录;任务失败快捷入口复用同一个导出弹窗,不维护第二套导出逻辑。
### 客户端事件结构
```ts
type ClientDiagnosticEvent = {
id: string;
timestamp: string;
level: "info" | "warning" | "error";
category: "navigation" | "action" | "request" | "runtime" | "task" | "plugin";
code?: string;
message: string;
route?: string;
durationMs?: number;
httpStatus?: number;
requestId?: string;
traceId?: string;
taskId?: string;
projectId?: string;
canvasId?: string;
stack?: string;
};
```
事件只记录诊断所需的结果和关联字段,不保存组件完整状态、表单值或请求正文。
### 当前采集范围
当前 MVP 已采集:
- `window.error`。
- `unhandledrejection`。
- 公共 `apiClient` 的 API 成功、业务失败和 HTTP 失败摘要,包括方法、规范化路径、状态、业务错误码、耗时和 `X-Request-ID`。
- 创建任务事件。
- 应用版本、构建提交、浏览器、操作系统和时区。
第一阶段不采集:
- 用户键盘、鼠标坐标和连续操作录像。
- 全量 `console.log`、`console.info` 或第三方库调试输出。
- Cookie、请求头、请求正文和响应正文。
- Blob、data URL、图片、视频和完整画布快照。
### 缓冲与持久化
使用固定容量环形缓冲区,当前最多保存 500 条事件,默认只保存在内存中,不写入 `localforage` 或其他持久化存储。用户切换或退出登录时清空当前缓冲区,避免账号之间串数据。
内存缓冲区需要:
- 按登录用户隔离,用户 scope 变化时清理。
- 单条堆栈和消息分别限制长度。
- 无法序列化的对象转换为固定错误描述,不递归保存原始对象。
### API 请求采集
在公共 `apiClient` 的请求和响应拦截器中完成统一采集:
- 请求开始时保存方法、规范化路径、开始时间和 `traceId`,并发送 `X-Canvas-Trace-ID`。
- 响应结束时记录状态、耗时、业务错误码和 `X-Request-ID`。
- URL 删除查询字符串和片段,动态资源 ID可以保留,令牌和签名参数不得保留。
- 二进制、SSE 和本地 Agent 请求不复制完整载荷。
### 生产堆栈
如果生产构建不提供可解析的源码映射,前端堆栈只能定位到压缩后的文件。源码映射应作为受控的构建产物保存给开发团队,不公开部署到无需鉴权即可下载的位置。诊断包记录应用版本、构建 Commit 和资源文件名,开发人员据此使用对应源码映射还原堆栈。
## 后端设计
### 分层
当前 MVP 实现:
```text
backend/internal/handler/diagnostics.go
backend/internal/service/diagnostics.go
backend/internal/repository/diagnostics.go
```
- Handler:绑定请求、获取当前用户、设置 ZIP 响应头并输出二进制内容。
- Service:校验时间和上下文归属、聚合数据、脱敏、截断并生成清单。
- Repository:只提供带 `userID` 的诊断查询,不向 Handler 暴露通用全表查询。
第一阶段同步生成 ZIP,不新增诊断包数据库表,也不在服务器长期保存 ZIP。当前实现先在内存中生成并限制压缩包不超过 10 MB;数据量增大后再升级为异步任务或流式生成。
### 数据来源
优先复用现有业务数据:
- `Task`:任务状态、时间、错误码和所属项目。
- `TaskLog`:任务执行步骤和结构化失败信息。
- `ApiCallLog`:渠道、模型、耗时、状态、错误码和 `providerRequestId`。
- 前端运行时信息(版本、构建提交、浏览器、操作系统和时区)。
- 前端请求体提交的短期客户端事件。
Gin 当前标准访问日志输出到进程标准输出,无法可靠地按用户归属查询,因此不作为用户诊断包的数据源。服务器进程日志应属于独立的管理员诊断能力。
### 模型调整
为形成完整链路,当前已增加:
```go
type Task struct {
RequestID string
TraceID string
// existing fields...
}
type ApiCallLog struct {
RequestID string
TraceID string
// existing fields...
}
type TaskLog struct {
RequestID string
TraceID string
// existing fields...
}
```
`Task`、`TaskLog` 和 `ApiCallLog` 都保存关联字段,并为 `request_id`、`trace_id` 增加查询索引;诊断查询继续带当前用户和时间范围约束。
字段和索引说明同步维护在后端数据库专题文档中。
## API 设计
### 预览
```http
POST /api/diagnostics/preview
Content-Type: application/json
```
请求示例:
```json
{
"from": "2026-08-25T06:00:00Z",
"to": "2026-08-25T06:30:00Z",
"taskId": "task_xxx",
"projectId": "project_xxx"
}
```
响应使用统一业务信封:
```json
{
"code": 0,
"data": {
"clientEventLimit": 500,
"taskCount": 1,
"taskLogCount": 18,
"apiCallCount": 4,
"estimatedBytes": 86000,
"willTruncate": false
},
"msg": "ok"
}
```
预览数量只是当前估算,导出期间产生的新事件可以不加入本次诊断包。
### 导出
```http
POST /api/diagnostics/export
Content-Type: application/json
Accept: application/zip
```
请求示例:
```json
{
"from": "2026-08-25T06:00:00Z",
"to": "2026-08-25T06:30:00Z",
"taskId": "task_xxx",
"projectId": "project_xxx",
"description": "生成视频一直停在处理中",
"runtime": {
"appVersion": "0.1.0",
"buildCommit": "local"
},
"clientEvents": []
}
```
成功响应:
```http
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename="yingce-diagnostics-20260825-143012-DIAG_8F3K2P.zip"
X-Diagnostic-Bundle-ID: DIAG_8F3K2P
X-Diagnostic-Schema-Version: 1
```
由于成功响应是二进制,不经过只处理 JSON 信封的 `request<T>` 解包函数;仍复用现有 `apiClient` 的认证和 Base URL,设置 `responseType: "blob"`。错误响应继续使用统一 `{ code, data, msg }` 语义,前端按响应类型解析。
### 权限规则
- 必须登录才能预览或导出。
- `taskId`、`projectId` 都由 Service 校验归属。
- Repository 查询始终同时带当前 `userID`,不能先按 ID 查询后再由前端隐藏。
- 不能因为用户知道其他任务 ID 而导出其他用户的日志。
- 管理员也不通过用户接口绕过归属;管理员诊断使用独立接口和审计规则。
## 诊断包格式
第一版 ZIP 结构:
```text
manifest.json
README.txt
client/
events.jsonl
backend/
tasks.jsonl
task-logs.jsonl
upstream-calls.jsonl
context/
runtime.json
```
插件上下文、功能开关、截图和其他可选媒体尚未进入当前 MVP,后续增加时必须单独设计隐私开关和脱敏规则。
JSONL 文件一行一个对象,便于流式生成、截断和命令行处理。所有文件使用 UTF-8。
### Manifest
```json
{
"schemaVersion": 1,
"bundleId": "DIAG_8F3K2P",
"generatedAt": "2026-08-25T06:30:12Z",
"appVersion": "0.0.0",
"buildCommit": "abcdef1",
"timeRange": {
"from": "2026-08-25T06:00:00Z",
"to": "2026-08-25T06:30:00Z"
},
"redactionVersion": 1,
"truncated": false,
"counts": {
"clientEvents": 42,
"tasks": 1,
"taskLogs": 18,
"upstreamCalls": 4
}
}
```
Manifest 不保存用户邮箱、用户名和真实 API Key。需要在包内区分同一用户时,使用只在当前诊断包内稳定的随机别名或哈希,不创建跨诊断包追踪标识。
## 脱敏规则
### 默认禁止内容
无论用户如何选择,以下内容都不能进入诊断包:
- `Authorization`、Cookie、Session、API Key、Access Key、Secret、Password、Token。
- `x-goog-api-key`、`X-Canvas-Upstream-Headers` 等上游认证头。
- 数据库、环境变量、`.settings-key` 和服务器配置原文。
- 本机绝对文件路径、内部数据库路径和对象存储签名参数。
- 完整浏览器存储、完整请求头和完整响应头。
- 原始媒体文件和 data URL。
### 默认关闭内容
以下内容只有用户明确选择后才能加入,并在界面说明隐私影响:
- 提示词原文和消息正文。
- 项目、画布、素材的用户自定义名称。
- 页面截图。
- 上游请求或响应中的业务正文。
即使用户选择包含,仍需执行密钥清理、字段白名单、字符串截断和 URL 去签名。
### 脱敏实现
后端维护单一脱敏服务,按以下顺序处理:
1. 只构造允许导出的字段,不直接序列化完整数据库模型。
2. 递归删除敏感键名。
3. 对字符串执行 Token、Bearer、密钥和签名 URL 模式清理。
4. 对路径、错误堆栈和上游错误信息执行长度限制。
5. 将被清理的内容替换为 `[REDACTED]`,不留原值摘要。
不要只依赖正则表达式处理完整对象;字段白名单是主要边界,模式清理只作为第二层防护。
## 限制与降级
建议第一阶段采用以下上限:
| 项目 | 上限 |
| --- | --- |
| 时间范围 | 24 小时 |
| 前端事件 | 500 条 |
| 任务 | 100 条 |
| 任务日志 | 2,000 条 |
| 上游调用 | 1,000 条 |
| 问题描述 | 1,000 字符 |
| 单条消息或堆栈 | 4,000 字符 |
| ZIP 总大小 | 10 MB |
| 用户截图 | 当前不支持 |
达到条数限制时优先保留错误事件、选中任务和距离错误最近的记录,并在 `manifest.json` 中写入 `truncated: true` 和各数据源的截断数量。
达到 ZIP 体积限制时,依次移除:普通信息事件、成功请求、冗长堆栈和可选截图。不能为了完成导出而放宽敏感字段规则。
导出失败不能影响原任务和用户数据。前端展示可操作错误,例如“时间范围过大,请缩短到 1 小时”,而不是笼统显示“系统错误”。
## 开发人员排障流程
拿到诊断包后建议按以下顺序检查:
1. 阅读 `README.txt` 和 `manifest.json`,确认版本、时间范围、是否截断。
2. 从用户描述或失败任务找到 `traceId`、`requestId` 和 `taskId`。
3. 在 `client/events.jsonl` 中还原用户操作和前端异常。
4. 在 `backend/tasks.jsonl` 和 `task-logs.jsonl` 中确认业务状态变化。
5. 在 `backend/upstream-calls.jsonl` 中检查渠道、模型、状态码、耗时和 `providerRequestId`。
6. 使用构建版本对应的源码映射还原前端堆栈。
第二阶段可以提供本地只读诊断查看器,将 JSONL 转换为时间线。查看器不得执行诊断包中的脚本或渲染不可信 HTML。
## 测试要求
### 前端
- 环形缓冲区按时间和容量正确淘汰。
- 用户切换后不会读取其他账号的缓存。
- Axios 成功、业务失败、HTTP 失败和取消请求均生成正确摘要。
- URL 查询参数、Token 和请求正文不会进入事件。
- 任务失败快捷入口正确带入 `taskId` 和时间范围。
- 二进制下载失败时可以解析后端 JSON 错误。
### 后端
- 普通用户无法通过猜测 ID 导出其他用户的数据。
- 时间范围、条数、消息长度和 ZIP 体积限制生效。
- `RequestBody`、`ResponseBody`、`UpstreamURL` 中的密钥和签名参数不会泄漏。
- ZIP 文件名、响应头、Manifest 和 JSONL 格式正确。
- 达到上限时保留失败任务并正确标记截断。
- 客户端提交恶意字段、超深 JSON、超长字符串和伪造关联 ID 时安全拒绝或截断。
- 生成失败不会留下长期临时 ZIP 文件。
### 验收场景
至少验证:
1. 前端渲染异常。
2. 后端返回业务错误码。
3. 模型上游返回 429 或 5xx。
4. 异步任务长时间处理中。
5. 插件连接失败。
6. 用户主动取消请求。
7. 诊断数据超过限制。
8. 包含疑似 API Key、Cookie 和签名 URL 的错误文本。
## 实施阶段
### 第一阶段:可用的安全诊断包
- 增加 `traceId`、`requestId` 和响应头。
- 建立前端错误与请求环形缓冲区。
- 增加设置入口和任务失败快捷入口。
- 后端关联 `Task`、`TaskLog` 和 `ApiCallLog`。
- 同步生成脱敏 ZIP,不落盘。
- 补齐脱敏、权限、限制和下载测试。
### 第二阶段:提升提交与分析效率
- 异步生成诊断包并设置 24 小时自动过期。
- 支持用户主动附加截图。
- 提供离线诊断时间线查看器。
- 管理员日志页面支持按 `traceId`、`requestId` 和诊断编号筛选。
### 第三阶段:生产可观测性联动
- 将结构化关联字段接入 OpenTelemetry 或内部监控。
- 对相同错误码和堆栈进行聚类。
- 在用户明确同意和部署方配置完成后接入工单提交。
生产可观测性与用户诊断包共享关联 ID,但权限、保留周期和数据范围保持独立。
+10 -6
View File
@@ -8,7 +8,9 @@
- [画布节点可读性设计沉淀](design/canvas-node-visual-contrast.mdx):节点外壳、空态和图片创作面板在浅色/深色画布上的表面、边界、阴影与控件状态约束。
- [画布浮动控件设计沉淀](design/canvas-floating-controls.mdx):顶部操作区、底部 Dock 和右下角工作模式切换的浮动面板、定位与响应式约束。
- [画布浮动控件设计沉淀](design/canvas-floating-controls.mdx):顶部操作区、底部 Dock、小地图和右下角工作模式切换的浮动面板、定位与响应式约束。
- [用户诊断包设计](design/user-diagnostic-bundle.mdx):面向普通用户的一键日志导出、前后端链路关联、脱敏、权限与排障方案。
## 本地协作文档(不随仓库分发)
@@ -16,9 +18,11 @@
## 按约定维护的文档(`docs/content/docs/`)
功能、代码地图、待办、待测试分别维护在以下页面(当前缺失,待后续任务重建):
功能、代码地图、待办、待测试分别维护在以下页面;尚未建立的专题会在对应任务中补齐:
- 功能:`features.mdx`
- 代码地图:`code-map.mdx`
- 待办:`todo.mdx`
- 待测试:`pending-test.mdx`
- [功能](content/docs/overview/features.mdx)
- [本地开发](content/docs/backend/local-development.mdx)
- [后端数据库](content/docs/backend/backend-database.mdx)
- 代码地图:待补充
- 待办:待补充
- [待测试](content/docs/progress/pending-test.mdx)