merge: 集成V2.6 性能增强与小宝后台化

# Conflicts:
#	apps/server/prisma/schema.prisma
#	apps/server/src/app.module.ts
#	apps/web/components/layout/Sidebar.tsx
#	docs/architecture.md
#	docs/decisions.md
#	docs/roadmap.md
This commit is contained in:
2026-07-08 18:04:39 +08:00
60 changed files with 4933 additions and 46 deletions

View File

@@ -623,3 +623,60 @@
- Xiaobao risk snapshots/insights 的 AppData key 进入 `read_only_archive`,关系表写入和后台化归 V2.6`xiaobao-warning-views` 读状态 API 归 V2.7。
**理由**:冻结写入能立即切断新的双主源风险,同时保留旧 JSON 的审计和回滚价值。把权限和审计合并到领域 mutation 装饰器,可以确保后续新增写接口默认带服务端 guard 和 audit event。审计覆盖对历史数据只告警避免为了“补齐历史审计”伪造事件。auth header adapter 给 V2.5 一个可测试的服务端权限边界,但不把它包装成最终安全方案,后续 JWT/企业 RBAC 可以替换 adapter 而不改领域 controller 合同。
## 48. V2.6 后台任务先采用 PostgreSQL Lease 队列
**问题**小宝风险摘要、AI 解读和后续通知都需要在用户不打开页面时后台刷新。直接把这些逻辑放在页面 effect 中会导致无人访问时数据不更新;直接引入 Redis queue 又会增加一套可靠性、幂等和迁移运维面。
**决策**
- 新增 `background_jobs` 表和 `JobsModule`,作为 V2.6 后台任务运行时。
- Job 行包含 `type``payload``dedupe_key``status``attempts``max_attempts``available_at``locked_by``locked_until``last_error`
- 同一 `type + dedupe_key``queued/running` 状态下唯一;服务层先查 active job遇到并发唯一冲突再回读保证 enqueue 幂等。
- Worker claim 使用数据库事务、`FOR UPDATE SKIP LOCKED` 和 lease 时间;`running``locked_until` 过期的 job 可以被新 worker 回收。
- Handler 失败时按 `attempts < max_attempts` 重回 `queued` 并设置下一次 `available_at`;达到上限后进入 `failed`,只记录错误,不修改业务实体。
- `BackgroundJobWorker` 只负责 handler 注册和单次执行,业务副作用仍放在各领域 service 内,避免队列层知道小宝、通知或审计细节。
**理由**PostgreSQL 队列足够支撑 V2.6 的低频后台刷新,同时能和领域写入共享事务边界、唯一约束和迁移流程。等 V2.7 通知或更高吞吐任务落地后,如确实需要 Redis/专用队列,再通过同一 `JobsService` 接口替换底层实现,而不是现在提前引入第二套事实源。
## 49. V2.6 小宝风险摘要改为服务端后台刷新
**问题**:小宝预警最初由页面加载完整前端 store 后计算并保存快照/缓存。这样会导致没人打开页面时 `xiaobao_risk_summaries` 不刷新,侧边栏和 V2.2 快读只能看到旧风险。
**决策**
- 新增 `XiaobaoModule`,包含 `XiaobaoRiskService``XiaobaoRiskWorker` 和最小 controller。
- 服务端先移植确定性规则的核心口径:剩余开发/测试/Bug 工作量、关键缺陷、阻塞项、失败用例、预测延期、置信度和 risk signature。
- `XiaobaoRiskService.markDirtyAndEnqueue(versionId)` 负责 upsert dirty summary 并排入 `xiaobao.summary.refresh`dedupe key 使用 `versionId`
- `XiaobaoRiskWorker` 通过 V2.6 `BackgroundJobWorker` 注册 handler执行时只刷新 `xiaobao_risk_summaries`,不修改 Version、Requirement、DevTask、TestCase、Bug 或 Member。
- 领域写入侧继续通过 `WorkActivityService.markXiaobaoSummaryDirty()` 收口;普通 update/delete 没有 activity 证据时显式标脏,避免风险摘要漏刷新。
**理由**:把 deterministic summary 放到服务端后,读路径不再依赖页面打开,且所有前端仍可沿用 V2.2 summary API。AI 解读仍是后续独立队列,只消费 summary/signature 并写 insight cache本决策不让 AI 或后台 worker 直接改业务实体。
## 50. V2.6 小宝 AI 解读改为服务端队列,只写 insight cache
**问题**:小宝 AI 解读原先由 `/xiaobao-warning` 页面触发。即使 V2.6 已经把 deterministic summary 刷新移到服务端,如果 AI 解读仍依赖页面打开,高风险版本在无人访问时仍不会产生新的解释缓存,也不利于后续 V2.7 通知使用同一解读结果。
**决策**
- 新增 `XiaobaoAiModule`,通过 `XiaobaoAiService``XiaobaoAiWorker` 注册 `xiaobao.ai.interpret` job。
- `XiaobaoRiskService.refreshSummary()` upsert summary 后调用 `XiaobaoAiService.evaluateSummary()`,按 policy 判断是否排入 AI 解读 job。
- AI 触发策略复用页面规则的核心边界:`at_risk``likely_delayed``blocked` 可触发;精确 `riskSignature` 命中时复用缓存;同版本最近 6 小时内已有解读时 cooldown风险等级升级可绕过 cooldown。
- 服务端当前没有完整前端趋势快照上下文,因此 `attention` 只在“距离预期发版日小于等于 1 天且仍有未完成工作”时触发。趋势、置信度下降和明细信号变化的完整 attention 策略等待服务端趋势快照补齐后再扩展。
- Worker 执行时重新读取 `xiaobao_risk_summaries`,若 job payload 的 `riskSignature` 已过期则跳过,避免为旧风险写新解释。
- AI 调用只走现有 `AiService.interpretRisk()` 和 risk prompt/provider 抽象,不新增 SDK 调用、不绕过 AI 配置。
- AI 成功后只写 `xiaobao_risk_insights`,缓存保存时间使用服务端 `now`,不信任模型返回的 `generatedAt` 作为缓存新鲜度;失败抛错交给 background job retry。
- Worker 不修改 Version、Requirement、DevTask、TestCase、Bug、Member 等业务实体也不写通知。V2.7 通知如需消费结果,应通过 insight cache 或 adapter 读取。
**理由**AI 解读是对确定性规则结果的解释层,不是业务事实源。把它做成 summary 后置队列,能让无人打开页面时也生成解释,同时通过 signature/cooldown/escalation 控制成本和重复调用。只写 cache 能保持 AI 与业务实体解耦,后续通知和审计可以复用缓存,而不是让 AI worker 直接参与业务状态流转。
## 51. V2.6 运维看板先做轻量运行时快照RBAC 通过 adapter 衔接
**问题**V2.6 增加了性能 harness、后台 job runtime、小宝 summary refresh 和 AI 解读队列。如果没有一个运行时入口慢请求、慢查询、job 堆积和 dirty summary 数只能从日志或数据库手工排查。与此同时V2.5 后端 RBAC/audit 合同尚未落地,不能为了看板临时硬编码一套后端权限结构。
**决策**
- 新增 `OpsModule`,提供 `GET /api/v1/ops/runtime`,返回慢请求、慢 Prisma 查询、后台任务队列、失败任务和 dirty summary 数。
- 慢请求继续由 `ApiTimingInterceptor` 识别;慢查询继续由 `PrismaService` query event 识别。二者额外写入进程内 ring buffer作为轻量 dashboard 数据源。
- 看板只保留最近事件,不做长期审计。长期审计和多实例聚合等待 V2.5 audit 或后续 observability 方案。
- 请求 URL 去掉 query stringSQL 只展示截断后的 query preview`sk-*`、token、secret、password、authorization 等 key-like 文本统一 redacted不展示 AI provider apiKey、请求参数或环境变量。
- Job 队列从 `background_jobs` 最近 200 行聚合,按 type 展示 queued/running/succeeded/failed并展示最近 failed job 的脱敏 `lastError`
- 前端 `/admin/ops` 使用 `RouteGuard permission="ops:view"`;权限字典新增 `ops:view`,但不默认授给非管理员 preset。后端通过 `OpsPermissionAdapter` 保留 `ops:view` 校验入口,待 V2.5 RBAC guard 落地后替换。
**理由**:当前目标是让 V2.6 的性能和后台化能力可观察,而不是建设完整监控平台。进程内 ring buffer 成本低、对生产数据无额外写放大;结合脱敏规则可避免把 secrets 带进管理端。权限 adapter 明确了未来替换点,避免 Ops 看板和未定型 RBAC/audit 合同互相绑死。