refactor(data): 收口关系表运行时数据源
Some checks failed
Deploy Production / Build, push, deploy, verify (push) Has been cancelled

- 移除已迁移业务 AppData 运行时 fallback,改走领域 API 和关系表快读
- 补齐需求产品负责人、版本计划任务 JSON 和成员 username 回填迁移
- 统一治理字典入口,并补充 AI provider、数据源契约和领域服务测试

Co-Authored-By: Codex GPT-5 <codex@openai.com>
This commit is contained in:
2026-07-09 14:59:49 +08:00
parent 1e6fb0c7aa
commit 2e595c7e72
98 changed files with 2938 additions and 1441 deletions

View File

@@ -33,7 +33,7 @@ Requirement Version TestCase
| 前端 | Next.js 14 (App Router) | TypeScript + 客户端组件为主 |
| UI | Tailwind CSS + Shadcn/ui | 紧凑信息密度、现代风格 |
| 状态 | Zustand | 每个领域一个 store |
| 持久化 | PostgreSQL 关系表领域主写 + AppData 兼容兜底V2.4/V2.5 | 高增长领域直接写关系表AppData 进入禁写、核对、归档退场阶段 |
| 持久化 | PostgreSQL 关系表领域主写 + AppData 归档/配置例外 | 高增长和核心业务运行时走关系表AppData 仅保留迁移归档与少量低频配置 |
| 后端 | NestJS + Prisma + PostgreSQLV2.5 | Product/Project/Version/Requirement/VersionPlan/DevTask/TestCase/Bug/Member/TaskCategory/TaskWorklog/Overtime/WorkActivity 均有领域 CRUDV2.5 收口 RBAC/审计/一致性 |
| AI | Anthropic SDKV3 远景) | 健康度/风险预警/排期建议 |
@@ -114,12 +114,12 @@ DevTask 没有"已完成"状态,"已提测"就是终态——开发交付完
## 数据持久化
**当前 V2.5 分层:** 领域 CRUD 主写 + AppData 禁写/核对/归档退场
**当前 V2.5+ 分层:** 领域 CRUD 主写 + AppData 归档/配置例外
领域主写层已经覆盖主要业务实体:
- 根数据Product、Project、Version 直接写领域 API`products-overview` 只作为兼容读取/兜底
- 根数据Product、Project、Version 直接写领域 API;正常业务运行时不再读取 `products-overview`,该文档只用于迁移、归档导出和人工核对
- 需求池Requirement 直接按 `productId` 分区键写 `requirements`,列表/search/filter/sort 使用服务端分页。
- 版本详情VersionPlan、DevTask、TestCase、Bug 直接按 `versionId` 分区键写关系表,并继续标脏 Xiaobao 摘要和写入工作活动证据。
- 版本详情VersionPlan、DevTask、TestCase、Bug 直接按 `versionId` 分区键写关系表,并继续标脏 Xiaobao 摘要和写入工作活动证据。
- 字典/成员/证据Member 写 `users` 的成员身份字段TaskCategory 写 `task_categories`TaskWorklog、OvertimeRecord、WorkActivity 保持追加/证据型关系表写入。
V2.5 控制面新增三类横切能力:
@@ -129,22 +129,22 @@ V2.5 控制面新增三类横切能力:
- 一致性:`GET /api/v1/consistency``pnpm consistency:v25` 检查 counts、分区键、孤儿引用和审计覆盖。历史数据没有审计事件时只报 warning不阻断关系表主源运行。
兼容层仍保留通用服务端文档表 `app_data`
- 后端:`apps/server/src/modules/data/` 提供 `GET/PUT /api/v1/data/:key`V2.5 起所有业务 key `AppDataRetirementService` 标记为 `write_frozen``read_only_archive`,冻结写入返回 `409 APP_DATA_WRITE_FROZEN` 并给出替代领域 API。
- 后端:`apps/server/src/modules/data/` 提供 `GET/PUT /api/v1/data/:key` 给迁移、归档、配置例外使用;已迁移业务 key 由 `AppDataRetirementService` 标记为 `write_frozen``read_only_archive`,冻结写入返回 `409 APP_DATA_WRITE_FROZEN` 并给出替代领域 API。
- 数据库Prisma `AppData` 模型,表名 `app_data``key` 为主键,`value` 为 JSONB
- 一致性:`GET` 返回 `updatedAt` 派生的 `version`;前端保存时带上最近读取的 `version`,后端用 `key + updatedAt` 原子更新,版本不匹配返回 `409 APP_DATA_CONFLICT`
- 前端:各 Zustand store 保持现有 UI 数据形状,优先调用 `apps/web/lib/domain-api.ts``apps/web/lib/server-data.ts` 只保留兼容读取和冻结写入错误处理,不再作为业务保存 fallback。
- 仍留在 AppData 历史形状中的内容:部门、角色、密码规则、加班原因等尚未拆出独立 RBAC/配置表的低频配置。`members``overtime` AppData key 已禁写;这些配置的独立表/API 归 V2.7 管理治理阶段承接
- 前端:各 Zustand store 保持现有 UI 数据形状,正常业务读写优先调用 `apps/web/lib/domain-api.ts` 和 V2.2 快读 API`apps/web/lib/server-data.ts` 不再作为业务运行时 fallback。
- 仍留在 AppData 历史形状中的内容:部门、角色、密码规则、加班原因等尚未拆出独立 RBAC/配置表的低频配置。成员身份已由 `/api/v1/members` 主写关系表,`members` AppData 只保存配置字段,写入时不再夹带成员身份数组;加班记录已由 `/api/v1/overtime` 主写关系表,`overtime` AppData 只保存原因配置
- 浏览器仅保留登录会话(`ftb_auth_session` / `ftb_auth_persist`),不再作为业务数据主存储
关系表层包含 V2.2-V2.4 能力:
- V2.2:高增长业务表使用分区表,并提供版本详情、需求池、工作台和小宝预警的快读 API。
- V2.3AppData 保存成功后触发关系表同步,让快读路径保持新鲜;同步失败只记日志,不阻塞用户保存。
- V2.4:领域 CRUD 成为主写入路径AppData 写桥保留给历史数据和回滚兜底。不要恢复业务 localStorage 缓存,避免线上部署后出现多端数据分叉。
- V2.4:领域 CRUD 成为主写入路径AppData 写桥只服务历史迁移窗口。当前运行时不要恢复业务 AppData 或 localStorage fallback,避免线上部署后出现多端数据分叉。
**目标主源(新方向):** 业务主数据必须落到 PostgreSQL 领域关系表。
- `app_data` 不再作为长期事实源,只保留迁移、回填、兼容读取和故障排查价值。
- `app_data` 不再作为长期事实源,只保留迁移、回填、归档导出、低频配置例外和故障排查价值。
- 新增业务模块不得新增 AppData key 作为主存储必须先设计关系表、Prisma model 和领域 CRUD API。
- 现有 AppData key 需要逐步完成一次性迁移、双读校验、关系表写入切换和 JSON fallback 移除。
- 现有 AppData key 需要逐步完成一次性迁移、双读校验、关系表写入切换和运行时 JSON fallback 移除。
- 前端 store 可以继续保留 Zustand 状态形状,但持久化入口要从 `saveServerData(key)` 迁到领域 API。
- `app_data` 删除前必须有备份/导出和数据量核对,不能直接丢弃历史 JSON。
@@ -281,7 +281,7 @@ The rule surface stays in pure frontend engines:
Managers with `xiaobao.warning:manage` can see all unfinished versions. Non-managers with `xiaobao.warning:view` can only see unfinished versions where the current user is in `version.members`.
AI explains rule results only. It writes interpretation cache to `xiaobao-risk-insights` / `xiaobao_risk_insights` and never mutates Version, Requirement, DevTask, TestCase, Bug, or Member data. Risk snapshots are saved to `xiaobao-risk-snapshots` when the page is opened.
AI explains rule results only. It writes interpretation cache to the relation table `xiaobao_risk_insights` and never mutates Version, Requirement, DevTask, TestCase, Bug, or Member data. Historical AppData keys `xiaobao-risk-insights` and `xiaobao-risk-snapshots` are read-only archive inputs for migration/export, not normal frontend runtime cache.
V2.6 moves the current risk summary refresh to the server:
@@ -290,9 +290,9 @@ V2.6 moves the current risk summary refresh to the server:
- Domain writes that produce work activity already mark the affected version dirty; V2.6 also enqueues a deduped refresh job. Plain update/delete paths for version plans, dev tasks, test cases, and bugs explicitly mark the version dirty as well.
- `XiaobaoAiService` evaluates the refreshed summary and enqueues `xiaobao.ai.interpret` when policy allows. The worker reloads the latest summary, skips stale signatures, calls the existing `AiService.interpretRisk()` prompt path, and writes only `xiaobao_risk_insights`.
- AI interpretation cache uses the summary `riskSignature`, exact cache reuse, a six-hour cooldown, and risk-level escalation bypass. Until the server has full daily trend snapshots, `attention` summaries trigger server-side AI only when release is within one day and unfinished work remains.
- Frontend `/xiaobao-warning` consumes V2.2 summary reads first. The V2.2 response includes the latest generated relation-table AI insight when available, and the frontend prefers that insight over legacy AppData insight cache. It only falls back to AppData risk calculation when summaries are empty or unavailable.
- Frontend `/xiaobao-warning` consumes V2.2 summary reads first. The V2.2 response includes the latest generated relation-table AI insight when available, and the frontend prefers that relation insight. If summaries are empty or unavailable, the page asks the relation-backed stores/domain APIs for scoped data instead of reloading heavy business AppData documents.
Per-user warning read state is saved to `xiaobao-warning-views`. The read marker stores `userId + versionId + risk signature`, so the sidebar can turn the Xiaobao badge blue when any visible risk has a completed unread update, then return to the red risk-count badge after the user opens every updated warning. AI interpretation that is still generating only shows the "updating" notice and must not produce the blue update badge yet.
Per-user warning read state no longer writes `xiaobao-warning-views` AppData at runtime. Until a dedicated per-user read-state API lands, the frontend keeps this as browser-local UI state keyed by `userId + versionId + risk signature`, so the sidebar can turn the Xiaobao badge blue when any visible risk has a completed unread update, then return to the red risk-count badge after the user opens every updated warning. AI interpretation that is still generating only shows the "updating" notice and must not produce the blue update badge yet.
## V2.2 Partitioned Domain Data Layer (2026-07-03)
V2.2 starts the move from AppData JSON documents to relation tables for high-volume domains. The first database foundation is partitioned from the start so future growth does not require a disruptive rewrite of primary keys, unique constraints, and foreign-key references.
@@ -346,11 +346,11 @@ The server also has lightweight observability for this phase: a global API timin
Current source-of-truth boundary:
- Product, Project, Version, Requirement, VersionPlan, DevTask, TestCase, Bug, Member, TaskCategory, TaskWorklog, OvertimeRecord, and WorkActivity now have public domain CRUD/write APIs.
- Frontend stores use domain APIs as the primary mutation path. AppData reads remain for archive/fallback inspection, but business AppData writes are frozen server-side and return `APP_DATA_WRITE_FROZEN`.
- `products-overview` is no longer the product/project/version tree source of truth; it remains a compatibility document for fallback reads and rollback.
- V2.2 read APIs and V2.3 relation sync remain compatibility infrastructure for fast reads, historical AppData imports, and rollback. They are no longer the main proof of data freshness for domains that now write relation tables directly.
- Frontend stores use domain APIs as the runtime mutation path. Migrated business stores do not call `loadServerData` / `saveServerData` for `products-overview`, `requirements`, `version-plans`, `dev-tasks`, `test-cases`, `bugs`, `task-categories`, `task-worklogs`, or `work-activities`.
- `products-overview` is no longer the product/project/version tree source of truth and is not used for normal fallback reads. It remains only as a historical AppData document for migration rehearsal, archive export, and manual incident inspection.
- V2.2 read APIs and V2.3 relation sync remain compatibility infrastructure for fast reads and historical AppData imports. Runtime data freshness for migrated domains is proven by relation-table domain writes and source-contract tests, not by AppData sync.
- `packages/shared` status contracts have been aligned with the current workflow statuses before the V2.4 write switch.
- V2.5 boundary: audit/RBAC/consistency are active on domain writes. Xiaobao risk snapshots/insights and warning read-state AppData keys are read-only archives pending V2.6 relation writer/backgrounding and V2.7 per-user read-state API.
- V2.5+ boundary: audit/RBAC/consistency are active on domain writes. AppData active exceptions are limited to `members` configuration (departments, roles, password rule) and `overtime` reason configuration; member identity and overtime records stay relation-backed. Xiaobao risk snapshots/insights and warning read-state AppData keys are historical archive inputs only and are not used by normal frontend runtime.
## V2.7 Enterprise Collaboration And Governance Layer (2026-07-08)
@@ -360,7 +360,8 @@ V2.7 adds enterprise collaboration capabilities on top of the relational source-
- `comments`: polymorphic comments for `dev_task / test_case / bug / requirement / version_plan`, with mention metadata and soft deletion.
- `project_members`: project-level Owner/Admin/Member/Viewer governance, now exposed through server-enforced APIs.
- `audit_logs`: append-only governance and collaboration audit events.
- `governance_dictionaries`: centralized requirement type/platform/source dictionaries; task categories continue to use `task_categories`.
- `governance_dictionaries`: centralized requirement type/platform/source dictionaries; the Requirement Pool consumes these dictionaries and no longer exposes local dictionary management drawers.
- Task categories continue to use `task_categories`, but the visible admin entry is `/admin/governance` so development and testing task types are managed together with requirement dictionaries.
V2.7 uses stable server adapters for collaboration and governance modules: