oh-my-muse/docs/agent-specs/2026-06-21-meta-projection投影实现-review.md
lili d8cb1047da docs(p1r): meta-projection 投影实现评审版 + 缺环回写总账[P1]
- 新增评审版:projectionKey≡schemaKey、列表=work targetType 匹配多 schema(人类第四轮+评审走查决策)、RealContentMetaFacade 置 content-server 调 meta-api+回填本域 planning value、可见性起步版本级;含 design-docs SSOT 约束、关键决策表、边界数据流、blast/风险/验收/open items。

- execution 总账回写 B5c 前端完成 + projection 缺环发现(ContentMetaFacade 真后端 unavailable,e2e 实为真后端活体)+ 第四轮决策。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 04:24:03 -07:00

123 lines
9.2 KiB
Markdown

# Meta-Projection 投影实现(补 B 缺环)—— 评审版
> 版本:v0.1(评审版) · 日期:2026-06-21 · 目标读者:架构 review + 实现 agent · 类型:评审版(结论先行,无代码细节)
> 上游:[用量投影执行版](2026-06-20-meta-schema用量投影-execution.md)第四轮决策(补 Meta owner projection 实现 + 完整投影分组建模)。
> 触发:B5 前端 PlanningEditor 依赖的 meta-projection 端口真后端 unavailable(`ContentMetaFacade` 仅 stub),B 落地链漏列了「Meta owner 投影实现」这一跨 BC 环。
---
## 一、结论先行
1. **目标**:让 `GET /works/{workId}/meta-projections[/{projectionKey}]` 从 stub-unavailable 变为**真实返回字段定义 + 字段级可见性 + 已存值回填**,使 B5 PlanningEditor 端到端真跑(用户按 schema 字段填值→usedFieldKeys→快照→字段级计数)。
2. **推荐方案(一句话)**:**projectionKey ≡ schemaKey**(对齐 design-docs);`RealContentMetaFacade`**content-server**,调新增 meta-api 端口拿「字段定义+字段级可见性」,读本域 planning 回填 value,组装投影;**起步以 work 当前绑定的单 schema 为投影列表,预留多 schema 演进**。
3. **关键依据(design-docs SSOT 已定义,实现须遵守)**:
- 投影是 **read model**(MetaSchema+权限+来源状态+dataRevision 动态计算),**owner = MetaSchema BC**,绝非事实源(后端-05 §4.3、架构-02 §4.2)。
- **projectionKey 倾向 = schema_key**(domain/scope/target_type 下唯一稳定业务键,后端-04 §4.7)。
- 5 可见性(uiVisible/aiContext/userEditable/userSearchable/exportable)= **策略级**,由 `muse_meta_visibility_policy` 管,语义不可合并(架构-02 §9)。
- 三版本 schemaVersion/projectionVersion/dataRevision 语义与递增条件已定(前端-03 §5.3、后端-05 §4.3)。
- 字段写入走 writeOwner/ownerCommand(如 planning:`PUT /works/{workId}/planning/{sectionKey}`),无跨 owner 通用写口。
4. **架构空白(design-docs 未明确,本评审拍板点见 §四/§九)**:① work↔多 schema 关联规则;② 字段↔投影分组存储映射;③ 多分组返回契约;④ 可见性字段级 vs 版本级粒度。
---
## 二、背景与缺环
- B5 前端(已完成):`usePlanning` hooks + `PlanningEditor`(按 fieldType 渲染) + `PlanningEditorPanel`(列 projection→选→编辑,unavailable 优雅降级) + WorkspacePage 右侧 Tab 集成。vitest 全量 66/66。
- 缺环(自验反假绿):`ContentMetaProjectionServiceImpl` 转调 `ContentMetaFacade`,但全仓仅 `UnavailableContentMetaFacade`(`@ConditionalOnMissingBean`)→ 真后端返 `CONTENT_EXTERNAL_OWNER_UNAVAILABLE`。playwright e2e 是真后端活体(`VITE_API_MOCK=false`),故 UI 全链路真跑必须先让 projection 真实可用。
- meta-api 现状:`MetaSchemaQueryApi``listActiveSchemasByTargetType` / `getActiveSchemaById`(返 brief:id/schemaKey/displayName/targetType),**无字段定义、无可见性、无投影分组查询**。
---
## 三、范围与非目标
**范围**
- 新增 meta-api 投影查询端口(字段定义 + 字段级可见性聚合)。
- meta-server 实现该端口(只读 active schema 的 `MetaFieldDO` + `MetaVisibilityPolicyDO`)。
- content-server `RealContentMetaFacade`(替换 Unavailable):调 meta-api + 读本域 planning 回填 value + 组装 `MetaProjectionSummary/Detail/FieldRespVO`
- DynamicFieldValidation 真实化(校验字段类型/必填/枚举,返路由建议;不写事实)。
**非目标(本轮)**
- 不引入 schema **内**字段分组子表(projectionKey≡schemaKey,schema 即投影单元;多 projection = 多 schema)。
- 不解决 work↔多 schema 的完整关联建模(起步用 work 单绑定;演进见 §九)。
- 不改 3 版本递增的既有产出点(沿用现有 schema/projection version 字段)。
---
## 四、关键决策(拍板点)
| # | 决策 | 选项 | 推荐 | 理由 |
|---|---|---|---|---|
| D-A | projectionKey 语义 | (a) =schemaKey;(b) schema 内分组键(需新表) | **(a)** | design-docs 后端-04 §4.7 schema_key 为唯一业务键;前端-02 的「作品设定/世界设定/角色关系…」对应**不同 domain/scope 的 schema**,非单 schema 内分组→无须新分组表 |
| D-B | work 的 projection 列表来源 | (a) work 单绑定 schema(1 个);(b) 按 targetType 匹配多 active schema;(c) work↔schema 多对多新表 | **(a) 起步,预留 (b)** | D1 已落 `workSchemaId` 单绑定,最小一致、零新建;(b)/(c) 涉 design-docs 空白(work 适用哪些 schema),留演进 |
| D-C | 字段级可见性粒度 | (a) 解析 policySnapshot JSON 取字段级;(b) 版本级统一应用到所有字段 | **(a) 若快照含字段级,否则 (b)** | 执行版 §四.3 实证字段级可见性在 `policySnapshot`/`fieldContractSnapshot` JSON;须实现期验证快照结构,不满足则降级 (b) 并声明 |
| D-D | RealContentMetaFacade 放置 | (a) content-server;(b) meta-server | **(a)** | value 回填须读 content 本域 planning;content 调 meta-api 拿定义合边界(参照 `RealContentFileFacade` 先例);meta 读 content 反向违边界 |
| D-E | meta-api 端口形态 | (a) 扩 `MetaSchemaQueryApi`;(b) 新增 `MetaProjectionQueryApi` | **(b)** | 投影查询(字段+可见性)与 schema 发现职责不同,独立端口更清晰、不污染现有契约 |
---
## 五、推荐方案数据流
```mermaid
flowchart TD
FE[muse-studio PlanningEditorPanel] -->|GET /works/workId/meta-projections| C1[AppContentMetaProjectionController]
C1 --> C2[ContentMetaProjectionServiceImpl\nrequireOwnedWork 权限校验]
C2 --> F[RealContentMetaFacade\ncontent-server, @Component @Primary]
F -->|work.workSchemaId| W[(本域 WorkDO)]
F -->|projectionKey=schemaKey 拿字段+可见性| API[MetaProjectionQueryApi\nmeta-api 端口 新增]
API --> M[meta-server 实现\n读 MetaFieldDO+MetaVisibilityPolicyDO active]
F -->|按 fieldKey 回填 value| P[(本域 PlanningSectionDO.contentPayload)]
F --> R[组装 MetaProjectionDetailRespVO\n字段定义+可见性+value+sourceSnapshot]
R --> C2
C2 -->|available=false 即 fail-closed| X[CONTENT_EXTERNAL_OWNER_UNAVAILABLE]
subgraph 边界门 BcBoundaryArchTest 0 违例
API
end
F -. 仅依赖 meta-api 端口,不直连 meta.dal .-> API
```
**职责切分(合边界)**:Meta 出「字段定义+可见性」(自域只读);Content 出「权限校验 + value 回填 + 投影组装」(本域只读 + 调 meta-api 端口)。
---
## 六、Blast Radius / 兼容 / 回滚
- **meta-api**:新增 `MetaProjectionQueryApi` + DTO(只增,无既有契约改动)。
- **meta-server**:新增端口实现(只读 schema/可见性,不写)。
- **content-server**:新增 `RealContentMetaFacade` `@Component @Primary` 替换 stub;`ContentMetaProjectionServiceImpl`/Controller 不改(已转调 facade)。
- **边界门**:content 仅依赖 meta-api 端口 + 本域 DAL;`BcBoundaryArchTest` 须保持 0 违例(验收硬条件)。
- **兼容**:projection 端点对外契约不变(VO 不动);仅从「恒 unavailable」变「真实返回」。
- **回滚**:移除 `RealContentMetaFacade``@Primary` 即回到 Unavailable stub(平滑)。
---
## 七、风险与缓解
| 风险 | 说明 | 缓解 |
|---|---|---|
| projectionKey=schemaKey 系推断 | design-docs 未逐字明说「projectionKey==schema_key」 | §九 列为人类确认点;若否,改 D-A(b) 需新分组表(范围扩大) |
| work↔多 schema 未定 | 前端-02 列 5 入口但 work 单绑定 | 起步单 schema(D-B a);多 schema 留演进,不阻塞 B5 |
| 可见性粒度不定 | policySnapshot 结构待实现期验证 | D-C:验证后字段级,否则版本级降级并声明 |
| 真后端无 active schema | 本地 fixture 可能无 governance 配置的 schema | meta-projections 空列表→前端已有优雅降级(不崩);e2e 需 fixture 含 active schema(globalSetup) |
| fail-closed 语义 | 任一 meta-api 失败/数据缺失 | facade 一律返 unavailable,绝不伪造部分投影(anti-false-green) |
---
## 八、验收标准
- meta-api `MetaProjectionQueryApi` 端口 + meta-server 实现:种 active schema → 返回字段定义 + 可见性(单测/IT)。
- `RealContentMetaFacade`:work 绑 schema → meta-projections 真实返回字段(非 unavailable);value 从 planning 回填正确(单测)。
- 边界:`BcBoundaryArchTest` 0 违例;恰一个 `ContentMetaFacade` Bean。
- 端到端(真后端活体):B5 e2e——进作品规划 Tab → 字段渲染 → 填值保存 → usedFieldKeys 落快照 → 值回填(playwright,需 fixture 含 active schema)。
- 反假绿:无 active schema/数据缺失一律 unavailable+前端降级,绝不伪造字段。
---
## 九、Open Items(须人类确认)
1. **(拍板)projectionKey ≡ schemaKey?** 若确认→无须新分组表(推荐);若 projectionKey 是 schema 内分组→须新建字段↔分组映射(范围显著扩大)。
2. **(拍板)work 的 projection 列表起步用单绑定 schema?** 还是本轮即做 targetType 匹配多 schema(需定「work 适用哪些 schema」规则)?
3. 可见性字段级 vs 版本级:实现期验证 policySnapshot 结构后定(D-C 已含降级)。
4. DynamicFieldValidation 真实校验范围:本轮做类型/必填/枚举 + 路由建议是否足够?
> 下一步:本评审走查确认 §九.1/§九.2 两个拍板点后,出执行版 spec(端口契约 + 数据流 + 边界验收 + 分步)并实现。