# 家庭平台积分(CF值)体系设计 > 日期:2026-08-12 > 分支:cfclub > 状态:待评审 ## 1. 背景与目标 现有系统有三套并行积分体系,职责不清、兑换能力空壳: | 体系 | 维度 | 发放 | 消费 | |------|------|------|------| | 孩子积分 `PointsService` | 孩子(family_member) | 任务/打卡/签到/文章 | 心愿兑换、优惠券兑换、兑换商品 | | 家长积分 `User.totalPoints` | 家长(user) | 无自动发放 | 商品订单积分抵扣(1积分=¥0.01) | | CF值 `PlatformPointsService` | 用户(user_id 唯一键) | 商品订单支付时、推荐分润、活动签到 | 仅提现;`spend()` 零调用 | **用户需求**(已确认): 1. 平台积分(CF值)是给**整个家庭**的共享池;家庭内部积分(家长积分+孩子积分)在**每个人身上**,两者是不同维度。 2. 商品按**实际购买金额**送积分:1元 = 1积分,可针对商品设置**积分倍数**,按实付金额计算。 3. 积分可兑换优惠券(**保持现状**:孩子积分兑换,家长积分/平台CF值不参与)。 4. 每个产品设置优惠券的可用积分(**现状已有**:`Coupon.pointsPrice`)。 5. 平台CF值消费场景本次一并做:**会员续费抵扣 + 商品购买抵扣**。 6. 旧 `user_platform_balance` 余额**一并迁入家庭池**(按 user→family 归属;无家庭自动创建)。 7. 家长积分**彻底退出商品购买抵扣**(现有结算页家长积分抵扣**替换为平台CF值抵扣**),家长积分只用于家庭内部许愿兑换。 ## 2. 目标架构 ``` 平台CF值(家庭维度,family_platform_balance) ├─ 发放:商品确认收货(实付×倍数)、推荐分润、活动签到 ├─ 消费:会员续费抵扣、商品购买抵扣、提现 └─ 原语:earn / spend / freeze / unfreeze / getBalance / getLogs / adjust 家庭内部积分(个人维度) ├─ 孩子(family_member):任务/打卡 → 心愿、兑换商品、兑换优惠券(现状保持) └─ 家长(user.total_points):仅家庭内部许愿兑换(商品抵扣入口移除) ``` ## 3. 数据库设计 ### 3.1 新表 `family_platform_balance` ```sql CREATE TABLE family_platform_balance ( id BIGINT AUTO_INCREMENT PRIMARY KEY, family_id BIGINT NOT NULL, total_earned INT NOT NULL DEFAULT 0, -- 累计获得 available INT NOT NULL DEFAULT 0, -- 可用 frozen INT NOT NULL DEFAULT 0, -- 提现冻结 withdrawn INT NOT NULL DEFAULT 0, -- 已提现 exchanged INT NOT NULL DEFAULT 0, -- 已消费(抵扣/兑换) created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_family (family_id) ); ``` ### 3.2 新表 `family_platform_balance_log` ```sql CREATE TABLE family_platform_balance_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, family_id BIGINT NOT NULL, type VARCHAR(20) NOT NULL, -- earn/spend/freeze/unfreeze/withdraw/adjust amount INT NOT NULL, balance_after INT NOT NULL, ref_type VARCHAR(50) NOT NULL, -- product_order/referral_dist/activity/withdrawal/adjust/refund ref_id BIGINT, remark VARCHAR(255), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_family (family_id), KEY idx_ref (ref_type, ref_id) ); ``` ### 3.3 修改 `products` ```sql ALTER TABLE products ADD COLUMN points_multiplier DECIMAL(4,2) NOT NULL DEFAULT 1.00 COMMENT '平台积分倍数,1元=1积分×倍数'; ``` ### 3.4 迁移 - 一次性迁移:`user_platform_balance` 按 user.family_id 归属累加进 `family_platform_balance`;user 无 familyId 的自动创建家庭(无则 new)。 - 迁移记录落 `family_platform_balance_log`(refType=`migration`,remark=来源 userId 列表),避免重复。 - 迁移后 `user_platform_balance` 表**保留**(只读历史),新发放入家庭池。 ## 4. 后端实现 ### 4.1 `FamilyPlatformPointsService`(新) 仿 `PlatformPointsService` 实现,键为 familyId: - `earn(familyId, amount, refType, refId, remark)`:total_earned+available,写流水,refId 幂等 - `spend(familyId, amount, refType, refId, remark)`:available→exchanged,余额不足抛错 - `freeze(familyId, amount, refId)` / `unfreeze(familyId, amount, refId)` / `confirmWithdraw(familyId, amount, refId)`:提现 - `adjust(familyId, amount, remark)`:管理端手动调整(正负),写 adjust 流水 - `getBalance(familyId)` / `getLogs(familyId, page, size)` / `getWithdrawable(familyId)` - 并发安全:`SELECT ... FOR UPDATE` 锁行 ### 4.2 商品送积分(核心) 触发点:`ProductOrderService.confirmReceive()`(L787-813)用户确认收货,`status: pending_receipt → completed` 成功分支之后。 计算:`积分 = floor(moneyAmount / 100) × points_multiplier`(向下取整) - `moneyAmount`:订单实付金额(分),即现金支付部分(`totalAmount - pointsCost`),积分抵扣的金额不产生积分,确认收货时已定 - `points_multiplier`:从 `products` 表按 `productId` 取 - 发放:`familyPlatformPointsService.earn(familyId, points, "product_order", orderId, "购买商品: {productName}")` - **幂等**:earn 按 (ref_type, ref_id) 查流水,存在则跳过 - 订单需能拿到 familyId:`ProductOrder.buyerId` → user.familyId ### 4.3 商品购买抵扣(替换家长积分抵扣) - 结算页 `checkout.vue`:积分抵扣输入从"家长积分"改为"家庭平台积分" - 下单入参 `CreateProductOrderDTO.pointsUsed` 语义不变(数量) - `ProductOrderService`:支付成功分支的 `deductUserPoints` 改为 `familyPlatformPointsService.spend(familyId, pointsUsed, "product_order", orderId, "商品购买积分抵扣")` - 支付失败/退款:`refundUserPoints` 改为 `earn` 回补(refType=`refund`) - `User.totalPoints` 商品抵扣路径删除(保留字段,仅许愿兑换使用) ### 4.4 会员续费抵扣 `MemberSubscriptionService.renew()`:增加可选平台积分抵扣参数 - 抵扣比例可配置:sys_config `membership_points_deduct_ratio`(默认如 1000 = 10%) - 抵扣后实付 = max(0, 原价 - 抵扣额) - 成功即 `spend(familyId, points, "membership", subId, "会员续费抵扣")` ### 4.5 提现 `WithdrawalService` 改走家庭池(freeze/confirmWithdraw/unfreeze 换服务),新增提现记录含 familyId。 ### 4.6 管理端 - `AdminFamilyBalanceController`:`/api/admin/family-platform-balance/*` - `list`(分页:family_id/余额/流水摘要) - `logs`(family_id + type/refType 筛选) - `adjust`(手动调整 + remark) - cfc-web 新增 `FamilyPlatformBalanceManage.vue` 页面(余额列表 + 流水 + 调整弹窗) ### 4.7 API(小程序) - `GET /api/family-platform/balance`:家庭余额(含累计/冻结/可提现) - `GET /api/family-platform/logs`:流水分页 - 商品详情接口 `ProductDTO` 增加 `pointsMultiplier` 字段(透出) ## 5. 前端实现 ### 5.1 小程序(cfc-frontend) - `utils/api.js`:新增 `getFamilyPlatformBalance` / `getFamilyPlatformLogs`;`ProductDTO.pointsMultiplier` 透出 - 商品详情页 `product-detail.vue`:价格区显示"购此商品可得 X 积分"(倍数>1 高亮"×N 倍积分") - 结算页 `checkout.vue`:积分抵扣改为家庭平台积分(余额取自新接口;1积分=¥0.01 规则沿用);显示"预计可得 X 积分" - 支付页 `payment.vue`:积分抵扣文案更新 - 家庭积分页(并入 `pages/wealth/index.vue` 或新建):余额 + 流水 + 提现入口 ### 5.2 管理端(cfc-web) - 商品编辑表单:新增"积分倍数"输入(ProductForm) - 新增 `FamilyPlatformBalanceManage.vue`:余额列表、流水、手动调整 ## 6. 错误处理与边界 | 场景 | 处理 | |------|------| | 积分余额不足 | 抵扣/提现抛业务异常,前端提示"平台积分不足" | | 并发抵扣 | 行锁 + 事务,二次校验余额 | | 支付失败退款 | 回补积分(refType=refund),幂等 | | 确认收货重复请求 | earn 按 refId 幂等跳过 | | 孤儿 user(无 family) | 迁移时自动创建家庭 | | 向下取整 | floor,不四舍五入(如 599元×1.5=898 积分) | ## 7. 测试 - 单元:积分计算(floor + 倍数)、earn/spend/freeze 余额变化、幂等防重 - 集成:确认收货→积分到账;结算抵扣→支付→spend;支付失败→回补 - 迁移:旧余额按 family 汇总正确、孤儿自动建家庭、不重复 - E2E:商品详情积分展示、家庭积分页 ## 8. 实施顺序 1. 数据库:两表 + products 字段 + 迁移(DatabaseInitializer + schema.sql 同步) 2. `FamilyPlatformPointsService` 原语 3. 商品送积分(confirmReceive 挂点) 4. 商品购买抵扣替换 + 会员续费抵扣 5. 提现改造 6. 管理端接口 + 页面 7. 小程序前端 8. 测试 + 部署 ## 9. 明确不做(YAGNI) - 不新建"家庭内部积分"表:家长/孩子积分保持现状 - 优惠券兑换不改:孩子积分兑换,平台CF值/家长积分不参与 - 不做积分商城(兑换实物商品):现状 PointsExchangeProduct 已有,不动 - 不迁移 `points_log`(孩子积分流水),仅迁移 `user_platform_balance`