Files
ftb-project-management/docs/architecture.md
2e595c7e72
Some checks failed
Deploy Production / Build, push, deploy, verify (push) Has been cancelled
refactor(data): 收口关系表运行时数据源
- 移除已迁移业务 AppData 运行时 fallback,改走领域 API 和关系表快读
- 补齐需求产品负责人、版本计划任务 JSON 和成员 username 回填迁移
- 统一治理字典入口,并补充 AI provider、数据源契约和领域服务测试

Co-Authored-By: Codex GPT-5 <codex@openai.com>
2026-07-09 14:59:49 +08:00

412 lines
33 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# FTB 智能项目管理系统 — 架构文档
## 项目定位
FTB 是一个面向中小型研发团队的项目管理平台,集成 AI 能力辅助决策。核心层级:
```
产品Product → 项目Project → 版本Version → 需求/任务/用例/Bug
```
不是给测试团队的工具,也不是 Jira 替代品。**核心目标是让产品/项目经理在一个页面看到:"开发完了吗 → 验收过了吗 → 还有多少 BUG → 能不能发布"**。
## 心智模型(重要)
系统设计遵循**三条维度线**的分离:
```
语义线Why 执行线How 质量线Quality
───────────────── ───────────────── ────────────────
Requirement Version TestCase
解释为什么做 驱动状态流转 └─ Bug
不参与流程 所有状态看版本 质量闭环
```
**Requirement需求= 语义层**,解释功能范围和业务原因,不驱动流程。
**Version版本= 执行主线**,所有任务归属版本,状态由版本派生;需求只是可选语义分组。
**TestCase + Bug = 质量闭环**,挂在版本上验收。
## 技术栈
| 层 | 技术 | 说明 |
|---|------|------|
| 前端 | Next.js 14 (App Router) | TypeScript + 客户端组件为主 |
| UI | Tailwind CSS + Shadcn/ui | 紧凑信息密度、现代风格 |
| 状态 | Zustand | 每个领域一个 store |
| 持久化 | 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 远景) | 健康度/风险预警/排期建议 |
## 模块结构
```
apps/web/
├── app/
│ ├── products/ # 产品列表 + 详情(点击项目可跳转)
│ ├── projects/[id]/ # 项目详情:版本卡片/状态胶囊
│ ├── versions/ # 版本列表 + 详情
│ ├── requirements/ # 需求池
│ ├── workspace/ # 与我相关(聚合工作台)
│ └── admin/ # 系统管理(成员/角色/任务类型/审计/一致性/AI 配置)
├── components/
│ ├── product/ # 产品组件
│ ├── version/ # 版本组件PlanTab, CapsuleStages, MemberChips...
│ ├── dev-task/ # 开发任务组件
│ ├── test-case/ # 测试用例组件
│ ├── bug/ # Bug 组件
│ └── requirement/ # 需求组件
├── lib/
│ ├── derive.ts # 派生数据flattenVersions/flattenProjects
│ ├── linkage-engine.ts # 跨模块联动引擎需求↔DevTask 派生状态)
│ ├── workspace-engine.ts # 工作台聚合引擎统一WorkItem
│ ├── version-status.ts # 版本执行态推导
│ ├── version-plan.ts # 计划任务(调研/产品/UI
│ ├── dev-task.ts # 开发任务
│ ├── test-case.ts # 测试用例
│ ├── bug.ts # Bug
│ └── requirement.ts # 需求
└── stores/ # Zustand stores每个领域独立
```
## 关键架构原则
### 1. 引擎模式(关键)
跨模块的数据派生通过**引擎层**处理,不是各 store 互相调用:
- **linkage-engine.ts**:从 DevTask 状态派生需求"实际进度",新建模块只需在此聚合
- **workspace-engine.ts**:聚合 WorkItem 给"与我相关"页面用
引擎是**纯函数**,输入多个 store 数据,输出统一视图。引擎使应用各模块松耦合,新增模块时不需要改其他模块的代码。
### 2. 派生胜过存储
时间字段、状态、进度都尽量从底层数据派生,而不是手填:
- 实际工时 = `startDate → completedAt` 时间差(精确到 0.5h
- 阶段耗时 = 所有阶段任务的最早开始 → 最晚完成
- 需求开发状态 = 从 DevTask 状态聚合
- 版本执行态 = 从所有 DevTask + TestCase + Bug 聚合
- 阶段进度 = 已完成子任务/总子任务
### 3. 时间戳精确到分钟
所有"实际开始/完成"时间都是 ISO 时间戳(含时分秒),不是日期。展示时统一 `slice(0, 16).replace('T', ' ')`
### 4. 数据联动
修改 A 影响 B 时,**B 通过 filter A 派生**,不要双向写。例:
- 需求关联到版本:需求设 `versionId`,版本详情 `requirements.filter(r => r.versionId === id)`
- 任务/用例归属版本:新数据优先使用 `versionId`;旧 DevTask 可通过 `requirementId -> Requirement.versionId` 兼容推导
- 删除版本清理孤儿数据PlanTask/DevTask/TestCase/Bug + 释放 Requirement.versionId
## 状态机概览
| 实体 | 状态流转 |
|------|---------|
| Requirement | pending_review → adopted → planned → developing → testing → released → closedrejected 可回 pending_review|
| VersionPlan | pending → in_progress → completed |
| DevTask | todo → in_progress → testing → submitted终态提测=已完成)|
| TestCase | pending → running → passed/failed/blocked |
| Bug | open → fixing → fixed → verifying → closed/rejected |
DevTask 没有"已完成"状态,"已提测"就是终态——开发交付完成,后续 Bug 单独流转。
## 数据持久化
**当前 V2.5+ 分层:** 领域 CRUD 主写 + AppData 归档/配置例外
领域主写层已经覆盖主要业务实体:
- 根数据Product、Project、Version 直接读写领域 API正常业务运行时不再读取 `products-overview`,该文档只用于迁移、归档导出和人工核对。
- 需求池Requirement 直接按 `productId` 分区键写 `requirements`,列表/search/filter/sort 使用服务端分页。
- 版本详情VersionPlan、DevTask、TestCase、Bug 直接按 `versionId` 分区键读写关系表,并继续标脏 Xiaobao 摘要和写入工作活动证据。
- 字典/成员/证据Member 写 `users` 的成员身份字段TaskCategory 写 `task_categories`TaskWorklog、OvertimeRecord、WorkActivity 保持追加/证据型关系表写入。
V2.5 控制面新增三类横切能力:
- 权限:`apps/server/src/common/auth/` 提供当前用户解析、`@CurrentUser()``@RequirePermission()``PermissionGuard`。当前 V2.5 使用前端会话透传的 `x-ftb-user-*` 请求头作为服务端 auth adapter正式 JWT/NextAuth 接入仍属于后续认证阶段。
- 审计:`audit_events` 是 append-only 表,按 `created_at` 分区,领域 mutation 通过 `@ProtectedMutation()` 同时挂权限、资源作用域和 `AuditMutationInterceptor`。审计查询走 `GET /api/v1/audit`,需要 `audit:view`
- 一致性:`GET /api/v1/consistency``pnpm consistency:v25` 检查 counts、分区键、孤儿引用和审计覆盖。历史数据没有审计事件时只报 warning不阻断关系表主源运行。
兼容层仍保留通用服务端文档表 `app_data`
- 后端:`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` 和 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 写桥只服务历史迁移窗口。当前运行时不要恢复业务 AppData 或 localStorage fallback避免线上部署后出现多端数据分叉。
**目标主源(新方向):** 业务主数据必须落到 PostgreSQL 领域关系表。
- `app_data` 不再作为长期事实源,只保留迁移、回填、归档导出、低频配置例外和故障排查价值。
- 新增业务模块不得新增 AppData key 作为主存储必须先设计关系表、Prisma model 和领域 CRUD API。
- 现有 AppData key 需要逐步完成一次性迁移、双读校验、关系表写入切换和运行时 JSON fallback 移除。
- 前端 store 可以继续保留 Zustand 状态形状,但持久化入口要从 `saveServerData(key)` 迁到领域 API。
- `app_data` 删除前必须有备份/导出和数据量核对,不能直接丢弃历史 JSON。
**V2 迁移阶段边界:**
- V2.1 先把业务数据从浏览器移到服务端 AppData解决部署和清站点数据丢失问题。
- V2.2 建关系表、分区、快读 API 和小宝摘要读取,写入仍走 AppData。
- V2.3 在 AppData 保存成功后非阻塞同步关系表,让快读路径持续有新数据。
- V2.4 逐领域把主写入口迁到关系表 CRUD领域 API 必须从一开始带资源作用域、当前用户、`actorId`、基础审计事件入口、分页和索引边界。
- V2.5 才做 AppData 分阶段退场和 RBAC/审计/一致性收口:先禁写,再双读核对,再移除 fallback最后只读归档/导出,不能直接删历史 JSON。
- V2.6 在关系表主源稳定后做大数据性能增强和小宝预警后台化;性能基础不后置,增强项包括压测、慢查询治理、缓存/摘要、后台任务、幂等和失败重试。
- V2.7 面向企业级协作与管理治理V2.8 面向生产硬化与运维闭环生产部署基线已经存在V2.8 重点是备份恢复演练、发布 smoke test、监控告警、日志检索、迁移回滚和运维手册。
## Background Job Runtime Layer (V2.6)
V2.6 introduces a database-backed background job runtime for server-side refresh work that must not depend on a user opening a page.
- Storage: `background_jobs` stores `type`, `payload`, `dedupe_key`, `status`, `attempts`, `max_attempts`, `available_at`, `locked_by`, `locked_until`, and `last_error`.
- Dedupe: active jobs (`queued` / `running`) are unique by `(type, dedupe_key)` when `dedupe_key` is present. Services still check first and recover from unique conflicts to stay idempotent under concurrent enqueue.
- Lease: `JobLockService.claimNext()` uses `FOR UPDATE SKIP LOCKED` and treats expired `running` rows as claimable, so a crashed worker can be recovered by a later worker.
- Retry: failed handlers are requeued while `attempts < max_attempts`; terminal failures keep `last_error` and move to `failed`.
- Worker boundary: `BackgroundJobWorker` is a small handler registry and single-job runner. Domain modules register typed handlers and only write through their own services.
This runtime is intentionally DB-backed first. Redis is already available in deployment, but V2.6 jobs need transactional dedupe with domain writes more than high-throughput queue semantics.
## Runtime Ops Dashboard (V2.6)
V2.6 adds an Ops runtime surface for local operators and future production admins:
- Backend: `OpsModule` exposes `GET /api/v1/ops/runtime`, reading recent in-memory slow API request events, recent in-memory slow Prisma query events, `background_jobs` queue rows, and dirty `xiaobao_risk_summaries` count.
- Capture: `ApiTimingInterceptor` and `PrismaService` still log slow events, and also append redacted bounded previews into the Ops runtime buffer. SQL query parameters are not exposed; request query strings and key-like values are stripped or redacted.
- Jobs: the dashboard summarizes queued/running/succeeded/failed jobs by type and shows recent terminal failures with redacted `lastError`.
- Permissions: the frontend route is guarded by `ops:view`. Backend RBAC is represented by `OpsPermissionAdapter` until the V2.5 RBAC contract lands, so the replacement point is explicit instead of hard-coupled to temporary role data.
## 生产部署层2026-07-01
当前仓库已补齐云服务器生产部署基线:
- `Dockerfile.web`:以 monorepo 根目录为 build context构建 `@ftb/shared` 和 Next.js 前端,运行 `pnpm --filter web start`
- `Dockerfile.server`:构建 `@ftb/shared` 和 NestJS 后端,执行 `prisma generate`,运行 `pnpm --filter server start:prod`
- `docker-compose.prod.yml`:编排 `web``server``postgres``redis``nginx` 五个服务,服务间通过 Docker 内网通信,对外只暴露 Nginx 80 端口。
- `docker-compose.local.yml`:本地服务器/局域网部署入口,同样编排五个服务,默认对外暴露 `8080`,使用独立 `local_*` volumes避免与云服务器生产数据混用。
- `deploy/nginx/default.conf.template`:同域反向代理,`/api/` 转发到 NestJS其他路径转发到 Next.js。
- `.env.production.example` / `.env.local-server.example`:生产和本地服务器配置模板;正式部署复制为 `.env.production``.env.local-server`,不提交真实密钥。
生产持久化边界:
- 业务主数据在 PostgreSQL `postgres_data` volume 中。
- Redis AOF 在 `redis_data` volume 中。
- AI Provider 配置文件在 `server_data` volume 中,对应容器路径 `/app/apps/server/data`
生产数据库初始化使用 Prisma migration`pnpm --filter server db:deploy`。本地开发仍可使用 `pnpm db:migrate`
## 权限模型V2.5
V2.5 后端 mutation API 已接入服务端 RBAC
- 系统角色先按内置权限字典判断,`role-admin` 支持 wildcard `*`
-`projectId` / `versionId` 的请求会解析资源作用域;项目成员角色按 Owner/Admin/Member/Viewer 授权。
- 版本级资源如果能解析到项目,会优先用 `ProjectMember` 判断;具有系统权限的版本成员也可访问对应版本范围。
- 未认证返回 401已认证但无权限返回 403。
- `@ProtectedMutation(permission, scope, audit)` 是领域写接口的统一入口,避免权限和审计在 controller 中分散实现。
当前 auth context 仍是 V2.5 过渡 adapter前端从 `ftb_auth_session` / `ftb_auth_persist` 读取当前用户并透传 `x-ftb-user-*` 头。JWT/NextAuth 服务端校验、企业级角色/部门/配置表属于 V2.7 之前需要协调的认证治理工作。
## AI Agent 层
详细规范见 `agent-spec.md`。要点:
- AI Agent 不是一个独立服务,而是嵌在前端/后台任务中的特定调用入口。当前 V3.1 Prototype Decompose Agent 与 V3.2 Risk Watch Agent 已落地。
- Agent 写入数据时必须带 `aiDraft: true` 标记,列表中视觉区分(紫色边)。用户编辑后自动清除标记。
- DevTask / TestCase 加入 `references[]` 字段,记录任务/用例的来源(需求 / 原型批注。Agent 和人工创建均强制至少 1 条引用。
- 原型中有明确功能但没有匹配到关联需求时AI 可以生成无需求ID分组草案写入任务/用例的 `requirementName`,不创建 Requirement不加入关联需求列表。
- 原型链接**不在 Version 上独立存储**,而是来自产品方案 (VersionPlan type=product) 已完成计划的 `resultUrl`。约定:提交产品方案的成果就是原型。
- AI 服务实现走 **NestJS 后端**`apps/server/src/modules/ai/`),不走 Next.js API Route。
- API Key 通过 **`/admin/ai-config` 页面配置**(仅超管可见),存到 `apps/server/data/ai-config.json`,不入 git环境变量 `ANTHROPIC_API_KEY` 作为兜底。
新增涉及 AI 的实体字段:
| 实体 | 字段 | 类型 | 说明 |
|------|------|------|------|
| DevTask | versionId | string | 执行归属版本无需求ID分组也通过它进入版本 |
| DevTask | requirementId | string? | 正式需求 ID无需求ID分组为空 |
| DevTask | requirementName | string? | 无正式需求 ID 时的展示分组名 |
| DevTask | references | Reference[]? | 引用来源(需求/原型批注) |
| DevTask | aiDraft | boolean? | AI 草案标记 |
| DevTask | aiDraftAt | string? | AI 生成时间戳 |
| DevTask | aiEstimateHours | number? | AI 预估耗时;执行人预估仍写 estimateHours |
| TestCase | requirementName | string? | 无正式需求 ID 时的展示分组名 |
| TestCase | references | Reference[]? | 同上 |
| TestCase | aiDraft | boolean? | 同上 |
| TestCase | aiDraftAt | string? | 同上 |
| TestCase | aiEstimateHours | number? | AI 预估耗时;执行人预估仍写 estimateHours |
## 版本模块规则层V2.2 设计约束)
版本详情里的计划完成、需求候选和任务类型映射必须走规则层:
- `version-plan-workflow.ts`:调研/产品方案/UI 设计的子任务、需求覆盖、成果提交和完成条件。
- `requirement-selector.ts`:当前版本所属项目下可关联需求的候选筛选,默认只返回 `status === 'adopted'` 的项目需求。
- `task-category.ts`DevTask/TestCase 共用任务类型字典,`id` 用于存储AI 输出的 `taskTypeName` 必须是可复用类型;可复用开发类型可在采纳时自动追加到字典,测试用例未知类型回退到已有测试分类,`code` 仅作可选语义映射。
页面组件只消费规则层输出,不直接拼完成条件或候选筛选条件。
## Production Runtime Version Layer (2026-07-06)
Production deployment now treats CI-built Docker images as the release artifact. `Dockerfile.web` and `Dockerfile.server` accept `APP_VERSION`, `APP_BUILD_TIME`, and image tag build metadata; `docker-compose.prod.yml` pulls immutable `WEB_IMAGE` and `SERVER_IMAGE` tags instead of relying on deployment-side rebuilds.
The server exposes `GET /api/v1/health/version`, returning the running server commit/version metadata. The web app embeds `NEXT_PUBLIC_APP_VERSION` at build time and shows a refresh prompt when the browser is still on an older frontend bundle than the server runtime.
GitHub Actions is the production release orchestrator: build and push images, SSH to the server, update `.env.production` image tags, pull images, run `pnpm --filter server db:deploy`, restart Compose services, and verify `/api/v1/health/version` against the current commit SHA.
## Work Activity Daily Report Layer (2026-06-26)
The personal daily report is derived from two inputs:
- `work-activities`: append-only activity records created by successful business actions.
- `task-worklogs`: legacy/manual worklog records that still contribute hours and written work content.
`work-activity-factory.ts` owns the mapping from domain actions to reportable activity semantics. Zustand stores call this factory after a successful operation, then append the result through `useWorkActivityStore`.
`workspace-daily-report.ts` remains a pure aggregation engine. It groups today's current-user activity into delivery, progress, creation, risk, and note sections, and also detects in-progress work that started before today but has no activity or progress note today.
This is intentionally not a generic rules engine or event bus. The rule surface is explicit, typed, and local to the workspace/daily-report use case.
## Xiaobao Warning Layer (2026-06-29)
Xiaobao Warning is a version-level release-risk capability shown above `/workspace` in the main navigation. It answers whether a version can ship on the expected release date, why it may not, roughly how long it may slip, and which release window is safer.
The rule surface stays in pure frontend engines:
- `xiaobao-risk.ts`: risk score, level, forecast release date, confidence, and current snapshot.
- `xiaobao-risk-evidence.ts`: version work aggregation, daily report/activity evidence, and silent-risk detection.
- `xiaobao-risk-trend.ts`: daily snapshots, trend detection, and snapshot signatures.
- `xiaobao-risk-ai.ts`: AI trigger policy, cache signature, and backend request mapping.
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 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:
- `XiaobaoRiskService.refreshSummary(versionId)` recomputes deterministic rule output from Version, DevTask, TestCase, Bug, WorkActivity, and TaskWorklog relation rows, then upserts `xiaobao_risk_summaries` with `dirty=false`.
- `XiaobaoRiskWorker` registers the `xiaobao.summary.refresh` background job handler, so dirty summaries can be refreshed without opening `/xiaobao-warning`.
- 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 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 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.
High-growth business tables use fixed hash partitions:
- `requirements`: hash partitioned by `product_id`; primary key is `(id, product_id)`.
- `dev_tasks`, `test_cases`, and `bugs`: hash partitioned by `version_id`; primary key is `(id, version_id)`.
Append-only evidence and history tables use range partitions by `created_at`:
- `work_activities`
- `task_worklogs`
- `overtime_records`
- `xiaobao_risk_snapshots`
- `xiaobao_risk_insights`
- `ai_logs`
Partitioned tables must include the partition key in every primary key and business unique constraint. Cross-table references to partitioned tables use composite foreign keys when the referencing row naturally carries the partition key, for example `(requirement_id, requirement_product_id)` and `(test_case_id, test_case_version_id)`. Polymorphic activity references remain logical references.
Xiaobao uses two storage shapes: `xiaobao_risk_summaries` keeps one current row per version for fast reads, while `xiaobao_risk_snapshots` stores append-only history for trend analysis.
The AppData migration path is staged through a pure mapper plus preview service. `AppDataV22MigrationService.preview()` reads the allowed `app_data` keys, maps legacy JSON into partition-key-ready rows, and reports row counts plus skipped records before any insert path is enabled.
The first V2.2 read layer is query-first and AppData-compatible. `V22QueryModule` exposes scoped read APIs for version detail, requirement pool, workspace, and Xiaobao warning summaries:
- Version detail reads only the current version's requirements, plans, dev tasks, test cases, and bugs.
- Requirement pool queries require `productId`, so list/search/filter operations stay on the `requirements.product_id` partition key.
- Workspace reads only the current user's unfinished plans, dev tasks, test cases, and bugs.
- Xiaobao warning reads `xiaobao_risk_summaries` first, then maps the precomputed summary into the existing warning UI shape.
During the V2.2 compatibility window, writes still go through the existing AppData stores. The frontend consumes V2.2 relation-table results for render-heavy pages and falls back to AppData only when the V2.2 read is unavailable or empty. This is a migration bridge, not the target steady state.
## V2.3 AppData-to-Relational Write Sync Layer (2026-07-03)
V2.3 closes the first compatibility gap after V2.2: AppData remains the frontend write source only during the compatibility window, but successful `PUT /api/v1/data/:key` calls now trigger a backend relation-table sync.
`DataService` writes AppData with the existing optimistic-lock rules first. After the AppData write succeeds, it calls `AppDataV23SyncService.syncAfterAppDataPut(key)`. Sync failures are logged and do not fail the user save during this compatibility window. The next phase must replace AppData writes with domain CRUD writes so relation tables become the source of truth.
`AppDataV23SyncService` reuses the V2.2 pure mapper, then writes only the table family affected by the changed AppData key:
- `requirements` is replaced by `product_id` scope.
- `version_plans`, `dev_tasks`, `test_cases`, and `bugs` are replaced by `version_id` scope.
- `work_activities`, `task_worklogs`, `overtime_records`, `xiaobao_risk_snapshots`, and `xiaobao_risk_insights` remain append-oriented with duplicate skipping.
- `xiaobao_risk_summaries` is refreshed from risk snapshots and marked `dirty=true` when plans, tasks, test cases, bugs, activities, worklogs, or overtime change.
The server also has lightweight observability for this phase: a global API timing interceptor logs slow HTTP requests, and `PrismaService` logs slow query events. Thresholds are controlled by `API_SLOW_REQUEST_MS` and `PRISMA_SLOW_QUERY_MS`.
## Current Backend Migration Boundary (2026-07-08)
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 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. 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)
V2.7 adds enterprise collaboration capabilities on top of the relational source-of-truth direction. New collaboration data does not add AppData keys:
- `notifications`: per-recipient notification records with stable event types `assignment / mention / risk_alert / overdue_item`.
- `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; 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:
- `RbacService`: project role and global permission assertion adapter. Feature modules call this instead of hard-coding permission checks, while V2.5 `PermissionGuard` / `@ProtectedMutation()` continue to protect existing domain mutation controllers.
- `AuditService`: append-only collaboration/governance audit adapter. Feature modules call this instead of writing ad-hoc audit records; V2.5 `audit_events` remains the cross-domain mutation audit control plane.
When the later JWT/NextAuth server verification replaces the current header auth adapter, global permission sourcing should be swapped behind these auth/RBAC adapters; feature modules should keep depending on the adapter boundary.
Management overview reads only relation tables and summaries. It intentionally avoids AppData so it reflects the target backend boundary rather than the compatibility document store.
## V3 Business Analysis Agent Layer (planned)
Business Analysis Agent is a read-only AI analysis layer for business data conversations and context-page analysis. It does not mutate Product, Project, Version, Requirement, VersionPlan, DevTask, TestCase, Bug, Member, WorkActivity, TaskWorklog, Overtime, or Xiaobao data.
The analysis pipeline is:
```
User question + context
-> Analysis Planner
-> Semantic Layer
-> Permission Scope Resolver
-> Analysis Strategy
1. Template Strategy
2. Rule Composition Strategy (Deterministic)
3. AI Planning Strategy
-> Analysis Plan Processor (validate + normalize)
-> Metric Engine
-> Metric Result
-> ChartSpec Builder
-> Insight Engine
-> Report Builder
-> Follow-up Builder
-> UI Composition
```
Key boundaries:
- AI may propose an `AnalysisPlan`, but the system validates and normalizes it before execution.
- AI never writes SQL, never directly queries the database, and never widens user permissions.
- The Semantic Layer maps business language such as "忙", "压力", "风险", "延期", "效率", and "质量" to Metric Catalog definitions.
- Metric Catalog owns metric formula, owner, supported dimensions, supported analysis types, default time policy, and metric `version`. Formula changes require a metric version bump.
- Metric Engine returns renderer-agnostic `MetricResult`. Chart, insight, report, evidence, follow-ups, export, and future dashboard cards consume this result in parallel.
- Chart output uses a platform Unified ChartSpec. The first renderer is ECharts, but raw ECharts options are not the backend or Agent contract.
- Evidence is clickable structured proof with label, value, source domain, and optional drilldown filters.
- Reports have a fixed structure: Summary, Key Findings, Evidence, Suggestions, Data Scope.
The first product surfaces are `/wenfan-xiaobao` business-data conversation and product/project/version detail analysis entries. Visual design follows the AI Analysis Design System: Apple Vision style, large whitespace, large radius, light shadow, translucent material, restrained color, number-first cards, smooth line-area charts, rounded horizontal bars, and no BI big-screen styling.