根治三类工程治理问题(技术默认值≠产品规则、配置存在但未闭环、文档代码漂移): - 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
91 lines
3.5 KiB
JavaScript
91 lines
3.5 KiB
JavaScript
/**
|
||
* 业务数量参数(规范 §118 App 配置 + AGENTS.md 第 12 条"所有未确认数量参数配置化")
|
||
*
|
||
* 全部数量限制走配置,禁止页面层硬编码业务常量。
|
||
* 实际运行时由 services/appConfigService 从服务端拉取并覆盖默认值(规范 §118)。
|
||
* 配置接口失败时使用本文件的安全默认值(规范 §119)。
|
||
*/
|
||
|
||
exports.BUSINESS_LIMITS = {
|
||
// 推荐相关
|
||
DAILY_RECOMMENDATION_LIMIT: 6, // 每日推荐人数
|
||
RECOMMENDATION_PAGE_SIZE: 10, // 推荐分页大小
|
||
|
||
// 资料相关
|
||
MAX_PROFILE_PHOTOS: 6, // 生活照上限
|
||
MAX_INTEREST_TAGS: 8, // 兴趣标签上限(规范 §35 不超过 8 个)
|
||
MIN_INTRO_LENGTH: 20, // 自我介绍最小字数
|
||
MAX_INTRO_LENGTH: 500, // 自我介绍最大字数
|
||
MAX_REPORT_LENGTH: 500, // 举报说明最大字数
|
||
MAX_FEEDBACK_LENGTH: 500, // 意见反馈最大字数
|
||
|
||
// 聊天相关
|
||
CHAT_IMAGE_ENABLED: false, // V1 默认关闭图片聊天(规范 §76 Feature Flag)
|
||
CHAT_MESSAGE_MAX_LENGTH: 1000,
|
||
CHAT_HISTORY_PAGE_SIZE: 30, // 聊天历史分页
|
||
|
||
// 功能开关(规范 §76)
|
||
ACTIVITY_ENABLED: true,
|
||
MATCHMAKER_ENABLED: true,
|
||
ENABLE_RISK_KEYWORD_WARNING: true,
|
||
|
||
// 后台分页(规范 §82)
|
||
PAGE_SIZE_DEFAULT: 20,
|
||
PAGE_SIZE_OPTIONS: [20, 50],
|
||
|
||
// 时长(ms)
|
||
TOAST_DURATION: 1500, // 规范 §56:1.5~2 秒
|
||
BUTTON_PRESS_FEEDBACK: 120, // 按钮按压反馈时长
|
||
|
||
// 网络请求
|
||
REQUEST_TIMEOUT_MS: 15000,
|
||
REQUEST_RETRY_COUNT: 1,
|
||
|
||
// 联系方式
|
||
SUPPORT_CONTACT: '',
|
||
};
|
||
|
||
/**
|
||
* 服务端 /app/config 的嵌套结构 → 客户端 UPPER_SNAKE 常量的显式映射。
|
||
*
|
||
* <p>此前用平铺合并 {@code {...BUSINESS_LIMITS, ...overrides}},命不中嵌套 key
|
||
* (服务端返回的是 {@code businessLimits.maxProfilePhotos} / {@code features.x}),
|
||
* 导致服务端下发的照片上限 / 推荐阈值等配置<b>从不生效</b> —— 前后端规则漂移
|
||
* (V1.0.1 配置纠偏 PROFILE-001 前端侧根因)。现改为按节(section)+ 键显式映射,
|
||
* 且仅在「服务端给了该键 && 客户端确有该常量」时才覆盖,缺省回退本地默认值。
|
||
*/
|
||
const SERVER_SECTION_TO_CLIENT_KEY = {
|
||
businessLimits: {
|
||
dailyRecommendationLimit: 'DAILY_RECOMMENDATION_LIMIT',
|
||
maxProfilePhotos: 'MAX_PROFILE_PHOTOS',
|
||
maxInterestTags: 'MAX_INTEREST_TAGS',
|
||
selfIntroMaxLength: 'MAX_INTRO_LENGTH',
|
||
},
|
||
features: {
|
||
activityEnabled: 'ACTIVITY_ENABLED',
|
||
matchmakerEnabled: 'MATCHMAKER_ENABLED',
|
||
imageChatEnabled: 'CHAT_IMAGE_ENABLED',
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 用服务端返回的配置覆盖默认值(规范 §118)
|
||
* @param {object} serverConfig 服务端 /app/config 的完整响应(含 businessLimits / features 等嵌套节)
|
||
* @returns {typeof exports.BUSINESS_LIMITS} 合并后的运行时配置
|
||
*/
|
||
exports.mergeAppConfig = (serverConfig) => {
|
||
const merged = { ...exports.BUSINESS_LIMITS };
|
||
if (!serverConfig || typeof serverConfig !== 'object') return merged;
|
||
for (const [section, keyMap] of Object.entries(SERVER_SECTION_TO_CLIENT_KEY)) {
|
||
const src = serverConfig[section];
|
||
if (!src || typeof src !== 'object') continue;
|
||
for (const [serverKey, clientKey] of Object.entries(keyMap)) {
|
||
if (Object.prototype.hasOwnProperty.call(src, serverKey)
|
||
&& Object.prototype.hasOwnProperty.call(merged, clientKey)) {
|
||
merged[clientKey] = src[serverKey];
|
||
}
|
||
}
|
||
}
|
||
return merged;
|
||
};
|