فهرست منبع

feat(membership): 商品赠送会员-计算giftAmount并累加触发赠送

Sisyphus 22 ساعت پیش
والد
کامیت
13acb75eb0

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

@@ -1047,6 +1047,8 @@ public class DatabaseInitializer implements CommandLineRunner {
             insertSysConfigSeed("trial_days", "7", "试用天数");
             insertSysConfigSeed("trial_features", "[\"free_activities\",\"free_courses\",\"teacher_consultation\"]", "试用期间可用功能");
             insertSysConfigSeed("member_pay_virtual_enabled", "1", "会员支付是否使用虚拟支付通道(1=虚拟,0=普通微信)");
+            insertSysConfigSeed("gift_membership_threshold", "20000", "购买送会员商品累计达到该金额(分)赠送会员");
+            insertSysConfigSeed("gift_membership_level", "FAMILY", "达到送会员额度后赠送的会员等级");
             insertSysConfigSeed("health_dimension_weights", "{\"growth\":0.20,\"sleep\":0.15,\"vision\":0.10,\"immunity\":0.15,\"nutrition\":0.15,\"gut\":0.10,\"exercise\":0.15}", "7维健康→身能量权重配置");
             insertSysConfigSeed("health_dimension_sync_factor", "0.5", "7维健康同步至身能量系数");
             log.info("SysConfig 种子数据已加载");
@@ -11147,6 +11149,8 @@ log.info("迁移298: 已为无 openid 的 child 账号补齐 family_members 记
         createUserShortcutConfigTable();
         createUserAppUsageTable();
         seedGuidePopupData();
+        addGiftAmountColumn();
+        createGiftMembershipProgressTable();
 
         // 迁移327: 创建 report_feedback 表(报告反馈+AI解答)
         try {
@@ -11648,4 +11652,30 @@ log.info("迁移298: 已为无 openid 的 child 账号补齐 family_members 记
             log.warn("预置引导弹窗种子数据失败: {}", e.getMessage());
         }
     }
+
+    // 迁移337: product_orders 表添加 gift_amount 列(送会员商品原价金额,分)
+    private void addGiftAmountColumn() {
+        ensureColumn("product_orders", "gift_amount", "INT DEFAULT 0 COMMENT '送会员商品原价金额(分)'");
+    }
+
+    // 迁移338: 创建 gift_membership_progress 表(家庭维度送会员累计进度)
+    private void createGiftMembershipProgressTable() {
+        try {
+            jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS gift_membership_progress (" +
+                    "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
+                    "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
+                    "accumulated_amount INT DEFAULT 0 COMMENT '累计送会员商品原价金额(分)', " +
+                    "granted INT DEFAULT 0 COMMENT '是否已赠送 1=已赠送 0=未赠送', " +
+                    "granted_level VARCHAR(50) COMMENT '已赠送会员等级', " +
+                    "granted_order_no VARCHAR(64) COMMENT '触发赠送的订单号', " +
+                    "granted_at DATETIME COMMENT '赠送时间', " +
+                    "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
+                    "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
+                    "UNIQUE KEY uk_family (family_id)" +
+                    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='送会员累计进度'");
+            log.info("迁移338: 已创建 gift_membership_progress 表");
+        } catch (Exception e) {
+            log.warn("迁移338: 创建 gift_membership_progress 表失败: {}", e.getMessage());
+        }
+    }
 }

+ 5 - 2
cfc-backend/src/main/java/com/etotem/cfc/dto/ProductOrderDTO.java

@@ -59,7 +59,10 @@ public class ProductOrderDTO {
 
     private Long vendorId;
 
-    public static ProductOrderDTO from(ProductOrder o) {
+    // 赠送会员扩展
+    private Integer giftAmount;          // 送会员商品原价金额(分),0=无
+
+public static ProductOrderDTO from(ProductOrder o) {
         if (o == null) return null;
         ProductOrderDTO d = new ProductOrderDTO();
         d.id = o.getId();
@@ -92,7 +95,6 @@ public class ProductOrderDTO {
         d.refundTime = o.getRefundTime();
         d.logisticsNo = o.getLogisticsNo();
         d.logisticsCompany = o.getLogisticsCompany();
-        d.trackingNumber = o.getTrackingNumber();
         d.expressCompany = o.getExpressCompany();
         d.deliveryMethod = o.getDeliveryMethod();
         d.pickupCode = o.getPickupCode();
@@ -109,6 +111,7 @@ public class ProductOrderDTO {
         d.fulfillStatus = o.getFulfillStatus();
         d.fulfillConfirmedAt = o.getFulfillConfirmedAt();
         d.receivedAt = o.getReceivedAt();
+        d.giftAmount = o.getGiftAmount();
         return d;
     }
 }

+ 3 - 0
cfc-backend/src/main/java/com/etotem/cfc/entity/ProductOrder.java

@@ -65,6 +65,9 @@ public class ProductOrder implements Serializable {
     private String auditReason;
     private Integer shippingCost;
 
+    // 赠送会员扩展
+    private Integer giftAmount;          // 送会员商品原价金额(分),0=无
+
     // 配送与履约扩展
     private Long pickupLocationId;           // 自取地点ID
     private String pickupLocationSnapshot;   // 自取地点快照(JSON)

+ 22 - 14
cfc-backend/src/main/java/com/etotem/cfc/service/ProductOrderService.java

@@ -58,6 +58,7 @@ import com.etotem.cfc.dto.MembershipLevelDTO;
 import com.etotem.cfc.service.EnergyService;
 import com.etotem.cfc.service.UnlockGateService;
 import com.etotem.cfc.service.UserService;
+import com.etotem.cfc.service.GiftMembershipProgressService;
 import com.alibaba.fastjson.JSON;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -141,6 +142,9 @@ public class ProductOrderService {
     @Resource
     private ProductGiftRuleService productGiftRuleService;
 
+    @Resource
+    private GiftMembershipProgressService giftMembershipProgressService;
+
     @Resource
     private MembershipService membershipService;
 
@@ -232,6 +236,8 @@ public class ProductOrderService {
         int discountAmount = dto.getDiscountAmount() != null ? dto.getDiscountAmount() : 0;
         int moneyAmount = order.getTotalAmount() - pointsCost - discountAmount;
         order.setMoneyAmount(moneyAmount > 0 ? moneyAmount : 0);
+        order.setGiftAmount(productGiftRuleService.getActiveByProductId(product.getId()) != null
+                ? product.getPrice() * dto.getQuantity() : 0);
         order.setStatus("pending");
         order.setPaymentMethod(dto.getPaymentMethod() != null ? dto.getPaymentMethod() : "wechat");
         order.setRemark(dto.getRemark());
@@ -452,6 +458,7 @@ public class ProductOrderService {
     private Result<ProductOrderDTO> createMultiItem(CreateProductOrderDTO dto, Long buyerId) {
         int totalAmount = 0;
         int totalQuantity = 0;
+        int giftAmount = 0;
         OrderItemVO firstItem = dto.getItems().get(0);
         Product firstProduct = null;
         int firstUnitPrice = 0;
@@ -505,6 +512,10 @@ public class ProductOrderService {
             }
             totalAmount += unitPrice * item.getQuantity();
             totalQuantity += item.getQuantity();
+            // 送会员商品:累计原价金额(用于赠送会员额度判定)
+            if (product != null && productGiftRuleService.getActiveByProductId(product.getId()) != null) {
+                giftAmount += unitPrice * item.getQuantity();
+            }
             if (firstProduct == null) {
                 firstProduct = product;
                 firstUnitPrice = unitPrice;
@@ -522,6 +533,7 @@ public class ProductOrderService {
         order.setQuantity(totalQuantity);
         order.setUnitPrice(firstUnitPrice);
         order.setTotalAmount(totalAmount);
+        order.setGiftAmount(giftAmount > 0 ? giftAmount : 0);
         order.setPointsUsed(dto.getPointsUsed() != null ? dto.getPointsUsed() : 0);
         int pointsCost = dto.getPointsUsed() != null ? dto.getPointsUsed() : 0;
         order.setPointsCost(pointsCost);
@@ -800,21 +812,17 @@ public class ProductOrderService {
             log.error("发放测评额度失败: orderNo={}", orderNo, e);
             // 不阻塞支付流程
         }
-        // 商品赠送会员:原价购买且配置赠送规则时,支付成功后自动赠送会员
+        // 赠送会员:原价支付时,送会员商品金额累计到家庭进度,达到阈值自动赠送会员
         try {
-            if (paidProduct != null) {
-                ProductGiftRule giftRule = productGiftRuleService.getActiveByProductId(paidProduct.getId());
-                if (giftRule != null) {
-                    // 原价判定:无积分抵扣、无优惠券、无折扣
-                    boolean fullPrice = (order.getPointsUsed() == null || order.getPointsUsed() == 0)
-                            && order.getCouponId() == null
-                            && (order.getDiscountAmount() == null || order.getDiscountAmount() == 0);
-                    if (fullPrice && order.getFamilyId() != null) {
-                        boolean granted = membershipService.grantMembershipByGift(
-                                order.getFamilyId(), order.getBuyerId(), giftRule.getLevelCode(),
-                                order.getOrderNo(), order.getTransactionId());
-                        log.info("商品订单{}赠送会员{}结果={}", orderNo, giftRule.getLevelCode(), granted);
-                    }
+            if (order.getGiftAmount() != null && order.getGiftAmount() > 0 && order.getFamilyId() != null) {
+                // 原价判定:无积分抵扣、无优惠券、无折扣
+                boolean fullPrice = (order.getPointsUsed() == null || order.getPointsUsed() == 0)
+                        && order.getCouponId() == null
+                        && (order.getDiscountAmount() == null || order.getDiscountAmount() == 0);
+                if (fullPrice) {
+                    boolean granted = giftMembershipProgressService.accumulate(
+                            order.getFamilyId(), order.getBuyerId(), order.getGiftAmount());
+                    log.info("商品订单{}累计送会员金额={}, 触发赠送={}", orderNo, order.getGiftAmount(), granted);
                 }
             }
         } catch (Exception e) {