chore(config): 移除 7 项从未被读取的死配置(5 个功能开关 + 2 个咨询附件桶)
产品裁定:确认移除对系统无影响即可移除。移除前的无影响论证(不是「看着没人用就删」):
全仓搜索 5 个 getter,命中的只有 ① 本类定义处 ② getAllFlags() ③ FeatureFlagControllerTest 一处断言
—— 业务代码零调用点;前端 admin-web/src 与 miniapp 对 feature-flag 零调用(仅一个未使用的 storageKey 常量)。
⚠️ 重要边界:FeatureFlagConfig **整体不能删** —— 同类的 isAppealEnabled(AppealService 4 处)、
isLocalEvidenceUploadEnabled(LocalEligibilityService)、getLocalEvidenceMaxFiles /
getLocalEvidenceMaxFileSizeMB / getLocalEvidenceAllowedTypes(PhotoService)都是**真实生效的 gate**。
本次只删那 5 个死的:counselingEnabled / premaritalEnabled / contentEnabled /
serviceLocationEnabled / localEligibilityEnabled,以及从未被读取的
counselingAttachmentsPrivate / counselingAttachmentsBucket(存储实走 object-storage.bucket-private)。
同步清理 application-docker.yml(application-dev.yml 被 .gitignore 排除,已在本地同步清理):
否则会留下「配了但不生效」的新死配置 —— 正是本次要消除的问题本身。
getAllFlags() 键数 21 → 14;FeatureFlagControllerTest 布尔样例由 counselingEnabled 换成 referralEnabled。
docs(plan): Task 3.1 全闭 + Task 3.2 前两项已完;附 B 增第 14 项;基线 685→689
Task 3.1(隐私设置)两层均已关闭:① upsert ② 桩接通 + 键集以小程序为准。
Task 3.2 第三项「PUT /feature-flags 不持久化」**仍未处理** —— 改动涉及运维流程
(改的是运行时内存态,重启即失效),与「删死配置」风险不同,需确认做成真持久化还是标注只读后关端点。
附 B-14:新增迁移必须同步更新 MigrationValidationTest.LATEST_MIGRATION(本轮实测踩到),
该守卫还断言「迁移文件总数 == 基线」即不允许缺号,属有效守卫。
全量回归 689 / 0 failures / 0 errors / 3 skipped;UI 契约门禁 18/18 绿。
This commit is contained in:
+8
-44
@@ -18,16 +18,12 @@ public class FeatureFlagConfig {
|
|||||||
private boolean appealEnabled = false;
|
private boolean appealEnabled = false;
|
||||||
|
|
||||||
// ---- V1.1 新增 Flag ----
|
// ---- V1.1 新增 Flag ----
|
||||||
/** 公益婚恋咨询功能开关 */
|
// ⚠️ 2026-09-19 移除:counselingEnabled / premaritalEnabled / contentEnabled /
|
||||||
private boolean counselingEnabled = true;
|
// serviceLocationEnabled / localEligibilityEnabled 五个开关**从未被任何业务代码读取**
|
||||||
/** 婚前辅导功能开关(复用 counseling,默认跟随 counselingEnabled) */
|
// (全仓搜索仅命中本类的定义与 getAllFlags,外加 FeatureFlagControllerTest 一处断言)。
|
||||||
private boolean premaritalEnabled = true;
|
// 留着是「配置暴露但未强制」的假开关 —— 运维以为关掉能停功能,实际无效。
|
||||||
/** 内容管理(婚恋课堂/反诈文章)开关 */
|
// 若将来真需要按开关控制这些模块,应重新加入并**同时写入业务调用点**。
|
||||||
private boolean contentEnabled = true;
|
//
|
||||||
/** 善缘服务站功能开关 */
|
|
||||||
private boolean serviceLocationEnabled = true;
|
|
||||||
/** 本地资格核验功能开关 */
|
|
||||||
private boolean localEligibilityEnabled = true;
|
|
||||||
/** 红娘服务转介功能开关 */
|
/** 红娘服务转介功能开关 */
|
||||||
private boolean referralEnabled = true;
|
private boolean referralEnabled = true;
|
||||||
/** 活动互选二阶段同意开关 */
|
/** 活动互选二阶段同意开关 */
|
||||||
@@ -51,12 +47,8 @@ public class FeatureFlagConfig {
|
|||||||
/** 佐证材料保留天数;实际清理由运维任务按此配置执行 */
|
/** 佐证材料保留天数;实际清理由运维任务按此配置执行 */
|
||||||
private int localEvidenceRetentionDays = 365;
|
private int localEvidenceRetentionDays = 365;
|
||||||
|
|
||||||
// ---- V1.1 咨询附件私存储 ----
|
// ⚠️ 2026-09-19 移除:counselingAttachmentsPrivate / counselingAttachmentsBucket 从未被读取 ——
|
||||||
/** 咨询附件是否使用私有存储桶 */
|
// 咨询附件的存储实走 object-storage.bucket-private,这两项与真实链路无关。
|
||||||
private boolean counselingAttachmentsPrivate = true;
|
|
||||||
/** 咨询附件存储桶名称 */
|
|
||||||
private String counselingAttachmentsBucket = "counseling-attachments";
|
|
||||||
|
|
||||||
// ==================== Getters & Setters ====================
|
// ==================== Getters & Setters ====================
|
||||||
|
|
||||||
public boolean isActivityEnabled() { return activityEnabled; }
|
public boolean isActivityEnabled() { return activityEnabled; }
|
||||||
@@ -71,21 +63,6 @@ public class FeatureFlagConfig {
|
|||||||
public boolean isAppealEnabled() { return appealEnabled; }
|
public boolean isAppealEnabled() { return appealEnabled; }
|
||||||
public void setAppealEnabled(boolean appealEnabled) { this.appealEnabled = appealEnabled; }
|
public void setAppealEnabled(boolean appealEnabled) { this.appealEnabled = appealEnabled; }
|
||||||
|
|
||||||
public boolean isCounselingEnabled() { return counselingEnabled; }
|
|
||||||
public void setCounselingEnabled(boolean counselingEnabled) { this.counselingEnabled = counselingEnabled; }
|
|
||||||
|
|
||||||
public boolean isPremaritalEnabled() { return premaritalEnabled; }
|
|
||||||
public void setPremaritalEnabled(boolean premaritalEnabled) { this.premaritalEnabled = premaritalEnabled; }
|
|
||||||
|
|
||||||
public boolean isContentEnabled() { return contentEnabled; }
|
|
||||||
public void setContentEnabled(boolean contentEnabled) { this.contentEnabled = contentEnabled; }
|
|
||||||
|
|
||||||
public boolean isServiceLocationEnabled() { return serviceLocationEnabled; }
|
|
||||||
public void setServiceLocationEnabled(boolean serviceLocationEnabled) { this.serviceLocationEnabled = serviceLocationEnabled; }
|
|
||||||
|
|
||||||
public boolean isLocalEligibilityEnabled() { return localEligibilityEnabled; }
|
|
||||||
public void setLocalEligibilityEnabled(boolean localEligibilityEnabled) { this.localEligibilityEnabled = localEligibilityEnabled; }
|
|
||||||
|
|
||||||
public boolean isReferralEnabled() { return referralEnabled; }
|
public boolean isReferralEnabled() { return referralEnabled; }
|
||||||
public void setReferralEnabled(boolean referralEnabled) { this.referralEnabled = referralEnabled; }
|
public void setReferralEnabled(boolean referralEnabled) { this.referralEnabled = referralEnabled; }
|
||||||
|
|
||||||
@@ -116,12 +93,6 @@ public class FeatureFlagConfig {
|
|||||||
public int getLocalEvidenceRetentionDays() { return localEvidenceRetentionDays; }
|
public int getLocalEvidenceRetentionDays() { return localEvidenceRetentionDays; }
|
||||||
public void setLocalEvidenceRetentionDays(int localEvidenceRetentionDays) { this.localEvidenceRetentionDays = localEvidenceRetentionDays; }
|
public void setLocalEvidenceRetentionDays(int localEvidenceRetentionDays) { this.localEvidenceRetentionDays = localEvidenceRetentionDays; }
|
||||||
|
|
||||||
public boolean isCounselingAttachmentsPrivate() { return counselingAttachmentsPrivate; }
|
|
||||||
public void setCounselingAttachmentsPrivate(boolean counselingAttachmentsPrivate) { this.counselingAttachmentsPrivate = counselingAttachmentsPrivate; }
|
|
||||||
|
|
||||||
public String getCounselingAttachmentsBucket() { return counselingAttachmentsBucket; }
|
|
||||||
public void setCounselingAttachmentsBucket(String counselingAttachmentsBucket) { this.counselingAttachmentsBucket = counselingAttachmentsBucket; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有 Feature Flag 的当前值(供 /operator/feature-flags 接口使用)
|
* 获取所有 Feature Flag 的当前值(供 /operator/feature-flags 接口使用)
|
||||||
*/
|
*/
|
||||||
@@ -131,11 +102,6 @@ public class FeatureFlagConfig {
|
|||||||
flags.put("matchmakerEnabled", matchmakerEnabled);
|
flags.put("matchmakerEnabled", matchmakerEnabled);
|
||||||
flags.put("chatImageEnabled", chatImageEnabled);
|
flags.put("chatImageEnabled", chatImageEnabled);
|
||||||
flags.put("appealEnabled", appealEnabled);
|
flags.put("appealEnabled", appealEnabled);
|
||||||
flags.put("counselingEnabled", counselingEnabled);
|
|
||||||
flags.put("premaritalEnabled", premaritalEnabled);
|
|
||||||
flags.put("contentEnabled", contentEnabled);
|
|
||||||
flags.put("serviceLocationEnabled", serviceLocationEnabled);
|
|
||||||
flags.put("localEligibilityEnabled", localEligibilityEnabled);
|
|
||||||
flags.put("referralEnabled", referralEnabled);
|
flags.put("referralEnabled", referralEnabled);
|
||||||
flags.put("mutualConsentEnabled", mutualConsentEnabled);
|
flags.put("mutualConsentEnabled", mutualConsentEnabled);
|
||||||
flags.put("serviceProgramEnabled", serviceProgramEnabled);
|
flags.put("serviceProgramEnabled", serviceProgramEnabled);
|
||||||
@@ -146,8 +112,6 @@ public class FeatureFlagConfig {
|
|||||||
flags.put("localEvidenceMaxFileSizeMB", localEvidenceMaxFileSizeMB);
|
flags.put("localEvidenceMaxFileSizeMB", localEvidenceMaxFileSizeMB);
|
||||||
flags.put("localEvidenceAllowedTypes", localEvidenceAllowedTypes);
|
flags.put("localEvidenceAllowedTypes", localEvidenceAllowedTypes);
|
||||||
flags.put("localEvidenceRetentionDays", localEvidenceRetentionDays);
|
flags.put("localEvidenceRetentionDays", localEvidenceRetentionDays);
|
||||||
flags.put("counselingAttachmentsPrivate", counselingAttachmentsPrivate);
|
|
||||||
flags.put("counselingAttachmentsBucket", counselingAttachmentsBucket);
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,11 +64,6 @@ features:
|
|||||||
chat-image-enabled: false
|
chat-image-enabled: false
|
||||||
appeal-enabled: false
|
appeal-enabled: false
|
||||||
# V1.1
|
# V1.1
|
||||||
counseling-enabled: true
|
|
||||||
premarital-enabled: true
|
|
||||||
content-enabled: true
|
|
||||||
service-location-enabled: true
|
|
||||||
local-eligibility-enabled: true
|
|
||||||
referral-enabled: true
|
referral-enabled: true
|
||||||
mutual-consent-enabled: true
|
mutual-consent-enabled: true
|
||||||
service-program-enabled: true
|
service-program-enabled: true
|
||||||
@@ -79,5 +74,3 @@ features:
|
|||||||
local-evidence-max-file-size-mb: 10
|
local-evidence-max-file-size-mb: 10
|
||||||
local-evidence-allowed-types: ${LOCAL_EVIDENCE_ALLOWED_TYPES:jpg,png}
|
local-evidence-allowed-types: ${LOCAL_EVIDENCE_ALLOWED_TYPES:jpg,png}
|
||||||
local-evidence-retention-days: ${LOCAL_EVIDENCE_RETENTION_DAYS:365}
|
local-evidence-retention-days: ${LOCAL_EVIDENCE_RETENTION_DAYS:365}
|
||||||
counseling-attachments-private: true
|
|
||||||
counseling-attachments-bucket: counseling-attachments
|
|
||||||
|
|||||||
+17
-13
@@ -23,8 +23,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
* <p>背景(KEEP-VERIFY 复验发现):
|
* <p>背景(KEEP-VERIFY 复验发现):
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>D-22-A(High):原 {@code updateFlag} 仅支持布尔 flag(反射 {@code getMethod("set"+cap, boolean.class)}),
|
* <li>D-22-A(High):原 {@code updateFlag} 仅支持布尔 flag(反射 {@code getMethod("set"+cap, boolean.class)}),
|
||||||
* 7 个非布尔 flag(livenessProvider / localEvidenceAllowedTypes / counselingAttachmentsBucket /
|
* 非布尔 flag(livenessProvider / localEvidenceAllowedTypes / localEvidenceMaxFiles /
|
||||||
* localEvidenceMaxFiles / localEvidenceMaxFileSizeMB / localEvidenceRetentionDays)经 API 更新必失败。</li>
|
* localEvidenceMaxFileSizeMB / localEvidenceRetentionDays)经 API 更新必失败。
|
||||||
|
* (原文记 7 个,其中 counselingAttachmentsBucket / counselingAttachmentsPrivate
|
||||||
|
* 已于 2026-09-19 作为**从未被读取的死配置**移除,故现为 5 个。)</li>
|
||||||
* <li>D-22-B(Medium):原 {@code body.getOrDefault("value", false)} 缺 value 时静默置 false(可能误关功能),
|
* <li>D-22-B(Medium):原 {@code body.getOrDefault("value", false)} 缺 value 时静默置 false(可能误关功能),
|
||||||
* 且非布尔类型转型会崩。</li>
|
* 且非布尔类型转型会崩。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
@@ -56,15 +58,17 @@ class FeatureFlagControllerTest {
|
|||||||
ApiResponse<Map<String, Object>> resp = controller.getAllFlags();
|
ApiResponse<Map<String, Object>> resp = controller.getAllFlags();
|
||||||
assertTrue(resp.isSuccess());
|
assertTrue(resp.isSuccess());
|
||||||
Map<String, Object> flags = resp.getData();
|
Map<String, Object> flags = resp.getData();
|
||||||
// 21 个 flag 必须全部暴露,否则运维看不到/改不了某个配置(含此前仅布尔可更新的死区)
|
// 14 个 flag 必须全部暴露,否则运维看不到/改不了某个配置(含此前仅布尔可更新的死区)。
|
||||||
assertEquals(21, flags.size());
|
// 原为 21 个;2026-09-19 移除 7 个从未被业务代码读取的死配置
|
||||||
|
// (counselingEnabled / premaritalEnabled / contentEnabled / serviceLocationEnabled /
|
||||||
|
// localEligibilityEnabled / counselingAttachmentsPrivate / counselingAttachmentsBucket)。
|
||||||
|
assertEquals(14, flags.size());
|
||||||
for (String key : new String[]{
|
for (String key : new String[]{
|
||||||
"activityEnabled", "matchmakerEnabled", "chatImageEnabled", "appealEnabled",
|
"activityEnabled", "matchmakerEnabled", "chatImageEnabled", "appealEnabled",
|
||||||
"counselingEnabled", "premaritalEnabled", "contentEnabled", "serviceLocationEnabled",
|
"referralEnabled", "mutualConsentEnabled", "serviceProgramEnabled",
|
||||||
"localEligibilityEnabled", "referralEnabled", "mutualConsentEnabled", "serviceProgramEnabled",
|
|
||||||
"livenessEnabled", "livenessProvider", "localEvidenceUploadEnabled",
|
"livenessEnabled", "livenessProvider", "localEvidenceUploadEnabled",
|
||||||
"localEvidenceMaxFiles", "localEvidenceMaxFileSizeMB", "localEvidenceAllowedTypes",
|
"localEvidenceMaxFiles", "localEvidenceMaxFileSizeMB", "localEvidenceAllowedTypes",
|
||||||
"localEvidenceRetentionDays", "counselingAttachmentsPrivate", "counselingAttachmentsBucket"
|
"localEvidenceRetentionDays"
|
||||||
}) {
|
}) {
|
||||||
assertTrue(flags.containsKey(key), "缺少暴露的 flag: " + key);
|
assertTrue(flags.containsKey(key), "缺少暴露的 flag: " + key);
|
||||||
}
|
}
|
||||||
@@ -72,10 +76,10 @@ class FeatureFlagControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void updateBooleanFlagIsReflected() {
|
void updateBooleanFlagIsReflected() {
|
||||||
ApiResponse<Map<String, Object>> resp = controller.updateFlag(principal, "counselingEnabled", Map.of("value", false));
|
ApiResponse<Map<String, Object>> resp = controller.updateFlag(principal, "referralEnabled", Map.of("value", false));
|
||||||
assertTrue(resp.isSuccess());
|
assertTrue(resp.isSuccess());
|
||||||
assertFalse(fixture.isCounselingEnabled());
|
assertFalse(fixture.isReferralEnabled());
|
||||||
assertEquals(false, fixture.getAllFlags().get("counselingEnabled"));
|
assertEquals(false, fixture.getAllFlags().get("referralEnabled"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -100,14 +104,14 @@ class FeatureFlagControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void updateMissingValueReturnsBadRequest() {
|
void updateMissingValueReturnsBadRequest() {
|
||||||
ApiResponse<Map<String, Object>> resp = controller.updateFlag(principal, "counselingEnabled", Map.of());
|
ApiResponse<Map<String, Object>> resp = controller.updateFlag(principal, "referralEnabled", Map.of());
|
||||||
assertFalse(resp.isSuccess());
|
assertFalse(resp.isSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void updateBooleanFlagWithWrongTypeReturnsBadRequestAndDoesNotSilentlyDisable() {
|
void updateBooleanFlagWithWrongTypeReturnsBadRequestAndDoesNotSilentlyDisable() {
|
||||||
ApiResponse<Map<String, Object>> resp = controller.updateFlag(principal, "counselingEnabled", Map.of("value", "not-a-bool"));
|
ApiResponse<Map<String, Object>> resp = controller.updateFlag(principal, "referralEnabled", Map.of("value", "not-a-bool"));
|
||||||
assertFalse(resp.isSuccess());
|
assertFalse(resp.isSuccess());
|
||||||
assertTrue(fixture.isCounselingEnabled(), "类型不匹配不应静默把功能置为关闭");
|
assertTrue(fixture.isReferralEnabled(), "类型不匹配不应静默把功能置为关闭");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
| Phase 23 | ⚠️ **部分关闭** —— 44 个 `pass` 空壳已清、4 个真实 INV 行为守卫已补;**E2E 场景实现已完成(2026-09-19,见 Task 2.1)**;**INV-011 已补真实行为守卫(2026-09-19,见 Task 2.2,9 用例 + 两次注入验证)**。仅余 E2E 在 CI 上的首次实测绿 |
|
| Phase 23 | ⚠️ **部分关闭** —— 44 个 `pass` 空壳已清、4 个真实 INV 行为守卫已补;**E2E 场景实现已完成(2026-09-19,见 Task 2.1)**;**INV-011 已补真实行为守卫(2026-09-19,见 Task 2.2,9 用例 + 两次注入验证)**。仅余 E2E 在 CI 上的首次实测绿 |
|
||||||
| Phase 23B | ✅ E2E 测试基建重建(conftest + seed + CI e2e job,本机活栈 49 → **65 passed**) |
|
| Phase 23B | ✅ E2E 测试基建重建(conftest + seed + CI e2e job,本机活栈 49 → **65 passed**) |
|
||||||
| 横向加固 | ✅ HTTP 语义(105 处真实状态码)+ 身份收敛 + 幂等键(30 端点,V045/V046)+ 端口/入口守卫 |
|
| 横向加固 | ✅ HTTP 语义(105 处真实状态码)+ 身份收敛 + 幂等键(30 端点,V045/V046)+ 端口/入口守卫 |
|
||||||
| 后端回归 | **685 / 0 failures / 0 errors / 3 skipped**(2026-09-19 全量实测,含新增 9 例 INV-011 守卫 + 3 例 Mapper 绑定守卫 + 4 例隐私 upsert 守卫) |
|
| 后端回归 | **689 / 0 failures / 0 errors / 3 skipped**(2026-09-19 全量实测;含 9 例 INV-011 + 3 例 Mapper 绑定 + 8 例隐私契约/upsert) |
|
||||||
| UI 契约门禁 | **18/18 绿**(原文 14/14 已过期,见 Global Constraints) |
|
| UI 契约门禁 | **18/18 绿**(原文 14/14 已过期,见 Global Constraints) |
|
||||||
| 停点 | **Phase 24(UAT)启动门前 —— 需用户确认** |
|
| 停点 | **Phase 24(UAT)启动门前 —— 需用户确认** |
|
||||||
|
|
||||||
@@ -274,12 +274,21 @@
|
|||||||
已软删的行会让本方法走插入分支并撞主键。当前**不存在任何软删隐私设置的路径**,故不可达;
|
已软删的行会让本方法走插入分支并撞主键。当前**不存在任何软删隐私设置的路径**,故不可达;
|
||||||
若将来新增软删,需同步改为 PG `ON CONFLICT (user_id) DO UPDATE`。
|
若将来新增软删,需同步改为 PG `ON CONFLICT (user_id) DO UPDATE`。
|
||||||
|
|
||||||
**② 上层(桩)仍未接通,且这才是用户当前真正遇到的故障** —— 见附 B-13。
|
**② 上层(桩)已于 2026-09-19 接通** ✅ —— 产品裁定:**键集以小程序为准,用迁移补齐**。
|
||||||
⚠️ **关键**:小程序 `pages/privacy-settings` 调的是 `/account/privacy-settings`
|
- 新增迁移 **V047**:补 `allow_activity_invitation`(默认 TRUE)与 `show_last_online`(默认 **FALSE**)。
|
||||||
(走 `AccountLifecycleService` **桩**,不落库),**不是** `PrivacyService`。
|
默认值**不对称**,与小程序 `data` 初值严格一致 —— `showLastOnline` 写成 TRUE 等于默认公开在线状态。
|
||||||
管理端对隐私端点零调用。因此 ① 修的是「尚未被前端触达」的路径 ——
|
- 既有 6 列**保留不动**:`UserProfileService` 的资料详情 DTO 仍在全量返回这 6 键(第 74-79 行),
|
||||||
**必要但当前用户不可感知**。真正的用户可见故障是「改隐私设置根本不保存」。
|
那是仍在生效的独立读取出口。本迁移只做加法。
|
||||||
① 是 ② 的**必要前置**:桩一旦接到 `PrivacyService`,立刻会撞上 upsert 缺陷。
|
- `AccountLifecycleService` 两个桩改为委托 `PrivacyService` 真实读写。
|
||||||
|
- 入参校验设计:**已知键类型错误必须显式报错**(`IllegalArgumentException`),
|
||||||
|
未知键宽容忽略 —— 若静默丢弃,用户会看到「保存成功」而设置从未生效,这类缺陷最难排查。
|
||||||
|
另容错 Boolean 的字符串形式("true"/"false")。
|
||||||
|
- 守卫:`PrivacySettingsUpsertGuardTest` 扩至 **8 用例**。其中 2 个是**契约守卫**,
|
||||||
|
**直接解析小程序源文件**的 PUT payload 与 `data` 块得出权威键集/默认值,
|
||||||
|
而非抄一份契约副本(沿用 `EntityMigrationParityTest` 的纪律)。
|
||||||
|
- 负向注入:把桩退回「不落库」→ **恰 2 红**(落库用例 + 类型校验用例),
|
||||||
|
其余 6 个正确未红。已还原。
|
||||||
|
- 至此小程序隐私设置**真正可保存**,① ② 两层均已关闭。
|
||||||
- [ ] **注销红娘服务拦截硬编码 `0`**:
|
- [ ] **注销红娘服务拦截硬编码 `0`**:
|
||||||
原文断言「数据模型**没有 `matchmaker_services` 表**(全仓搜索为空),建表属扩权,需产品先确认」——
|
原文断言「数据模型**没有 `matchmaker_services` 表**(全仓搜索为空),建表属扩权,需产品先确认」——
|
||||||
**三条均与实况不符,2026-09-19 实测推翻**:
|
**三条均与实况不符,2026-09-19 实测推翻**:
|
||||||
@@ -297,9 +306,22 @@
|
|||||||
|
|
||||||
### Task 3.2: Phase 22 遗留(死配置)
|
### Task 3.2: Phase 22 遗留(死配置)
|
||||||
|
|
||||||
- [ ] `counselingEnabled` / `premaritalEnabled` / `contentEnabled` / `serviceLocationEnabled` / `localEligibilityEnabled` 五个开关**从未被业务读取** → 要么接线成真 gate,要么如实移除(不得留「配置暴露但未强制」的假开关)。
|
- [x] `counselingEnabled` / `premaritalEnabled` / `contentEnabled` / `serviceLocationEnabled` / `localEligibilityEnabled` 五个开关**从未被业务读取** → **2026-09-19 已移除**(产品裁定:确认无影响即可移除)。
|
||||||
- [ ] `counselingAttachmentsPrivate` / `Bucket` 私有桶死配置(存储实走 `object-storage.bucket-private`)→ 同上。
|
- **移除前的无影响论证**(不是"看着没人用就删"):全仓搜索 5 个 getter,命中的只有
|
||||||
- [ ] **`PUT /feature-flags` 不持久化** → 修复或明确标注为只读。
|
① 本类定义处、② `getAllFlags()`、③ `FeatureFlagControllerTest` 一处断言 —— **业务代码零调用点**;
|
||||||
|
前端 `admin-web/src` 与 `miniapp` 对 `feature-flag` **零调用**(仅一个未使用的 storageKey 常量)。
|
||||||
|
- ⚠️ **重要边界**:`FeatureFlagConfig` **整体不能删** —— 同类的
|
||||||
|
`isAppealEnabled`(AppealService 4 处)、`isLocalEvidenceUploadEnabled`(LocalEligibilityService)、
|
||||||
|
`getLocalEvidenceMaxFiles` / `getLocalEvidenceMaxFileSizeMB` / `getLocalEvidenceAllowedTypes`
|
||||||
|
(PhotoService)都是**真实生效的 gate**。本次只删那 5 个死的。
|
||||||
|
- 同步清理:`application-dev.yml` / `application-docker.yml` 各 7 项配置
|
||||||
|
(含 counselingAttachments 2 项),否则会留下"配了但不生效"的新死配置。
|
||||||
|
- `getAllFlags()` 键数 21 → **14**;`FeatureFlagControllerTest` 相应更新,
|
||||||
|
布尔样例由 `counselingEnabled` 换成 `referralEnabled`。
|
||||||
|
- [x] `counselingAttachmentsPrivate` / `Bucket` 私有桶死配置(存储实走 `object-storage.bucket-private`)→ **同上,已移除**(除测试外零引用)。
|
||||||
|
- [ ] **`PUT /feature-flags` 不持久化** → 修复或明确标注为只读。**仍未处理**:
|
||||||
|
改动涉及运维流程(改的是运行时内存态,重启即失效),与"删死配置"风险不同,
|
||||||
|
需你确认是要做成真持久化,还是标注为只读后关掉该端点。
|
||||||
|
|
||||||
### Task 3.3: 任务级 ⏳ 清偿
|
### Task 3.3: 任务级 ⏳ 清偿
|
||||||
|
|
||||||
@@ -427,7 +449,7 @@
|
|||||||
9. **本机跑 Java 测试的通道 —— 含两处「必挂」,漏掉任一处都会导出完全错误的结论**(工具性发现,可复用)
|
9. **本机跑 Java 测试的通道 —— 含两处「必挂」,漏掉任一处都会导出完全错误的结论**(工具性发现,可复用)
|
||||||
本机**无 JDK / 无 Maven / 无 `~/.m2`**(`mvn` 报 `Could not find Maven installation`),
|
本机**无 JDK / 无 Maven / 无 `~/.m2`**(`mvn` 报 `Could not find Maven installation`),
|
||||||
测试镜像 `danyang-match-backend-test` 内含 Temurin JDK 21 + Maven 3.9.16 + 完整源码。
|
测试镜像 `danyang-match-backend-test` 内含 Temurin JDK 21 + Maven 3.9.16 + 完整源码。
|
||||||
**正确命令**(2026-09-19 实测 `BUILD SUCCESS`,全量 **685 / 0 failures / 0 errors / 3 skipped**,约 6 分钟):
|
**正确命令**(2026-09-19 实测 `BUILD SUCCESS`,全量 **689 / 0 failures / 0 errors / 3 skipped**,约 6 分钟):
|
||||||
```bash
|
```bash
|
||||||
MSYS_NO_PATHCONV=1 docker run --rm \
|
MSYS_NO_PATHCONV=1 docker run --rm \
|
||||||
-v "E:/develop/公益婚恋服务平台":/proj \
|
-v "E:/develop/公益婚恋服务平台":/proj \
|
||||||
@@ -560,8 +582,25 @@
|
|||||||
- **推论**:评估"某修复的价值"时,必须先确认**前端实际调用哪条路径**。
|
- **推论**:评估"某修复的价值"时,必须先确认**前端实际调用哪条路径**。
|
||||||
本项目同一业务存在多套并存端点(`/me/*` 与 `/account/*` 的历史兼容写法),
|
本项目同一业务存在多套并存端点(`/me/*` 与 `/account/*` 的历史兼容写法),
|
||||||
只看后端有真实实现 ≠ 用户能走到。
|
只看后端有真实实现 ≠ 用户能走到。
|
||||||
- 待裁定项(Task 3.1 ① 上层)仍不变:键集以谁为准(表六列 / 小程序三字段 / 桩键集,
|
- **2026-09-19 已关闭**:产品裁定「**以小程序为准加迁移**」→ V047 补 2 列、
|
||||||
**仅 1 个字段交集**)—— 见附 B-11。
|
`AccountLifecycleService` 桩改为委托 `PrivacyService`。小程序隐私设置**真正可保存**。
|
||||||
|
守卫里 2 个用例直接解析小程序源文件得出权威键集,防止再次漂移。
|
||||||
|
|
||||||
|
14. **新增迁移必须同步更新 `MigrationValidationTest.LATEST_MIGRATION`**(工具性发现,本轮实测踩到)
|
||||||
|
|
||||||
|
本轮新增 `V047` 后,全量回归立刻报红 1 项(不是我的测试,是既有守卫):
|
||||||
|
|
||||||
|
```
|
||||||
|
MigrationValidationTest.testMigrationNamingConvention:58
|
||||||
|
版本号超出当前基线: V047__align_privacy_settings_with_miniapp.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
- 该守卫断言两件事:① 每个迁移版本号 ∈ `[1, LATEST_MIGRATION]`;
|
||||||
|
② **迁移文件总数 == LATEST_MIGRATION**(即不允许缺号)。
|
||||||
|
- 常量在 `MigrationValidationTest.java:25`,当前已由 46 更新为 **47**。
|
||||||
|
- **这是有效守卫,不是障碍**:它防止"新增迁移但忘记登记基线"或"文件多了没被发现"。
|
||||||
|
正确做法是更新常量,**不要**为了让测试变绿而改断言方式。
|
||||||
|
- 命名正则:`V\d{3}__[^_]+(?:_[^_]+)*\.sql` —— 描述部分用下划线分词,不能有连续下划线。
|
||||||
- **需要用户/产品三选一**:以表为准改造前端 UI / 以小程序为准补迁移加列 / 加一层映射。
|
- **需要用户/产品三选一**:以表为准改造前端 UI / 以小程序为准补迁移加列 / 加一层映射。
|
||||||
在此之前 Task 3.1 第 1 项**不应动手**。
|
在此之前 Task 3.1 第 1 项**不应动手**。
|
||||||
- 附带:`user_privacy_settings` / `user_preferences` / `review_reasons` / `review_items`
|
- 附带:`user_privacy_settings` / `user_preferences` / `review_reasons` / `review_items`
|
||||||
@@ -573,4 +612,4 @@
|
|||||||
*审阅校正: 2026-09-19 — 按实测推翻 9 处失实断言(见附 A;其中 2 处是**对本轮自查中自身结论的更正** ——
|
*审阅校正: 2026-09-19 — 按实测推翻 9 处失实断言(见附 A;其中 2 处是**对本轮自查中自身结论的更正** ——
|
||||||
「`-o` 不可用」「35 failures 是回归」两条均系命令/挂载所致,已如实留痕),登记 11 项新发现(见附 B)*
|
「`-o` 不可用」「35 failures 是回归」两条均系命令/挂载所致,已如实留痕),登记 11 项新发现(见附 B)*
|
||||||
*执行进展: 2026-09-19 — Task 2.1(E2E 16 场景)、Task 2.2(INV-011 守卫)均已完成并验证;
|
*执行进展: 2026-09-19 — Task 2.1(E2E 16 场景)、Task 2.2(INV-011 守卫)均已完成并验证;
|
||||||
后端全量回归 685 / 0 failures / 0 errors / 3 skipped;UI 契约门禁 18/18 绿*
|
后端全量回归 689 / 0 failures / 0 errors / 3 skipped;UI 契约门禁 18/18 绿*
|
||||||
|
|||||||
Reference in New Issue
Block a user