diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/WeComShareLinkLogisticsJobController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/WeComShareLinkLogisticsJobController.java index fb5bf49..22c21a9 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/WeComShareLinkLogisticsJobController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/WeComShareLinkLogisticsJobController.java @@ -9,6 +9,7 @@ import com.ruoyi.jarvis.service.ILogisticsService; import com.ruoyi.jarvis.service.IWeComShareLinkLogisticsJobService; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -70,6 +71,9 @@ public class WeComShareLinkLogisticsJobController extends BaseController { if (job == null) { return AjaxResult.error("任务不存在"); } + if ("CANCELLED".equalsIgnoreCase(job.getStatus())) { + return AjaxResult.error("任务已取消扫描,请先恢复或新建任务"); + } if (!StringUtils.hasText(job.getTrackingUrl())) { return AjaxResult.error("该任务无物流短链"); } @@ -109,4 +113,49 @@ public class WeComShareLinkLogisticsJobController extends BaseController { r.put("hint", "为单次弹栈处理条数;每项内部仍可能因未出单重新入队"); return AjaxResult.success(r); } + + /** + * 订单取消等:标记为 CANCELLED,不再被定时对账入队;队列弹出时也会跳过物流请求与推送。 + */ + @PreAuthorize("@ss.hasPermi('jarvis:wecom:shareLinkLog:list')") + @PostMapping("/cancel") + public AjaxResult cancel(@RequestBody Map body) { + if (body == null || body.get("jobKey") == null) { + return AjaxResult.error("jobKey 不能为空"); + } + String jobKey = body.get("jobKey").toString().trim(); + if (!StringUtils.hasText(jobKey)) { + return AjaxResult.error("jobKey 不能为空"); + } + WeComShareLinkLogisticsJob job = weComShareLinkLogisticsJobService.selectByJobKey(jobKey); + if (job == null) { + return AjaxResult.error("任务不存在"); + } + if ("CANCELLED".equalsIgnoreCase(job.getStatus())) { + return AjaxResult.success("已是取消状态"); + } + String extra = body.get("lastNote") != null ? body.get("lastNote").toString().trim() : ""; + String note = "manual_cancel"; + if (StringUtils.hasText(extra)) { + note = note + "|" + extra; + } + if (note.length() > 500) { + note = note.substring(0, 500) + "…"; + } + weComShareLinkLogisticsJobMapper.updateByJobKey(jobKey, "CANCELLED", note, null, null); + return AjaxResult.success(); + } + + /** + * 物理删除任务行(Redis 中已存在的同 jobKey 队列项仍可能被弹出,但会因库中无行而跳过扫描)。 + */ + @PreAuthorize("@ss.hasPermi('jarvis:wecom:shareLinkLog:list')") + @DeleteMapping("/{jobKey}") + public AjaxResult remove(@PathVariable("jobKey") String jobKey) { + if (!StringUtils.hasText(jobKey)) { + return AjaxResult.error("jobKey 不能为空"); + } + weComShareLinkLogisticsJobMapper.deleteByJobKey(jobKey.trim()); + return AjaxResult.success(); + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/WeComShareLinkLogisticsJob.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/WeComShareLinkLogisticsJob.java index 0570a91..9fd3d5d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/WeComShareLinkLogisticsJob.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/WeComShareLinkLogisticsJob.java @@ -4,7 +4,8 @@ import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.core.domain.BaseEntity; /** - * 企微分享链物流任务 wecom_share_link_logistics_job + * 企微分享链物流任务 wecom_share_link_logistics_job。 + * 状态含 CANCELLED:不再参与对账入队与队列扫描(订单取消等场景)。 */ public class WeComShareLinkLogisticsJob extends BaseEntity { diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/WeComShareLinkLogisticsJobMapper.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/WeComShareLinkLogisticsJobMapper.java index b00b5e5..d27aae6 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/WeComShareLinkLogisticsJobMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/WeComShareLinkLogisticsJobMapper.java @@ -25,4 +25,6 @@ public interface WeComShareLinkLogisticsJobMapper { * 仅 {@code create_time} 在最近一个月内的记录,避免扫到过旧历史。 */ List selectJobsNeedingQueueReconcile(@Param("limit") int limit); + + int deleteByJobKey(@Param("jobKey") String jobKey); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java index 1d92393..6ee3639 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java @@ -472,6 +472,11 @@ public class LogisticsServiceImpl implements ILogisticsService { logger.warn("adhoc 入队跳过:jobKey 或 trackingUrl 为空"); return; } + WeComShareLinkLogisticsJob row = weComShareLinkLogisticsJobMapper.selectByJobKey(job.getJobKey().trim()); + if (row != null && "CANCELLED".equalsIgnoreCase(row.getStatus())) { + logger.info("adhoc 入队跳过:任务已取消 jobKey={}", job.getJobKey()); + return; + } int attempts = job.getScanAttempts() != null ? job.getScanAttempts() : 0; rightPushAdhocQueueJson(job.getJobKey().trim(), attempts, job.getTrackingUrl().trim(), job.getUserRemark(), job.getTouserPush(), job.getFromUserName()); @@ -561,6 +566,13 @@ public class LogisticsServiceImpl implements ILogisticsService { String touser = o.getString("touser"); String jobKey = o.getString("jobKey"); int attempts = o.getIntValue("attempts"); + if (StringUtils.hasText(jobKey)) { + WeComShareLinkLogisticsJob row = weComShareLinkLogisticsJobMapper.selectByJobKey(jobKey.trim()); + if (row == null || "CANCELLED".equalsIgnoreCase(row.getStatus())) { + logger.info("adhoc 队列项跳过(任务已删除或已取消扫描) jobKey={} rowNull={}", jobKey, row == null); + continue; + } + } AdhocTryResult tr = tryAdhocShareLinkOnce(url, remark, touser, null); if (tr.needsRequeue) { int nextAttempts = attempts + 1; diff --git a/ruoyi-system/src/main/resources/mapper/jarvis/WeComShareLinkLogisticsJobMapper.xml b/ruoyi-system/src/main/resources/mapper/jarvis/WeComShareLinkLogisticsJobMapper.xml index d7890b1..d98b3be 100644 --- a/ruoyi-system/src/main/resources/mapper/jarvis/WeComShareLinkLogisticsJobMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/jarvis/WeComShareLinkLogisticsJobMapper.xml @@ -78,4 +78,8 @@ order by id asc limit #{limit} + + + delete from wecom_share_link_logistics_job where job_key = #{jobKey} + diff --git a/sql/wecom_share_link_logistics_job.sql b/sql/wecom_share_link_logistics_job.sql index e82bfab..b63921c 100644 --- a/sql/wecom_share_link_logistics_job.sql +++ b/sql/wecom_share_link_logistics_job.sql @@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS `wecom_share_link_logistics_job` ( `tracking_url` varchar(768) NOT NULL COMMENT '3.cn 物流短链', `remark` mediumtext COMMENT '用户备注', `touser_push` varchar(512) DEFAULT NULL COMMENT '解析后的推送接收人(企微成员 UserID,多个逗号分隔)', - `status` varchar(32) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/WAITING/PUSHED/ABANDONED/IMPORTED', + `status` varchar(32) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/WAITING/PUSHED/ABANDONED/IMPORTED/CANCELLED', `waybill_no` varchar(128) DEFAULT NULL COMMENT '成功解析并推送后的运单号', `scan_attempts` int(11) NOT NULL DEFAULT 0 COMMENT '已扫描次数(含重新入队)', `last_note` varchar(512) DEFAULT NULL COMMENT '最近一次处理说明',