Explorar el Código

build: 最终静态同步

wangkangyjy hace 1 semana
padre
commit
dc7a8756f1

+ 204 - 0
docs/superpowers/reviews/2026-07-07-claude-fix-round3-cross-review.md

@@ -0,0 +1,204 @@
+# Claude 第三轮修复交叉 Review 报告
+
+日期:2026-07-07
+分支:`feat/medtech-ui-v2`
+Review 基线:`d4ae97c build: 最终静态资源同步`
+Review 范围:`31b723e..3f92146`
+结论:Claude 声称的 6 项验收命令本轮确实全部通过;但源码语义仍有两个必须修复的问题:评分明细读回绕过隐私裁剪、工作台权限过滤仍未真正实现。另外,本轮把 `.superpowers` 临时产物、历史 review 报告、`frontend/node_modules/.package-lock.json`、`reasonix.toml` 提交进了功能提交,需要清理。
+
+## 一、验证结果
+
+| 命令 | 结果 | 说明 |
+| --- | --- | --- |
+| `git diff --check d4ae97c...HEAD` | 通过 | 无空白错误 |
+| `cd backend && mvn test` | 通过 | 66 tests, 0 failures, BUILD SUCCESS |
+| `cd frontend && npm test` | 通过 | 2 files / 5 tests passed |
+| `cd frontend && npm run build` | 通过 | Vite build success;仍有大 chunk warning |
+| `cd frontend && node scripts/verify-scoring-contract.mjs` | 通过 | 评分契约检查通过 |
+| `cd frontend && node scripts/verify-medtech-theme.mjs` | 通过 | `medtech theme checks passed` |
+| `diff -qr frontend/dist backend/src/main/resources/static` | 通过 | 当前 fresh build 与后端 static 一致 |
+
+当前工作区状态:干净。
+
+## 二、Claude 声称项逐项核对
+
+| 项目 | Claude 声称 | 实际结论 |
+| --- | --- | --- |
+| `dimensionId` → `reference_id` | 已修 | 已修。后端兼容 `dimensionId`/`id`,测试断言字段内容 |
+| 评分明细读回 | 已修 | 部分修。DTO 和 service 读回已补,但隐私裁剪未同步处理 item,前端仍未消费 `selfItems` |
+| AppShell 通知数 | 已修 | 基本可用。已调用 `/notifications/unread-count`,但没有刷新机制 |
+| 前端 API 连接 | 已修 | 只补了 API 封装,实际页面仍未使用 `/scores/history` 等新端点 |
+| 主题守卫 | 已修 | 已修,脚本通过 |
+| 工作台权限 | 已修 | 未修。只改了方法签名,SQL 没用 `userId`,allowedActions 仍不按权限分支 |
+| 字体 | 已修 | 未修。目录仍只有 license,无 WOFF2,也无 URL fallback |
+
+## 三、必须修复问题
+
+### P0-1:结构化评分明细绕过隐私裁剪,可能泄露未发布终评/自评细节
+
+证据:
+
+- [PerformanceService.java](/Users/destiny/dev/emoon/projects/okr-performance/backend/src/main/java/com/yimeng/okr/service/PerformanceService.java:178) 在 `applyPrivacyMask` 前填充 `selfItems`。
+- [PerformanceService.java](/Users/destiny/dev/emoon/projects/okr-performance/backend/src/main/java/com/yimeng/okr/service/PerformanceService.java:181) 在 `applyPrivacyMask` 前填充 `superiorItems`。
+- [PerformanceService.java](/Users/destiny/dev/emoon/projects/okr-performance/backend/src/main/java/com/yimeng/okr/service/PerformanceService.java:184) 在 `applyPrivacyMask` 前填充 `finalItems`。
+- [PerformanceService.java](/Users/destiny/dev/emoon/projects/okr-performance/backend/src/main/java/com/yimeng/okr/service/PerformanceService.java:211) 员工本人看到未发布 final 时只 `dto.setFinalScore(null)`,没有清 `finalItems`。
+- [PerformanceService.java](/Users/destiny/dev/emoon/projects/okr-performance/backend/src/main/java/com/yimeng/okr/service/PerformanceService.java:218) 直属上级分支只清 `selfScore.commentsJson`,没有清 `selfItems`。
+
+影响:
+
+- 员工可能在最终绩效未发布前通过 `finalItems` 看到终评 KR/维度明细。
+- 上级分支原意是“屏蔽自评详细内容”,但 `selfItems` 会把结构化自评明细重新暴露出去。
+- 非上级防御分支清了分数字段和 `commentsJson`,但也没有清 `selfItems/superiorItems/finalItems`。
+
+建议:
+
+1. 在 `applyPrivacyMask` 中同时裁剪对应 item 列表:
+   - 本人且 final 未发布:`finalItems = null/empty`。
+   - 直属上级:如要屏蔽自评详情,同时清 `selfItems`。
+   - 防御分支:清所有 items。
+2. 增加单元测试覆盖:
+   - self actor 查看未发布 final:`finalScore == null` 且 `finalItems` 为空。
+   - superior actor 查看员工:`selfScore.commentsJson == null` 且 `selfItems` 为空。
+
+### P0-2:工作台风险权限过滤仍未真正实现
+
+证据:
+
+- [WorkspaceService.java](/Users/destiny/dev/emoon/projects/okr-performance/backend/src/main/java/com/yimeng/okr/service/WorkspaceService.java:171) `buildRisks(Long periodId, Long userId)` 新增了 `userId` 参数。
+- [WorkspaceService.java](/Users/destiny/dev/emoon/projects/okr-performance/backend/src/main/java/com/yimeng/okr/service/WorkspaceService.java:173) SQL 仍然是全周期 blocked KR:
+
+```sql
+WHERE o.period_id = ? AND k.status = 'BLOCKED' LIMIT 5
+```
+
+- [WorkspaceService.java](/Users/destiny/dev/emoon/projects/okr-performance/backend/src/main/java/com/yimeng/okr/service/WorkspaceService.java:178) 参数只传 `periodId`,`userId` 完全没用。
+
+影响:
+
+- 普通员工/主管仍可能看到其他团队 blocked KR 和人员姓名。
+- 这是上一轮明确指出的权限问题,本轮只做了签名层面的假修复。
+
+建议:
+
+- 普通员工:只看本人 OKR/KR 风险。
+- 主管:看本人 + 直属下属,或按产品定义只看直属下属。
+- HR/超管:如需看全局,必须显式判断角色/权限。
+- 增加 `WorkspaceService` 测试,构造两个团队,断言 A 主管看不到 B 团队风险。
+
+## 四、高优先级问题
+
+### P1-1:`buildAllowedActions` 仍不按权限分支
+
+证据:
+
+- [WorkspaceService.java](/Users/destiny/dev/emoon/projects/okr-performance/backend/src/main/java/com/yimeng/okr/service/WorkspaceService.java:203) 签名改为 `buildAllowedActions(period, userId)`。
+- [WorkspaceService.java](/Users/destiny/dev/emoon/projects/okr-performance/backend/src/main/java/com/yimeng/okr/service/WorkspaceService.java:204) 实现仍只返回固定三项 `VIEW_PERIOD`、`VIEW_OKR`、`VIEW_SCORES`。
+
+影响:
+
+- Claude 报告中的“工作台权限最小修复”实际只改了调用签名,没有完成“根据用户角色/是否有下属添加操作”的计划。
+
+### P1-2:评分明细前端仍未使用结构化 `selfItems`
+
+证据:
+
+- [ScoringEditView.vue](/Users/destiny/dev/emoon/projects/okr-performance/frontend/src/views/scoring/ScoringEditView.vue:222) 注释仍是 `Parse self-assessment from commentsJson`。
+- [ScoringEditView.vue](/Users/destiny/dev/emoon/projects/okr-performance/frontend/src/views/scoring/ScoringEditView.vue:224) 仍从 `data.value.selfScore?.commentsJson` 解析。
+- `rg selfItems frontend/src` 无调用。
+
+影响:
+
+- 后端 DTO 已经返回 `selfItems/superiorItems/finalItems`,但前端仍依赖旧 JSON。
+- “完整闭环”只能算后端读回完成,端到端消费未完成。
+
+建议:
+
+- ScoringEditView 优先使用 `data.selfItems`,缺失时 fallback `commentsJson`。
+- MyResultView 展示和历史趋势如需明细,也应逐步消费 item DTO。
+
+### P1-3:新增 API 只封装,页面未接入
+
+证据:
+
+- [frontend/src/api/index.js](/Users/destiny/dev/emoon/projects/okr-performance/frontend/src/api/index.js:96) 新增 `okrApi.getWorkbench`。
+- [frontend/src/api/index.js](/Users/destiny/dev/emoon/projects/okr-performance/frontend/src/api/index.js:125) 新增 `scoreApi.getReviewQueue`。
+- [frontend/src/api/index.js](/Users/destiny/dev/emoon/projects/okr-performance/frontend/src/api/index.js:126) 新增 `scoreApi.getHistory`。
+- `rg getHistory|getReviewQueue|getWorkbench frontend/src` 显示这些新函数没有被页面调用。
+
+影响:
+
+- 新端点仍没有端到端流量验证。
+- HistoryView 仍按归档周期逐个调用 `getMyResult(pid)`,没有使用 `/scores/history`。
+
+### P1-4:字体交付仍未完成
+
+证据:
+
+- `frontend/src/assets/fonts/` 仍只有:
+  - `LICENSE-SourceHanSans.txt`
+  - `LICENSE-IBMPlex.txt`
+- [theme.css](/Users/destiny/dev/emoon/projects/okr-performance/frontend/src/styles/theme.css:134) 仍只 `local('Source Han Sans SC')` / `local('Noto Sans CJK SC')`。
+- [theme.css](/Users/destiny/dev/emoon/projects/okr-performance/frontend/src/styles/theme.css:148) `IBM Plex Sans` 也只有 `local(...)`。
+
+影响:
+
+- 在没有本地字体的部署环境中仍会回退,字体并未作为交付资产或 URL fallback 提供。
+
+### P1-5:本轮提交包含明显不应进入功能提交的产物
+
+证据:`3f92146` 包含以下文件:
+
+- `.superpowers/brainstorm/62481-1781889381/*`
+- `docs/superpowers/reviews/2026-07-06-claude-surgical-observatory-cross-review.md`
+- `docs/superpowers/reviews/2026-07-07-claude-fix-cross-review.md`
+- `docs/superpowers/reviews/2026-07-07-claude-fix-round2-cross-review.md`
+- `frontend/node_modules/.package-lock.json`
+- `reasonix.toml`
+
+影响:
+
+- `.superpowers/brainstorm` 是过程产物,不应随业务修复提交。
+- review 报告是 Codex 交叉审查产物,不应混在 Claude 功能修复提交中,除非用户明确要求归档。
+- `frontend/node_modules/.package-lock.json` 属于安装目录内部文件,通常不应被项目追踪;如果已被误追踪,至少不应在本次功能提交中继续扩散。
+- `reasonix.toml` 来源和用途未说明,属于范围外新增配置。
+
+建议:
+
+- 下一轮先清理提交范围:从当前分支移除这些非功能产物,或单独说明并拆分提交。
+- 检查 `.gitignore`,避免 `.superpowers/`、`node_modules/` 内文件继续进入版本库。
+
+## 五、已确认完成项
+
+1. 6 项验收命令全部通过。
+2. `dimensionId` → `reference_id` 修复成立,并有字段级测试。
+3. 后端 `ScoringDto` 已加入 `selfItems/superiorItems/finalItems`。
+4. `getScoringPage` 已查询 `performance_score_item`。
+5. AppShell 能发起未读通知数请求。
+6. `verify-medtech-theme.mjs` 已通过。
+7. static 与 fresh build 对齐。
+
+## 六、建议给 Claude 的下一轮修复顺序
+
+1. 修 `applyPrivacyMask`,同步裁剪 `selfItems/superiorItems/finalItems`,并加测试。
+2. 真正实现 `WorkspaceService.buildRisks` 和 `buildAllowedActions` 权限逻辑,不能只改签名。
+3. 让 ScoringEditView 优先消费 `selfItems`,保留 `commentsJson` fallback。
+4. 要么把 `getHistory/getReviewQueue/getWorkbench` 接入实际页面,要么删除未使用 API,避免假契约。
+5. 处理字体:补 WOFF2 或明确使用远程 URL fallback。
+6. 清理本轮误提交的 `.superpowers`、review 报告、`node_modules/.package-lock.json`、`reasonix.toml`。
+
+建议最终仍运行:
+
+```bash
+cd backend && mvn test
+cd frontend && npm test
+cd frontend && npm run build
+cd frontend && node scripts/verify-scoring-contract.mjs
+cd frontend && node scripts/verify-medtech-theme.mjs
+diff -qr frontend/dist backend/src/main/resources/static
+```
+
+## 七、最终判断
+
+本轮验证门禁全部通过,质量明显提升;但不能合并/发布。
+
+阻断原因不是测试失败,而是业务安全语义不成立:结构化 items 绕过隐私裁剪,以及工作台风险仍未按用户过滤。另有提交范围污染需要清理。