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

chore: auto bump version and changelog [skip ci]

iwt 1 месяц назад
Родитель
Сommit
9e5265f628

+ 36 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/AdminProductGiftRuleController.java

@@ -0,0 +1,36 @@
+package com.etotem.cfc.controller.admin;
+
+import com.etotem.cfc.common.Result;
+import com.etotem.cfc.entity.ProductGiftRule;
+import com.etotem.cfc.service.ProductGiftRuleService;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/admin/product-gift-rule")
+public class AdminProductGiftRuleController {
+
+    @Resource
+    private ProductGiftRuleService giftRuleService;
+
+    @PostMapping("/save")
+    public Result<Void> save(@RequestBody Map<String, Object> params) {
+        Long productId = Long.parseLong(params.get("productId").toString());
+        Integer status = Integer.parseInt(params.get("status").toString());
+        Object levelCodeObj = params.get("levelCode");
+        String levelCode = levelCodeObj != null ? levelCodeObj.toString() : null;
+        return giftRuleService.saveRule(productId, status, levelCode);
+    }
+
+    @PostMapping("/list")
+    public Result<List<ProductGiftRule>> list(@RequestBody Map<String, Object> params) {
+        Long productId = Long.parseLong(params.get("productId").toString());
+        return Result.success(giftRuleService.listByProduct(productId));
+    }
+}

+ 74 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/MembershipService.java

@@ -514,6 +514,80 @@ public class MembershipService implements MembershipServiceInterface {
         return originalPrice;
     }
 
+    /**
+     * 商品赠送会员:赠送对象=家庭创建者;已有同级/更高级有效会员则跳过。
+     * 有效期=365天;写入 family_memberships + 升级记录(upgrade_type='gift');跳过佣金。
+     */
+    @Override
+    public boolean grantMembershipByGift(Long familyId, Long userId, String levelCode,
+                                         String sourceOrderNo, String transactionId) {
+        if (familyId == null || userId == null || levelCode == null || levelCode.isEmpty()) {
+            return false;
+        }
+        // 等级编码校验:需存在于 membership_levels 且非 FREE
+        MembershipLevel giftLevel = levelMapper.selectOne(new LambdaQueryWrapper<MembershipLevel>()
+                .eq(MembershipLevel::getLevelCode, levelCode));
+        if (giftLevel == null || "FREE".equals(giftLevel.getLevelCode())) {
+            return false;
+        }
+        // 目标用户=家庭创建者
+        Family family = familyMapper.selectById(familyId);
+        if (family == null || family.getCreatorId() == null) {
+            return false;
+        }
+        Long adminUserId = family.getCreatorId();
+        // 防御:购买者必须属于该家庭(赠送仅对家庭创建者生效)
+        User buyer = userMapper.selectById(userId);
+        if (buyer == null || !familyId.equals(buyer.getFamilyId())) {
+            return false;
+        }
+        User adminUser = userMapper.selectById(adminUserId);
+        if (adminUser == null) {
+            return false;
+        }
+        // 前置校验:当前有效等级低于赠送等级才送(无会员/过期=FREE 视为最低)
+        String currentLevel = getMemberLevel(adminUserId);
+        Long currentLevelId = null;
+        if (currentLevel != null && !"FREE".equals(currentLevel)) {
+            MembershipLevel current = levelMapper.selectOne(new LambdaQueryWrapper<MembershipLevel>()
+                    .eq(MembershipLevel::getLevelCode, currentLevel));
+            currentLevelId = current != null ? current.getId() : null;
+        }
+        if (currentLevelId != null && currentLevelId >= giftLevel.getId()) {
+            return false; // 已有同级或更高级有效会员,跳过
+        }
+        // 有效期:按级别年费周期(默认365天)
+        Date expireTime = new Date(System.currentTimeMillis() + 365L * 24 * 60 * 60 * 1000);
+        // 写订阅记录
+        FamilyMembership membership = new FamilyMembership();
+        membership.setFamilyId(familyId);
+        membership.setLevelCode(levelCode);
+        membership.setStartDate(new Date());
+        membership.setEndDate(expireTime);
+        membership.setPaymentMethod("gift");
+        membership.setPaymentStatus("paid");
+        membership.setOrderNo(sourceOrderNo);
+        membership.setTransactionId(transactionId);
+        membership.setAmount(0);
+        membershipMapper.insert(membership);
+        // 升级用户
+        String fromLevel = adminUser.getMemberLevel() != null ? adminUser.getMemberLevel() : "FREE";
+        adminUser.setMemberLevel(levelCode);
+        adminUser.setMemberExpireTime(expireTime);
+        adminUser.setUpdatedAt(new Date());
+        userMapper.updateById(adminUser);
+        // 升级记录(upgradeType='gift')
+        MemberUpgradeRecord record = new MemberUpgradeRecord();
+        record.setUserId(adminUserId);
+        record.setFromLevel(fromLevel);
+        record.setToLevel(levelCode);
+        record.setUpgradeType("gift");
+        record.setExpireTime(expireTime);
+        record.setCreatedAt(new Date());
+        memberUpgradeRecordMapper.insert(record);
+        return true;
+    }
+
     // ==================== 定时任务支持 ====================
 
     /**

+ 82 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/ProductGiftRuleService.java

@@ -0,0 +1,82 @@
+package com.etotem.cfc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.etotem.cfc.common.Result;
+import com.etotem.cfc.entity.MembershipLevel;
+import com.etotem.cfc.entity.ProductGiftRule;
+import com.etotem.cfc.mapper.MembershipLevelMapper;
+import com.etotem.cfc.mapper.ProductGiftRuleMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class ProductGiftRuleService {
+
+    @Resource
+    private ProductGiftRuleMapper mapper;
+
+    @Resource
+    private MembershipLevelMapper levelMapper;
+
+    /** 查询商品启用的赠送规则(一商品最多一条启用) */
+    public ProductGiftRule getActiveByProductId(Long productId) {
+        return mapper.selectOne(new LambdaQueryWrapper<ProductGiftRule>()
+                .eq(ProductGiftRule::getProductId, productId)
+                .eq(ProductGiftRule::getStatus, 1)
+                .last("LIMIT 1"));
+    }
+
+    /** 查询商品全部规则(管理端回显) */
+    public List<ProductGiftRule> listByProduct(Long productId) {
+        return mapper.selectList(new LambdaQueryWrapper<ProductGiftRule>()
+                .eq(ProductGiftRule::getProductId, productId));
+    }
+
+    /** 等级编码 → 等级名(不存在则原样返回) */
+    public String getLevelName(String levelCode) {
+        if (levelCode == null) {
+            return null;
+        }
+        MembershipLevel level = levelMapper.selectOne(new LambdaQueryWrapper<MembershipLevel>()
+                .eq(MembershipLevel::getLevelCode, levelCode));
+        return level != null ? level.getLevelName() : levelCode;
+    }
+
+    /**
+     * 保存赠送规则:先删后插(一商品一启用规则)。
+     * status=1 且 levelCode 有效 → 启用;否则视为停用(清空规则)。
+     */
+    @Transactional
+    public Result<Void> saveRule(Long productId, Integer status, String levelCode) {
+        if (productId == null || status == null) {
+            return Result.error("参数不完整");
+        }
+        boolean enable = (status == 1);
+        if (enable) {
+            if (levelCode == null || levelCode.isEmpty()) {
+                return Result.error("请选择赠送等级");
+            }
+            MembershipLevel level = levelMapper.selectOne(new LambdaQueryWrapper<MembershipLevel>()
+                    .eq(MembershipLevel::getLevelCode, levelCode));
+            if (level == null || "FREE".equals(level.getLevelCode())) {
+                return Result.error("无效的赠送等级");
+            }
+        }
+        mapper.delete(new LambdaQueryWrapper<ProductGiftRule>()
+                .eq(ProductGiftRule::getProductId, productId));
+        if (enable) {
+            ProductGiftRule rule = new ProductGiftRule();
+            rule.setProductId(productId);
+            rule.setLevelCode(levelCode);
+            rule.setStatus(1);
+            rule.setCreatedAt(new Date());
+            rule.setUpdatedAt(new Date());
+            mapper.insert(rule);
+        }
+        return Result.success(null);
+    }
+}

+ 7 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/api/MembershipServiceInterface.java

@@ -29,4 +29,11 @@ public interface MembershipServiceInterface {
 
     /** 应用会员折扣,返回折后价格(分) */
     Integer applyMemberDiscount(Long userId, Integer originalPrice);
+
+    /**
+     * 商品赠送会员:原价购买赠送指定级别会员给家庭创建者。
+     * 返回 true=已赠送/升级,false=跳过(已有同级/更高级、或参数异常)。
+     */
+    boolean grantMembershipByGift(Long familyId, Long userId, String levelCode,
+                                  String sourceOrderNo, String transactionId);
 }

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-1951c26197902d0705a0e19d118e791d9bb437f1
+4904d247e4d98907d5c8a7e450b10f72d91ab096

+ 2 - 2
cfc-web/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "cfc-web",
-  "version": "1.0.760",
+  "version": "1.0.761",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "cfc-web",
-      "version": "1.0.760",
+      "version": "1.0.761",
       "dependencies": {
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^1.0.2",