Bläddra i källkod

fix: 会员下单金额统一读membership_levels(修复FAMILY展示1980却按1314扣款)

- MembershipService.createOrder/createOrderWithPayType 的 FAMILY/PROVIDER/PREMIUM 分支原读 sys_config 旧价格键(member_fee_family_yearly=131400 等),与前端/后台展示源 membership_levels(198000) 不一致
- 抽出 resolveMembershipAmount 统一从 membership_levels 取价(FAMILY 强制年付)
- 迁移380: virtual_goods_config 中 FAMILY 道具映射 member1314->member1980,避免微信下单金额=道具价校验拒付
- sys_config 价格键保留不动(会员价单一来源为 membership_levels)
Xiaogang Liao 1 vecka sedan
förälder
incheckning
7feeeb7ed4

+ 17 - 0
cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java

@@ -3085,6 +3085,9 @@ insertSysConfigSeed("gift_membership_pay_price", "198000", "送会员商品达
 
         // 迁移379: 创建 report_member_permissions 表(管理员授权某用户查看某成员的全部报告)
         addReportMemberPermissionsTable();
+
+        // 迁移380: 修正 FAMILY 会员虚拟道具映射 member1314 → member1980(会员等级表 FAMILY 年费 ¥1980,原道具价 ¥1314 与下单金额不一致,会被微信"下单金额=道具价"校验拒付)
+        fixFamilyMembershipVirtualGoodsMapping();
     }
 
     private void migrateHealthDimensionsTable() {
@@ -12499,4 +12502,18 @@ log.info("迁移298: 已为无 openid 的 child 账号补齐 family_members 记
             log.warn("迁移379: 创建 report_member_permissions 表失败或已存在: {}", e.getMessage());
         }
     }
+
+    // 迁移380: 修正 FAMILY 会员虚拟道具映射为 member1980(幂等;仅纠正历史错误值 member1314,不覆盖后台后续配置)
+    private void fixFamilyMembershipVirtualGoodsMapping() {
+        try {
+            int updated = jdbcTemplate.update(
+                    "UPDATE virtual_goods_config SET product_id = 'member1980' " +
+                    "WHERE goods_type = 'MEMBERSHIP' AND biz_key = 'FAMILY' AND product_id = 'member1314'");
+            if (updated > 0) {
+                log.info("迁移380: FAMILY 会员虚拟道具映射已修正为 member1980,受影响 {} 行", updated);
+            }
+        } catch (Exception e) {
+            log.warn("迁移380: 修正 FAMILY 会员道具映射失败: {}", e.getMessage());
+        }
+    }
 }

+ 32 - 63
cfc-backend/src/main/java/com/etotem/cfc/service/MembershipService.java

@@ -362,6 +362,26 @@ public class MembershipService implements MembershipServiceInterface {
 
     // ==================== 订单与支付 ====================
 
+    /**
+     * 会员方案价格(分)。统一从 membership_levels 读取,与 /api/membership/plans 展示、后台配置同源,
+     * 避免历史 sys_config 价格键与会员等级表不一致导致"页面显示与下单扣款不符"。
+     * FAMILY 仅支持年付(调用方已把 effectivePeriod 规整为 yearly)。
+     */
+    private Integer resolveMembershipAmount(MembershipLevel level, String effectivePeriod) {
+        if ("monthly".equals(effectivePeriod)) {
+            return level.getPriceMonthly();
+        }
+        if ("quarterly".equals(effectivePeriod)) {
+            Integer quarterly = level.getPriceQuarterly();
+            if (quarterly != null && quarterly > 0) {
+                return quarterly;
+            }
+            Integer monthly = level.getPriceMonthly();
+            return monthly != null ? monthly * 3 : null;
+        }
+        return level.getPriceYearly();
+    }
+
     /**
      * 创建订单
      */
@@ -379,44 +399,14 @@ public class MembershipService implements MembershipServiceInterface {
             throw new RuntimeException("会员等级不存在");
         }
 
-        // 根据period确定费用(分)
-        Integer amount;
+        // 根据period确定费用(分)— 统一从 membership_levels 读取(与前端展示/后台配置同源,避免双源不一致)
         String effectivePeriod = period != null ? period : "yearly";
         if ("FAMILY".equals(levelCode)) {
-            // 家庭会员仅支持年付
-            effectivePeriod = "yearly";
-            String feeStr = sysConfigService.getValue("member_fee_family_yearly");
-            if (feeStr == null) {
-                feeStr = sysConfigService.getValue("member_fee_family");
-            }
-            int fee = feeStr != null ? Integer.parseInt(feeStr) : 131400;
-            amount = fee;
-        } else if ("PROVIDER".equals(levelCode)) {
-            String configKey = "member_fee_standard_" + effectivePeriod;
-            String feeStr = sysConfigService.getValue(configKey);
-            if (feeStr == null) {
-                feeStr = sysConfigService.getValue("member_fee_standard");
-            }
-            int fee = feeStr != null ? Integer.parseInt(feeStr) : 1314;
-            amount = fee;
-        } else if ("PREMIUM".equals(levelCode)) {
-            // PREMIUM定价从sysConfig读取
-            if ("monthly".equals(effectivePeriod)) {
-                String feeStr = sysConfigService.getValue("member_fee_premium_monthly");
-                amount = feeStr != null ? Integer.parseInt(feeStr) : 131400;
-            } else {
-                String feeStr = sysConfigService.getValue("member_fee_premium");
-                amount = feeStr != null ? Integer.parseInt(feeStr) : 1316800;
-            }
-        } else {
-            // 按period从会员等级表定价
-            if ("monthly".equals(effectivePeriod)) {
-                amount = level.getPriceMonthly();
-            } else if ("quarterly".equals(effectivePeriod)) {
-                amount = level.getPriceQuarterly() != null ? level.getPriceQuarterly() : level.getPriceMonthly() * 3;
-            } else {
-                amount = level.getPriceYearly(); // 默认yearly
-            }
+            effectivePeriod = "yearly"; // 家庭会员仅支持年付
+        }
+        Integer amount = resolveMembershipAmount(level, effectivePeriod);
+        if (amount == null || amount <= 0) {
+            throw new RuntimeException("该会员方案暂未开放购买");
         }
 
         int originalAmount = amount;
@@ -542,35 +532,14 @@ public class MembershipService implements MembershipServiceInterface {
             throw new RuntimeException("会员等级不存在");
         }
 
-        // 根据 period 确定费用(分)— 与 createOrder() 逻辑完全一致
-        Integer amount;
+        // 根据 period 确定费用(分)— 统一从 membership_levels 读取(与前端展示/后台配置同源,避免双源不一致)
         String effectivePeriod = period != null ? period : "yearly";
         if ("FAMILY".equals(levelCode)) {
-            effectivePeriod = "yearly";
-            String feeStr = sysConfigService.getValue("member_fee_family_yearly");
-            if (feeStr == null) { feeStr = sysConfigService.getValue("member_fee_family"); }
-            amount = feeStr != null ? Integer.parseInt(feeStr) : 131400;
-        } else if ("PROVIDER".equals(levelCode)) {
-            String configKey = "member_fee_standard_" + effectivePeriod;
-            String feeStr = sysConfigService.getValue(configKey);
-            if (feeStr == null) { feeStr = sysConfigService.getValue("member_fee_standard"); }
-            amount = feeStr != null ? Integer.parseInt(feeStr) : 1314;
-        } else if ("PREMIUM".equals(levelCode)) {
-            if ("monthly".equals(effectivePeriod)) {
-                String feeStr = sysConfigService.getValue("member_fee_premium_monthly");
-                amount = feeStr != null ? Integer.parseInt(feeStr) : 131400;
-            } else {
-                String feeStr = sysConfigService.getValue("member_fee_premium");
-                amount = feeStr != null ? Integer.parseInt(feeStr) : 1316800;
-            }
-        } else {
-            if ("monthly".equals(effectivePeriod)) {
-                amount = level.getPriceMonthly();
-            } else if ("quarterly".equals(effectivePeriod)) {
-                amount = level.getPriceQuarterly() != null ? level.getPriceQuarterly() : level.getPriceMonthly() * 3;
-            } else {
-                amount = level.getPriceYearly();
-            }
+            effectivePeriod = "yearly"; // 家庭会员仅支持年付
+        }
+        Integer amount = resolveMembershipAmount(level, effectivePeriod);
+        if (amount == null || amount <= 0) {
+            throw new RuntimeException("该会员方案暂未开放购买");
         }
 
         int originalAmount = amount;