Explorar el Código

feat(backend): PERIODIC会员券每日5:00定时补发任务+listActiveMemberUserIds

Xiaogang Liao hace 1 mes
padre
commit
f3ab81bfac

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

@@ -553,6 +553,26 @@ public class MembershipService implements MembershipServiceInterface {
         return level;
     }
 
+    /**
+     * 查询指定会员等级当前有效的家庭创建者用户ID列表(PERIODIC发券用)
+     */
+    public List<Long> listActiveMemberUserIds(String levelCode) {
+        Date now = new Date();
+        List<FamilyMembership> memberships = membershipMapper.selectList(
+                new LambdaQueryWrapper<FamilyMembership>()
+                        .eq(FamilyMembership::getLevelCode, levelCode)
+                        .eq(FamilyMembership::getPaymentStatus, "paid")
+                        .gt(FamilyMembership::getEndDate, now));
+        List<Long> userIds = new ArrayList<>();
+        for (FamilyMembership m : memberships) {
+            Family family = familyMapper.selectById(m.getFamilyId());
+            if (family != null && family.getCreatorId() != null) {
+                userIds.add(family.getCreatorId());
+            }
+        }
+        return userIds.stream().distinct().collect(Collectors.toList());
+    }
+
     /**
      * 应用会员折扣,返回折后价格(分)
      */

+ 60 - 0
cfc-backend/src/main/java/com/etotem/cfc/task/CouponGrantTask.java

@@ -0,0 +1,60 @@
+package com.etotem.cfc.task;
+
+import com.etotem.cfc.entity.Coupon;
+import com.etotem.cfc.service.CouponService;
+import com.etotem.cfc.service.MembershipService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+/**
+ * 会员优惠券周期性补发任务
+ * - 每天 5:00 执行,给有效会员按 grant_period 补发 PERIODIC 券
+ */
+@Slf4j
+@Component
+public class CouponGrantTask {
+
+    @Resource
+    private CouponService couponService;
+
+    @Resource
+    private MembershipService membershipService;
+
+    @Scheduled(cron = "0 0 5 * * ?")
+    public void grantPeriodicCoupons() {
+        log.info("开始执行周期性会员券补发任务");
+        try {
+            List<Coupon> templates = couponService.listPeriodicTemplates();
+            if (templates.isEmpty()) {
+                log.info("无PERIODIC券模板,跳过");
+                return;
+            }
+            LocalDate now = LocalDate.now();
+            String monthKey = now.format(DateTimeFormatter.ofPattern("yyyy-MM"));
+            int quarter = (now.getMonthValue() - 1) / 3 + 1;
+            String quarterKey = now.getYear() + "-Q" + quarter;
+
+            for (Coupon c : templates) {
+                String periodKey = "QUARTERLY".equals(c.getGrantPeriod()) ? quarterKey : monthKey;
+                List<Long> userIds = membershipService.listActiveMemberUserIds(c.getGrantLevelCode());
+                int granted = 0;
+                for (Long uid : userIds) {
+                    if (couponService.grant(uid, c.getId(), "PERIODIC", periodKey, null)) {
+                        granted++;
+                    }
+                }
+                log.info("PERIODIC券补发: couponId={}, level={}, period={}, 发放用户数={}",
+                        c.getId(), c.getGrantLevelCode(), periodKey, granted);
+            }
+            log.info("周期性会员券补发任务执行完成");
+        } catch (Exception e) {
+            log.error("周期性会员券补发任务执行失败", e);
+        }
+    }
+}