YPZ 6 giorni fa
parent
commit
07ae7c0246

+ 1 - 1
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/controller/MarketEmailsController.java

@@ -53,7 +53,7 @@ public class MarketEmailsController {
     }
 
     @PostMapping("/cancel")
-    @ApiOperation("营销: 取消/重新发送邮件(按 id 或 idList,status 置 -2)")
+    @ApiOperation("营销: 取消发送 / 重新发送邮件(按 id 或 idList;status=-3 取消,status=-1 已取消邮件重发,重发时预计发送时间已过期则顺延至当前时间+5分钟)")
     public Result<Object> cancel(@RequestBody MarketEmailsDTO marketEmailsDTO) {
         return marketEmailsService.cancel(marketEmailsDTO);
     }

+ 1 - 1
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/dto/MarketEmailsDTO.java

@@ -38,7 +38,7 @@ public class MarketEmailsDTO extends Page {
     @TableField("email_id")
     private Long emailId;
 
-    @ApiModelProperty(value = "发送状态:-2草稿 -1待定时 0发送中 1成功 2失败;收信记录一般为1")
+    @ApiModelProperty(value = "发送状态:-3取消 -2草案 -1待发送 0发送中 1已发送 2发送失败;取消/重发接口由前端指定目标状态")
     @TableField("status")
     private Integer status;
 

+ 5 - 1
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/MarketEmailsService.java

@@ -38,7 +38,11 @@ public interface MarketEmailsService extends MyBaseService<MarketEmailsEntity> {
     Result<Object> suspend(MarketEmailsDTO marketEmailsDTO);
 
     /**
-     * 取消发送邮件(按 id 或 idList,status 置 -2;软取消,不影响 isDelete)
+     * 邮件状态流转(按 id 或 idList;软操作,不影响 isDelete)
+     * status = -3:取消发送(已发送 status=1 不允许取消)
+     * status = -1:已取消邮件重新发送(-3 → -1 待发送),并按预计发送时间校正:
+     *              预计时间为空或已过期(&lt;= 当前时间)→ 改为当前时间 +5 分钟;
+     *              预计时间未到点(&gt; 当前时间)→ 保持原预计发送时间
      */
     Result<Object> cancel(MarketEmailsDTO marketEmailsDTO);
 

+ 85 - 6
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/impl/MarketEmailsServiceImpl.java

@@ -57,6 +57,9 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
     /** AI workflow 单次排期最多重试次数(应对模型思考被截断、未吐出 JSON 的情况) */
     private static final int WORKFLOW_MAX_RETRY = 3;
 
+    /** 重新发送邮件时,若预计发送时间已过期,顺延的分钟数 */
+    private static final int RESEND_DELAY_MINUTES = 5;
+
     @Resource
     private CustomerBaseService customerBaseService;
 
@@ -388,8 +391,11 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
     }
 
     /**
-     * 取消发送邮件:status 置 -3(软取消,不删数据,isDelete 保持 0)
-     * 支持单条(dto.id)或批量(dto.idList)
+     * 邮件状态流转:支持单条(dto.id)或批量(dto.idList)
+     *
+     * status = -3:取消发送(软取消,不删数据,isDelete 保持 0;已发送 status=1 不允许取消)
+     * status = -1:已取消邮件重新发送(status -3 → -1 待发送),并按预计发送时间校正,见 {@link #resend(List)}
+     * 其余 status:沿用原逻辑,直接把 dto.status 写入
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -397,6 +403,9 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
         if (dto == null) {
             return Result.error("请求体不能为空");
         }
+        if (ObjectUtils.isEmpty(dto.getStatus())) {
+            return Result.error("status 不能为空");
+        }
         List<Long> ids = new ArrayList<>();
         if (!ObjectUtils.isEmpty(dto.getId())) {
             ids.add(dto.getId());
@@ -408,20 +417,90 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
             return Result.error("id 或 idList 不能为空");
         }
 
+        // 重新发送:-1(待发送),转 resend 单独处理时间校正
+        if (Integer.valueOf(-1).equals(dto.getStatus())) {
+            return resend(ids);
+        }
+
         LambdaUpdateWrapper<MarketEmailsEntity> wrapper = new LambdaUpdateWrapper<>();
         wrapper.in(MarketEmailsEntity::getId, ids);
         wrapper.eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
-        if (dto.getStatus() == -3){
+        if (Integer.valueOf(-3).equals(dto.getStatus())) {
+            // 已发出的邮件(status=1)不允许再取消
             wrapper.ne(MarketEmailsEntity::getStatus, 1);
         }
-        // status = -3 表示取消发送
-//        wrapper.set(MarketEmailsEntity::getStatus, -3);
         wrapper.set(MarketEmailsEntity::getStatus, dto.getStatus());
         boolean ok = this.update(wrapper);
         if (!ok) {
             return Result.error("操作失败");
         }
-        //如果是重发,判断时间如果小于当前时间,直接发送
+        return Result.ok();
+    }
+
+    /**
+     * 已取消邮件重新发送:status -3(已取消)→ -1(待发送)
+     *
+     * 只有 status = -3 的记录会被重发,其余状态(含已发送)直接忽略。
+     * 预计发送时间校正规则:
+     * <ol>
+     *   <li>expectSendTime 为空,或 expectSendTime &lt;= 当前时间(已过期)→ 顺延为「当前时间 +5 分钟」,
+     *       留出缓冲,避免定时任务({@code MarketEmailsSendSyncTask},每分钟跑一次)漏掉这一分钟的窗口;</li>
+     *   <li>expectSendTime &gt; 当前时间(未到点)→ 保持原预计发送时间不变。</li>
+     * </ol>
+     * 更新后由定时任务按 expect_send_time 到达对应分钟时自动投递。
+     */
+    private Result<Object> resend(List<Long> ids) {
+        LambdaQueryWrapper<MarketEmailsEntity> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.in(MarketEmailsEntity::getId, ids);
+        queryWrapper.eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
+        queryWrapper.eq(MarketEmailsEntity::getStatus, -3);
+        queryWrapper.select(MarketEmailsEntity::getId, MarketEmailsEntity::getExpectSendTime);
+        List<MarketEmailsEntity> cancelledList = this.list(queryWrapper);
+        if (CollectionUtils.isEmpty(cancelledList)) {
+            return Result.error("没有可重新发送的邮件(仅已取消的邮件可重发)");
+        }
+
+        Date now = new Date();
+        Date fiveMinutesLater = DateUtils.addMinutes(now, RESEND_DELAY_MINUTES);
+
+        // 预计发送时间已过期(或为空)→ 顺延到 5 分钟后
+        List<Long> expiredIdList = new ArrayList<>();
+        // 预计发送时间仍在未来 → 保持原时间
+        List<Long> pendingIdList = new ArrayList<>();
+        for (MarketEmailsEntity email : cancelledList) {
+            Date expectSendTime = email.getExpectSendTime();
+            if (expectSendTime == null || !expectSendTime.after(now)) {
+                expiredIdList.add(email.getId());
+            } else {
+                pendingIdList.add(email.getId());
+            }
+        }
+
+        // 过期/无预计时间的:改时间为 5 分钟后
+        if (!CollectionUtils.isEmpty(expiredIdList)) {
+            LambdaUpdateWrapper<MarketEmailsEntity> expiredWrapper = new LambdaUpdateWrapper<>();
+            expiredWrapper.in(MarketEmailsEntity::getId, expiredIdList);
+            expiredWrapper.eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
+            expiredWrapper.eq(MarketEmailsEntity::getStatus, -3);
+            expiredWrapper.set(MarketEmailsEntity::getStatus, -1);
+            expiredWrapper.set(MarketEmailsEntity::getExpectSendTime, fiveMinutesLater);
+            if (!this.update(expiredWrapper)) {
+                return Result.error("重新发送失败");
+            }
+        }
+        // 未到点的:按原预计发送时间发送
+        if (!CollectionUtils.isEmpty(pendingIdList)) {
+            LambdaUpdateWrapper<MarketEmailsEntity> pendingWrapper = new LambdaUpdateWrapper<>();
+            pendingWrapper.in(MarketEmailsEntity::getId, pendingIdList);
+            pendingWrapper.eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
+            pendingWrapper.eq(MarketEmailsEntity::getStatus, -3);
+            pendingWrapper.set(MarketEmailsEntity::getStatus, -1);
+            if (!this.update(pendingWrapper)) {
+                return Result.error("重新发送失败");
+            }
+        }
+        log.info("[resend] 重发完成,顺延5分钟={}条,按原预计时间={}条,新发送时间={}",
+                expiredIdList.size(), pendingIdList.size(), fiveMinutesLater);
         return Result.ok();
     }