Просмотр исходного кода

docs(cf): 打卡CF值后台配置核查报告(等级矩阵不可配 + 写死清单 B1/B2/P1-P3)

Agnes Agent 1 неделя назад
Родитель
Сommit
25c2640606
1 измененных файлов с 132 добавлено и 0 удалено
  1. 132 0
      docs/superpowers/plans/2026-10-31-checkin-cf-backend-config-audit.md

+ 132 - 0
docs/superpowers/plans/2026-10-31-checkin-cf-backend-config-audit.md

@@ -0,0 +1,132 @@
+# 打卡 CF 值后台配置核查与改造方案
+
+> 生成:2026-10-31 21:1x · 状态:待廖总确认 · 依据:全仓静态核查 + 规则引擎现状
+> 范围:① 打卡任务 CF 值按会员等级后台可配;② 全仓 CF 值写死清单与改造
+
+---
+
+## 〇、结论速览
+
+| 用户诉求 | 现状 | 结论 |
+|---|---|---|
+| 会员各等级不同打卡任务 CF 值**可后台配置** | 等级差异化**只作用于每日限次**(cf_earn_daily_limit);**单次 CF 值固定**(computeAmount 不读等级) | ❌ **不可配**,需改造 |
+| 还有哪些 CF 值**写死在代码里** | CF 规则 F02–F24 已走 cf_earn_rule(可配);但 **2 处业务硬编码** + **3 处 points 写死** + **1 处配置假入口** | ⚠️ 见 §三 |
+
+---
+
+## 一、现有后台配置能力(已具备部分)
+
+### 1.1 CF 规则引擎(cf_earn_rule,F02–F24)—— 已有管理端页
+
+- 页面:`cfc-web/src/views/admin/CfRuleConfig.vue`(路由 `cf-rule-config`,权限 `finance:cf-value`)
+- 接口:`cfc-web/src/api/cfRule.js` → `AdminCfEarnRuleController`(list/save/toggle/history)
+- **可配字段**:code/name/category/ruleType/**amount(固定数量)**/formula/dailyLimitPerMember/totalLimitPerMember/cooldownSeconds/dedupRefType/sortOrder/enabled/**paramsJson(自由文本)**/remark
+- **已配的打卡类规则**(数量在规则表,非代码写死):
+  | 规则 | 打卡内容 | 类型 | 数量 | 日限 |
+  |---|---|---|---|---|
+  | F14 | 喝水打卡 | fixed | 5 | 按等级 |
+  | F15 | 饮食打卡 | fixed | 5 | 按等级 |
+  | F16 | 每日健康打卡 | fixed | 10 | 按等级 |
+  | F05 | 活动签到 | per_item | 活动配置 platform_points | — |
+  | F07 | 文章答题 | formula | score+全对3 | 5 |
+  | F08 | AI阅读答题 | fixed | 5 | 3 |
+
+### 1.2 等级差异化现状(仅限次,非金额)
+
+- 表:`cf_earn_daily_limit`(rule_code × level_code → daily_limit)
+- 种子:`CF_DAILY_LIMIT_RULES = {F14, F15, F16, F22}`,等级 FREE=1 / BASIC=3 / FAMILY=10 / LIFETIME=10 / PREMIUM=20
+- 读取:`CfBalanceServiceImpl.resolveDailyLimit(rule, levelCode)`(F14/F15/F16/F22 命中分级,其余走规则默认)
+- **⚠️ 管理端无 cf_earn_daily_limit 配置页**(CfRuleConfig.vue 只显示规则的日限字段,不显示等级矩阵)
+
+---
+
+## 二、核心缺口:单次 CF 值按等级差异化
+
+### 2.1 现状(为何不可配)
+
+`CfBalanceServiceImpl.computeAmount()` 只按 `rule_type` 计算:fixed→`rule.amount`、formula→公式、per_item→ctx 数量;
+`resolveMultiplier()` 读 `params.multiplier_key/multiplier`(全局倍数,非等级)。**等级 levelCode 在发放时已解析(resolveLevelCode),但只用于日限判断,不参与金额计算。**
+
+→ 结论:同一规则不管会员等级多少,单次 CF 值相同(F14 喝水=5CF 无论 PREMIUM 或 FREE)。**用户诉求无法满足。**
+
+### 2.2 改造建议(对齐 2026-10-31 打卡 CF 设计文档 D8)
+
+**方案:等级金额通道 `params_json.levels[levelCode]`**(不改表,复用规则引擎 params_json)
+
+1. **后端 `computeAmount` 增强**:
+   - 解析 `params_json.levels`(Map<levelCode, number>),发放时 `amount = levels.getOrDefault(levelCode, rule.amount)`;
+   - 规则 `amount` 作为兜底(FAMILY 基准),等级缺失回落 amount;
+   - 等级乘数也可用 `params_json.level_multipliers`(NONE=0.5/FAMILY=1.0/PREMIUM=1.5)实现,二选一(建议 levels 显式值,可审计)。
+2. **管理端 CfRuleConfig.vue 增强**:规则编辑弹窗增加「按会员等级配置单次 CF 值」矩阵(等级行 × 数量输入),
+   写入 `paramsJson.levels`;替代裸 `paramsJson` 文本框(或并存)。
+3. **种子**:为打卡类规则 F14/F15/F16/F05/F07/F08 补 `levels` 建议默认(与设计文档 §3.2 矩阵一致)。
+
+### 2.3 等级矩阵建议值(来自 2026-10-31 设计文档)
+
+| 打卡内容 | 规则 | 非会员0.5× | FAMILY1.0× | PREMIUM1.5× |
+|---|---|---|---|---|
+| 健康打卡 | F16(每日健康) | 5 | 10 | 15 |
+| 喝水 | F14 | 2 | 5 | 8 |
+| 饮食 | F15 | 2 | 5 | 8 |
+| 文章答题 | F07 | 1 | 3 | 5 |
+| AI阅读答题 | F08 | 1 | 3 | 5 |
+| 活动签到 | F05 | 1 | 3 | 5 |
+
+> 数值可整体缩放;等级用 `membership_levels` 动态档位(FAMILY/PREMIUM,非会员 NONE)。
+
+---
+
+## 三、全仓 CF 值写死清单(核查结论)
+
+### 3.1 CF 值本体(统一账本)—— 已外置 ✅
+
+F02–F24 全部走 `cf_earn_rule`(管理端可配 amount/formula),代码只组装 refId + ctx,**不写死 CF 数量**。
+已确认:CfPathwayEarnService / CfGrowthPathwayEarnService / CfEarnDispatchService / CfBalanceServiceImpl 均从规则读。
+
+### 3.2 ⚠️ 业务硬编码(需改造)
+
+| # | 位置 | 写死内容 | 影响 | 建议 |
+|---|---|---|---|---|
+| B1 | `InviteMilestoneService.MILESTONE_TIERS` | INVITE_3={3,200,20} / INVITE_5={5,500,50} / INVITE_10={10,1000,100} | **F11 邀请里程碑 CF=200/500/1000 写死**,管理端修改不生效 | 迁移到 `invite_milestone` 种子表(reward_points),管理端更新该表 |
+| B2 | `AdminMilestoneController.MILESTONE_CONFIGS` | 同 200/500/1000 静态副本 | 配置**假入口**:update 只改 status=PENDING 行(DB 无种子则无效) | 与 B1 一并:改读 DB,update 落库 |
+
+### 3.3 ⚠️ points 积分写死(能量体系,非 CF,低优先)
+
+| # | 位置 | 写死 | 说明 |
+|---|---|---|---|
+| P1 | HealthCheckinService:92 | `int points=5` | 读 sys_config `health_checkin_points` 覆盖,默认 5 写死 |
+| P2 | FinanceCheckinService:70 | `int points=5` | 读 `finance_checkin_points` 覆盖,默认 5 写死 |
+| P3 | MicroActionService:237 | `int points=5` | **误读 `health_checkin_points` 键**(应为 micro 专用键) |
+
+> 这些是**能量体系积分**(family_members.total_points),非 CF。用户诉求聚焦 CF;points 可后续统一。
+
+### 3.4 ✅ 已确认非硬编码(勿动)
+
+- F05/F10/F11/F12/F17/F20/F21 per_item 类:数量来自 `activities.platform_points` / `family_challenge.reward_points` / `invite_milestone.reward_points` / `growth_task.reward_points` / `challenge_task.reward_cf` / `problem_domain.reward_cf_value` / `unlock_gate.reward_points` —— **全部管理端可配**(除 F11 见 B1)。
+- F18 健康启航:formula `floor(amt)` + `cf_per_yuan=100`,原硬编码 300 已删除 ✅。
+- CfConfigKeys 兜底默认值:仅配置行缺失时生效,权威值在 service_config(管理端 `/api/admin/service-config/save` 可配)✅。
+
+---
+
+## 四、改造任务清单(待廖总确认后入池)
+
+| # | 模块 | 改动 | 优先级 |
+|---|---|---|---|
+| T1 | cfc-backend | `CfBalanceServiceImpl.computeAmount` 支持 `params_json.levels[levelCode]` 等级金额通道 | P0 |
+| T2 | cfc-web | `CfRuleConfig.vue` 规则编辑弹窗增加「等级×单次CF值」矩阵(写 paramsJson.levels) | P0 |
+| T3 | cfc-backend | 种子:打卡类规则 F14/F15/F16/F05/F07/F08 补 `levels` 默认值(幂等 upsert) | P1 |
+| T4 | cfc-backend | B1:`invite_milestone` 建表种子(INVITE_3/5/10)+ `InviteMilestoneService` 改读 DB | P1 |
+| T5 | cfc-web | B2:`AdminMilestoneController` 改读 DB,update 落库(修假入口) | P1 |
+| T6 | cfc-backend | P3:MicroActionService 误读键修正(health→micro 专用) | P2 |
+
+> 另可选项:T7 管理端 cf_earn_daily_limit 等级日限矩阵配置页(当前无页,F14/F15/F16/F22 日限按等级写死在种子)。
+
+---
+
+## 五、待确认决策点
+
+1. **等级金额通道形态**:`levels[levelCode]` 显式值(可审计,推荐)vs `level_multipliers` 乘数(省配置但语义隐晦)?
+2. **等级档位**:固定 NONE/FAMILY/PREMIUM 三档 vs 动态读 membership_levels(动态更灵活,推荐)?
+3. **F11 里程碑**:是否本次一并迁移到 DB(B1/B2 是真假配置缺口,建议一并修)?
+4. **points 写死**(P1–P3):是否顺带修(低优先,非 CF)?
+5. **建议矩阵数值**:§2.3 是否按默认确认,或调整?