YPZ 1 місяць тому
батько
коміт
7008accca0

+ 6 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/MarketingCampaignService.java

@@ -24,4 +24,10 @@ public interface MarketingCampaignService extends MyBaseService<MarketingCampaig
 
     Result<Object> getDashboardStats();
 
+    /**
+     * 按开始/结束日期同步营销活动的生命周期状态(每天定时任务调用)。
+     * 方法内部会打印本次被变更的活动:哪些改为开始(运行中)、哪些改为结束(完成)。
+     */
+    void syncStartEndStatus();
+
 }

+ 11 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/impl/MarketEmailsServiceImpl.java

@@ -705,9 +705,20 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
         if (!ObjectUtils.isEmpty(marketEmailsCustomerVO.getId())){
             queryWrapper.eq(MarketEmailsEntity::getId, marketEmailsCustomerVO.getId());
         }
+        if (!CollectionUtils.isEmpty(marketEmailsCustomerVO.getMarketingCampaignIdList())){
+            queryWrapper.in(MarketEmailsEntity::getMarketingCampaignId, marketEmailsCustomerVO.getMarketingCampaignIdList());
+        }
+        if (!CollectionUtils.isEmpty(marketEmailsCustomerVO.getCustomerIdList())){
+            queryWrapper.in(MarketEmailsEntity::getCustomerId, marketEmailsCustomerVO.getCustomerIdList());
+        }
         queryWrapper.eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
         List<MarketEmailsEntity> entityList = this.list(queryWrapper);
+        List<Long> idList = entityList.stream().map(MarketEmailsEntity::getId).collect(Collectors.toList());
         List<Long> emailIdList = entityList.stream().map(MarketEmailsEntity::getEmailId).collect(Collectors.toList());
+        LambdaUpdateWrapper<MarketEmailsEntity> markerEmailUpdateWrapper = new LambdaUpdateWrapper<>();
+        markerEmailUpdateWrapper.in(MarketEmailsEntity::getId, idList);
+        markerEmailUpdateWrapper.set(MarketEmailsEntity::getStatus, marketEmailsCustomerVO.getStatus());
+        this.update(null, markerEmailUpdateWrapper);
 
         if (!CollectionUtils.isEmpty(emailIdList) && !ObjectUtils.isEmpty(marketEmailsCustomerVO.getStatus())){
             LambdaUpdateWrapper<MktMailStatusEntity> updateWrapper = new LambdaUpdateWrapper<>();

+ 110 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/impl/MarketingCampaignServiceImpl.java

@@ -13,12 +13,14 @@ import com.storlead.trade.entity.*;
 import com.storlead.trade.mapper.MarketingCampaignEntityMapper;
 import com.storlead.trade.service.*;
 import com.storlead.trade.vo.CampaignStatsVO;
+import com.storlead.trade.vo.MarketEmailsCustomerVO;
 import com.storlead.trade.vo.MarketingCampaignDetailVO;
 import com.storlead.user.service.IUserService;
 import com.storlead.trade.vo.MarketingCampaignVO;
 import com.storlead.framework.common.constant.CommonConstant;
 import com.storlead.framework.common.result.Result;
 import com.storlead.trade.service.impl.support.TradeDataSourceServiceImpl;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -34,7 +36,10 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import java.time.LocalDate;
+import java.time.ZoneId;
 
+@Slf4j
 @Service
 @DS(DSConstants.DATASOURCE_TRADE)
 public class MarketingCampaignServiceImpl
@@ -368,6 +373,111 @@ public class MarketingCampaignServiceImpl
         return Result.ok();
     }
 
+    /**
+     * 生命周期状态常量(对应 marketing_campaign.status 注释:0未开始 1运行中 2暂停 3完成)
+     */
+    private static final int STATUS_NOT_STARTED = 0;
+    private static final int STATUS_RUNNING = 1;
+    private static final int STATUS_PAUSED = 2;
+    private static final int STATUS_COMPLETED = 3;
+
+    /**
+     * 按开始/结束日期同步所有营销活动的生命周期状态。
+     * <p>每天 8 点由 {@code MarketStartOrEndTask} 调度调用。</p>
+     * 规则:
+     * <ul>
+     *     <li>今天 &lt; 开始日期 → 未开始(0)</li>
+     *     <li>开始日期 ≤ 今天 ≤ 结束日期 → 运行中(1)</li>
+     *     <li>今天 &gt; 结束日期 → 完成(3)</li>
+     * </ul>
+     * 手动暂停(2)的活动不自动覆盖,尊重人工操作;开始/结束日期任一缺失则跳过。
+     *
+     * @return 被更新的活动数量
+     */
+    @Override
+    public void syncStartEndStatus() {
+        LocalDate today = LocalDate.now(ZoneId.of("Asia/Shanghai"));
+        List<MarketingCampaignEntity> all = this.lambdaQuery()
+                .eq(MarketingCampaignEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .list();
+        if (CollectionUtils.isEmpty(all)) {
+            log.info("营销活动开始/结束状态同步:无未删除活动,跳过");
+            return;
+        }
+        // 按目标状态分组,只收集需要变更的 id(跳过暂停 / 日期缺失 / 状态已一致)
+        // 后续每种状态仅执行一次批量 UPDATE,而非逐条 UPDATE
+        Map<Integer, List<Long>> idsByStatus = new HashMap<>(4);
+        // 变更的实体列表(status 已回填为目标状态,用于区分改为开始/结束)
+        List<MarketingCampaignEntity> changed = new ArrayList<>();
+        for (MarketingCampaignEntity c : all) {
+            Integer expected = computeStartEndStatus(c, today);
+            if (expected == null) {
+                continue;
+            }
+            Integer current = c.getStatus();
+            // 尊重手动暂停,不自动覆盖
+            if (current != null && current == STATUS_PAUSED) {
+                continue;
+            }
+            if (expected.equals(current)) {
+                continue;
+            }
+            idsByStatus.computeIfAbsent(expected, k -> new ArrayList<>()).add(c.getId());
+            c.setStatus(expected);
+            changed.add(c);
+        }
+        // 每种目标状态执行一次批量 UPDATE
+        for (Map.Entry<Integer, List<Long>> entry : idsByStatus.entrySet()) {
+            List<Long> ids = entry.getValue();
+            if (CollectionUtils.isEmpty(ids)) {
+                continue;
+            }
+            this.lambdaUpdate()
+                    .in(MarketingCampaignEntity::getId, ids)
+                    .set(MarketingCampaignEntity::getStatus, entry.getKey())
+                    .update();
+        }
+        // 按目标状态拆分列表并打印:哪些改为开始、哪些改为结束
+        List<Long> started = changed.stream()
+                .filter(e -> STATUS_RUNNING == e.getStatus())
+                .map(MarketingCampaignEntity::getId)
+                .collect(Collectors.toList());
+        List<Long> ended = changed.stream()
+                .filter(e -> STATUS_COMPLETED == e.getStatus())
+                .map(MarketingCampaignEntity::getId)
+                .collect(Collectors.toList());
+        log.info("营销活动开始/结束状态同步完成:改为开始(运行中)={}, 改为结束(完成)={}", started, ended);
+        MarketEmailsCustomerVO startVO = new MarketEmailsCustomerVO();
+        startVO.setMarketingCampaignIdList(started);
+        //修改状态为待发送
+        startVO.setStatus(-1);
+        marketEmailsService.updateCustomerEmailStatus(startVO);
+
+        MarketEmailsCustomerVO endVO = new MarketEmailsCustomerVO();
+        endVO.setMarketingCampaignIdList(ended);
+        //修改状态为待发送
+        endVO.setStatus(-3);
+        marketEmailsService.updateCustomerEmailStatus(endVO);
+    }
+
+    /**
+     * 根据开始/结束日期与今天计算期望的状态值;开始/结束日期任一缺失返回 null。
+     */
+    private Integer computeStartEndStatus(MarketingCampaignEntity c, LocalDate today) {
+        LocalDate begin = c.getBeginData();
+        LocalDate end = c.getEndDate();
+        if (begin == null || end == null) {
+            return null;
+        }
+        if (today.isBefore(begin)) {
+            return STATUS_NOT_STARTED;
+        }
+        if (!today.isAfter(end)) {
+            return STATUS_RUNNING;
+        }
+        return STATUS_COMPLETED;
+    }
+
     @Override
     public Result<Object> getDashboardStats() {
         // 计算本月起止时间

+ 32 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/task/MarketStartOrEndTask.java

@@ -0,0 +1,32 @@
+package com.storlead.trade.task;
+
+import com.storlead.trade.service.CustomerAnalysisDispatchService;
+import com.storlead.trade.service.MarketingCampaignService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 每天 8 点:修改营销活动的开始/结束状态
+ */
+@Slf4j
+@Component
+public class MarketStartOrEndTask {
+
+    @Resource
+    private MarketingCampaignService marketingCampaignService;
+
+    @Scheduled(cron = "0 0 8 * * ?")
+    public void dispatch() {
+        long start = System.currentTimeMillis();
+        log.info("营销活动开始/结束状态同步任务开始");
+        try {
+            marketingCampaignService.syncStartEndStatus();
+            log.info("营销活动开始/结束状态同步任务结束, cost={}ms", System.currentTimeMillis() - start);
+        } catch (Exception e) {
+            log.error("营销活动开始/结束状态同步任务失败", e);
+        }
+    }
+}

+ 10 - 2
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/vo/MarketEmailsCustomerVO.java

@@ -5,6 +5,8 @@ import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.util.List;
+
 @Data
 @ApiModel(value = "MarketEmailsCustomerVO", description = "营销邮件客户信息VO")
 public class MarketEmailsCustomerVO {
@@ -12,15 +14,21 @@ public class MarketEmailsCustomerVO {
     @ApiModelProperty(value = "id")
     private Long id;
 
-    @ApiModelProperty(value = "id")
+    @ApiModelProperty(value = "营销活动Id")
     private Long marketingCampaignId;
 
+    @ApiModelProperty(value = "营销活动IdList")
+    private List<Long> marketingCampaignIdList;
+
     @ApiModelProperty(value = "客户分析结果id")
     private Long analysisResultId;
 
     @ApiModelProperty(value = "客户id")
     private Long customerId;
 
+    @ApiModelProperty(value = "客户idList")
+    private List<Long> customerIdList;
+
     @ApiModelProperty(value = "客户名称")
     private String customerName;
 
@@ -42,6 +50,6 @@ public class MarketEmailsCustomerVO {
     @ApiModelProperty(value = "置信度")
     private Double confidence;
 
-    @ApiModelProperty(value = "发送状态:-3取消 -2草稿 -1待定时 0发送中 1成功 2失败;收信记录一般为1")
+    @ApiModelProperty(value = "发送状态:-3取消 -2草稿 -1定时 0发送中 1成功 2失败;收信记录一般为1")
     private Integer status;
 }