|
@@ -57,6 +57,9 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
|
|
|
/** AI workflow 单次排期最多重试次数(应对模型思考被截断、未吐出 JSON 的情况) */
|
|
/** AI workflow 单次排期最多重试次数(应对模型思考被截断、未吐出 JSON 的情况) */
|
|
|
private static final int WORKFLOW_MAX_RETRY = 3;
|
|
private static final int WORKFLOW_MAX_RETRY = 3;
|
|
|
|
|
|
|
|
|
|
+ /** 重新发送邮件时,若预计发送时间已过期,顺延的分钟数 */
|
|
|
|
|
+ private static final int RESEND_DELAY_MINUTES = 5;
|
|
|
|
|
+
|
|
|
@Resource
|
|
@Resource
|
|
|
private CustomerBaseService customerBaseService;
|
|
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
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@@ -397,6 +403,9 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
|
|
|
if (dto == null) {
|
|
if (dto == null) {
|
|
|
return Result.error("请求体不能为空");
|
|
return Result.error("请求体不能为空");
|
|
|
}
|
|
}
|
|
|
|
|
+ if (ObjectUtils.isEmpty(dto.getStatus())) {
|
|
|
|
|
+ return Result.error("status 不能为空");
|
|
|
|
|
+ }
|
|
|
List<Long> ids = new ArrayList<>();
|
|
List<Long> ids = new ArrayList<>();
|
|
|
if (!ObjectUtils.isEmpty(dto.getId())) {
|
|
if (!ObjectUtils.isEmpty(dto.getId())) {
|
|
|
ids.add(dto.getId());
|
|
ids.add(dto.getId());
|
|
@@ -408,20 +417,90 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
|
|
|
return Result.error("id 或 idList 不能为空");
|
|
return Result.error("id 或 idList 不能为空");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 重新发送:-1(待发送),转 resend 单独处理时间校正
|
|
|
|
|
+ if (Integer.valueOf(-1).equals(dto.getStatus())) {
|
|
|
|
|
+ return resend(ids);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
LambdaUpdateWrapper<MarketEmailsEntity> wrapper = new LambdaUpdateWrapper<>();
|
|
LambdaUpdateWrapper<MarketEmailsEntity> wrapper = new LambdaUpdateWrapper<>();
|
|
|
wrapper.in(MarketEmailsEntity::getId, ids);
|
|
wrapper.in(MarketEmailsEntity::getId, ids);
|
|
|
wrapper.eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
|
|
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);
|
|
wrapper.ne(MarketEmailsEntity::getStatus, 1);
|
|
|
}
|
|
}
|
|
|
- // status = -3 表示取消发送
|
|
|
|
|
-// wrapper.set(MarketEmailsEntity::getStatus, -3);
|
|
|
|
|
wrapper.set(MarketEmailsEntity::getStatus, dto.getStatus());
|
|
wrapper.set(MarketEmailsEntity::getStatus, dto.getStatus());
|
|
|
boolean ok = this.update(wrapper);
|
|
boolean ok = this.update(wrapper);
|
|
|
if (!ok) {
|
|
if (!ok) {
|
|
|
return Result.error("操作失败");
|
|
return Result.error("操作失败");
|
|
|
}
|
|
}
|
|
|
- //如果是重发,判断时间如果小于当前时间,直接发送
|
|
|
|
|
|
|
+ return Result.ok();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 已取消邮件重新发送:status -3(已取消)→ -1(待发送)
|
|
|
|
|
+ *
|
|
|
|
|
+ * 只有 status = -3 的记录会被重发,其余状态(含已发送)直接忽略。
|
|
|
|
|
+ * 预计发送时间校正规则:
|
|
|
|
|
+ * <ol>
|
|
|
|
|
+ * <li>expectSendTime 为空,或 expectSendTime <= 当前时间(已过期)→ 顺延为「当前时间 +5 分钟」,
|
|
|
|
|
+ * 留出缓冲,避免定时任务({@code MarketEmailsSendSyncTask},每分钟跑一次)漏掉这一分钟的窗口;</li>
|
|
|
|
|
+ * <li>expectSendTime > 当前时间(未到点)→ 保持原预计发送时间不变。</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();
|
|
return Result.ok();
|
|
|
}
|
|
}
|
|
|
|
|
|