Files
WorkBuddy 438b38d121 V1.0.1 配置与规则纠偏:照片上限配置化+强校验 / 活体开关闭环 / 推荐阈值快照+规则版本化 / governance 三表+漂移审计
根治三类工程治理问题(技术默认值≠产品规则、配置存在但未闭环、文档代码漂移):
- PROFILE-001:profile.photo.maxCount(默认6未确认)+ ProfilePolicy + 服务器端 FOR UPDATE
  并发强校验(PHOTO_LIMIT_EXCEEDED,拒绝先于 S3 不留孤儿)+ 前端 mergeAppConfig 嵌套键修复
- CFG-001/IDENTITY-001:IdentityPolicy + GET /verification/liveness-status 真实判定点
  (默认关不阻断,开则 enhancedVerified 变化,消除假开关)+ 删死 seed livenessVerification.enabled
- MATCH-001:阈值60定为技术默认(消解 PENDING 矛盾)+ V050 recommendation_records.threshold_snapshot
  规则版本化 + 删死重复配置类 MatchConfig
- DOC-001:新增 docs/governance/(config-register/open-questions/decision-register/
  hardcoded-rule-audit/config-drift-audit)
- TEST-001:新增守卫 PhotoLimitGuardTest(8)/IdentityLivenessGuardTest(5)/
  RecommendationThresholdSnapshotGuardTest(4),均经负向注入可红

全量回归 733/0/0/3(基线717+17新增-1删死测试);UI 契约门禁 18/18;STATUS 升 rc.21
2026-09-21 02:05:15 +08:00

43 lines
4.3 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.
# 配置漂移审计 — config-drift-audit.md
> **版本**:V1.0.1 配置与规则纠偏 · **日期**:2026-09-20
> **判定原则**:同一业务含义只允许**一套 canonical key**;不得出现「seed 写一套、真实系统读另一套」「前端写一套、后端又一套」「文档说一套、代码做一套」。
---
## 一、`system_configs` 是**死表**(0 个 Java 读取点)
- `V001__init_base_tables.sql:131` 建表,`:213-217` seed 了 `match_rule_version / daily_recommendation_limit / match_score_threshold='60' / recommendation_cooldown_days`。
- `demo-seed-final.sql` 又 seed 一批 flag 名(`localEligibility.evidencePolicy`、`counseling.enabled`、`premarital.enabled`、`serviceLocation.enabled`、`content.enabled`、`localEligibility.enabled`、`banner.maxActiveCount`)。
- **全仓 `backend/src/main/java` 无任何 `SystemConfig` 实体 / Mapper / 读取**(仅 `EntityMigrationParityTest` 把它列入 H2 白名单)。
- **后果**:真实系统读的是 `MatchRuleConfigProperties`(yml `match.*`)+ `FeatureFlagConfig`(`features.*` / `feature_flag_overrides`),**不是** `system_configs`。于是 `match_score_threshold='60'` 在死表里躺著,与活键 `match.default-score-threshold` 各写一份 → **seed 与 migration 漂移、seed 与代码漂移**。
- **处置**:本轮删除 `demo-seed-final.sql` 的死 key `livenessVerification.enabled`(与活键 `livenessEnabled` 同义双 key)。其余 `system_configs` 死行**未删**(表整体退役需单独迁移裁定,避免越界)——登记为未来项:整体退役 `system_configs` 或把它接成真实配置源。
## 二、`match_rule_configs` 是**未接线的规则版本化雏形**(死表)
- `V006` 建 `match_rule_configs`(version/status/10 权重/`minimum_score DEFAULT 60`),`MatchRuleConfigMapper.findActive/findByVersion` **无任何调用方**。
- 真实规则版本化本轮走轻量路线:`recommendation_records.rule_version` + 新列 `threshold_snapshot`(V050)+ `match.rule-version`。
- **处置**:登记为未来项 —— 要么把 `match_rule_configs` 接成「规则版本唯一读取源」(配合 open-questions 的 10 维权重配置化),要么删除该死表。本轮未动(避免越界)。
## 三、本轮已消除的 key/类漂移
| 漂移 | 处置 |
|------|------|
| seed `livenessVerification.enabled` vs 活键 `livenessEnabled`(双 key 同义) | 已删 seed 行;canonical `identity.liveness.enabled` 映射到唯一实现键 `livenessEnabled`(见 config-register)。 |
| `shared/config/MatchConfig` 与 `match/config/MatchRuleConfigProperties` 同前缀 `match` 双 bean(前者 0 业务引用) | 已删死类 `MatchConfig`,统一读 `MatchRuleConfigProperties`。 |
| 前端 `mergeAppConfig` 平铺合并 → 服务端嵌套 `businessLimits.maxProfilePhotos` 等**从不生效**(前端 6 写死、后端也不强校验) | 已修前端显式节-键映射 + 后端 `ProfilePolicy` 强校验 + `/app/config` 读 `ProfilePolicy`。前后端现共用同一配置源。 |
| 活体开关「假开关」(`livenessEnabled` 无业务强制点,0 读取点) | 已建 `IdentityPolicy` + `GET /verification/liveness-status` 真实判定点,开关开则 `enhancedVerified` 变化(FF-LIVENESS-002)。 |
## 四、剩余待登记漂移(未来项,勿本轮扩大)
| 项 | 说明 |
|----|------|
| `AppController` 剩余硬编码(dailyRecommendationLimit / maxInterestTags / selfIntroMaxLength / recommendationCooldownDays / reviewConfig / features) | 与 `MatchRuleConfigProperties` / 各配置源各写一份;待统一为「读配置源 + 经 `/app/config` 下发」(见 hardcoded-rule-audit 第二节)。 |
| 前端 `BUSINESS_LIMITS` 本地默认值 vs 服务端 | `maxProfilePhotos` 已打通;`DAILY_RECOMMENDATION_LIMIT / MAX_INTEREST_TAGS` 等仍为本地 fallback,服务端值经 `/app/config` 下发后应全部以服务端为准(前端只作失败兜底)。 |
| 打分公式占位(0.6/0.4、80/60、恒 50.0) | 非漂移,属 V1 占位未产品化;正式化时随规则版本一起配置化。 |
---
*结论:本轮消除了文档点名的 4 类漂移(liveness 双 key、MatchConfig 重复、前后端照片上限失同步、活体假开关)。
`system_configs` 死表、`match_rule_configs` 死表、AppController 剩余硬编码为已登记的未来项。*