From 9f04fe0ce397fc91538dca4bec3e70aefcd51838 Mon Sep 17 00:00:00 2001 From: dzq Date: Mon, 21 Jul 2025 08:29:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=99=BA=E8=83=BD=E6=9F=9C):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=BD=92=E8=BF=98=E6=9C=9F=E9=99=90=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=B9=B6=E5=AE=9E=E7=8E=B0=E9=80=BE=E6=9C=9F=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在smart_cabinet表添加return_deadline字段记录归还期限 - 在SmartCabinetEntity和SmartCabinetDTO中添加对应字段 - 新增DeadlineOrderJob定时任务处理逾期订单 - 修改订单商品状态枚举和SQL查询逻辑 - 调整订单商品状态查询条件为不等于2 --- .../service/job/DeadlineOrderJob.java | 155 ++++++++++++++++++ .../smartCabinet/db/SmartCabinetEntity.java | 4 + .../smartCabinet/dto/SmartCabinetDTO.java | 3 + .../shop/order/OrderApplicationService.java | 4 +- .../shop/order/db/ShopOrderGoodsEntity.java | 10 +- .../domain/shop/order/db/ShopOrderMapper.java | 2 +- sql/20250718.sql | 1 + 7 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 agileboot-admin/src/main/java/com/agileboot/admin/customize/service/job/DeadlineOrderJob.java create mode 100644 sql/20250718.sql diff --git a/agileboot-admin/src/main/java/com/agileboot/admin/customize/service/job/DeadlineOrderJob.java b/agileboot-admin/src/main/java/com/agileboot/admin/customize/service/job/DeadlineOrderJob.java new file mode 100644 index 0000000..9642cbc --- /dev/null +++ b/agileboot-admin/src/main/java/com/agileboot/admin/customize/service/job/DeadlineOrderJob.java @@ -0,0 +1,155 @@ +package com.agileboot.admin.customize.service.job; + +import cn.hutool.core.date.DateUtil; +import com.agileboot.domain.cabinet.cell.db.CabinetCellEntity; +import com.agileboot.domain.cabinet.cell.db.CabinetCellService; +import com.agileboot.domain.cabinet.smartCabinet.db.SmartCabinetEntity; +import com.agileboot.domain.cabinet.smartCabinet.db.SmartCabinetService; +import com.agileboot.domain.qywx.authCorpInfo.db.QyAuthCorpInfoEntity; +import com.agileboot.domain.shop.order.db.ShopOrderEntity; +import com.agileboot.domain.shop.order.db.ShopOrderGoodsEntity; +import com.agileboot.domain.shop.order.db.ShopOrderGoodsService; +import com.agileboot.domain.shop.order.db.ShopOrderService; +import com.agileboot.domain.shop.paymentOperationLog.PaymentOperationLogApplicationService; +import com.agileboot.domain.shop.paymentOperationLog.command.AddPaymentOperationLogCommand; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; + +@RequiredArgsConstructor +@Component +@Slf4j +public class DeadlineOrderJob { + private final ShopOrderService shopOrderService; + private final ShopOrderGoodsService shopOrderGoodsService; + private final CabinetCellService cabinetCellService; + private final SmartCabinetService smartCabinetService; + private final PaymentOperationLogApplicationService paymentOperationLogApplicationService; + + /** + * 定时任务:处理超过归还期限的订单商品 + * 每天凌晨3点执行,检查所有处于“借出中”状态的订单商品是否逾期并进行处理。 + */ + @Scheduled(cron = "0 50 * * * *") // 每小时第50分钟执行 + @Transactional // 确保任务的原子性,如果处理过程中出现异常,数据不会被部分更新 + public void markOverdueOrdersAndRemoveFromCabinet() { + log.info("开始执行逾期订单处理及商品下架任务..."); + + try { + // 1. 查询所有处于“正常/借出中”状态且关联了格口ID的订单商品 + // 假设 ShopOrderGoodsEntity.STATUS_NORMAL_BORROWED = 1 + // 并且,只处理借还模式(ShopOrderEntity.mode = 2)的订单商品 + // 仅选择 payStatus 为 2 (已支付) 的订单商品进行处理,因为未支付的订单不应该算作借出。 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ShopOrderGoodsEntity::getStatus, ShopOrderGoodsEntity.STATUS_NORMAL_BORROWED); // Assuming 1 means "borrowed" + queryWrapper.isNotNull(ShopOrderGoodsEntity::getCellId); // 必须在格口中 + + List borrowedGoodsList = shopOrderGoodsService.list(queryWrapper); + + if (CollectionUtils.isEmpty(borrowedGoodsList)) { + log.info("未找到需要检查的借出中商品记录."); + return; + } + + log.info("查找到 {} 条需检查的借出中商品记录.", borrowedGoodsList.size()); + + for (ShopOrderGoodsEntity orderGoods : borrowedGoodsList) { + // 获取关联的订单信息 + ShopOrderEntity order = shopOrderService.getById(orderGoods.getOrderId()); + if (order == null) { + log.warn("订单商品[{}]关联的订单[{}]不存在,跳过处理.", orderGoods.getOrderGoodsId(), orderGoods.getOrderId()); + continue; + } + + // 确保是借还模式 + if (order.getMode() != 0) { // 0-支付模式 + continue; + } + + // 确保订单已支付 + if (order.getPayStatus() != 2) { // 2-已支付 + log.debug("订单[{}]未支付,订单商品[{}]跳过处理.", order.getOrderId(), orderGoods.getOrderGoodsId()); + continue; + } + + // 获取关联的格口信息 + CabinetCellEntity cabinetCell = cabinetCellService.getById(orderGoods.getCellId()); + if (cabinetCell == null) { + log.warn("订单商品[{}]关联的格口[{}]不存在,跳过处理.", orderGoods.getOrderGoodsId(), orderGoods.getCellId()); + continue; + } + + if (!cabinetCell.getStock().equals(0)) { + log.warn("订单商品[{}]关联的格口[{}]库存不为0,跳过处理.", orderGoods.getOrderGoodsId(), cabinetCell.getCellId()); + continue; + } + + // 获取关联的智能柜信息,以获取归还期限 + SmartCabinetEntity smartCabinet = smartCabinetService.getById(cabinetCell.getCabinetId()); + if (smartCabinet == null) { + log.warn("格口[{}]关联的智能柜[{}]不存在,跳过处理.", cabinetCell.getCellId(), cabinetCell.getCabinetId()); + continue; + } + + Integer returnDeadlineDays = smartCabinet.getReturnDeadline(); + + // 检查归还期限是否有效(大于0天) + if (returnDeadlineDays == null || returnDeadlineDays <= 0) { + continue; + } + + Date orderCreateTime = order.getCreateTime(); + if (orderCreateTime == null) { + log.warn("订单[{}]的创建时间为空,无法计算归还期限,订单商品[{}]跳过处理.", order.getOrderId(), orderGoods.getOrderGoodsId()); + continue; + } + + // 计算归还截止日期 + Date deadlineDate = DateUtil.offsetDay(orderCreateTime, returnDeadlineDays); + Date now = new Date(); // 当前时间 + + // 如果当前时间晚于归还截止日期,则商品已逾期 + if (now.after(deadlineDate)) { + log.info("发现逾期订单商品:订单号[{}], 商品ID[{}], 名称[{}]。创建时间[{}], 归还期限[{}天], 截止日期[{}].", + order.getOrderId(), orderGoods.getOrderGoodsId(), orderGoods.getGoodsName(), + DateUtil.formatDateTime(orderCreateTime), returnDeadlineDays, DateUtil.formatDateTime(deadlineDate)); + + // 1. 更新订单商品状态为“已逾期” + orderGoods.setStatus(ShopOrderGoodsEntity.STATUS_OVERDUE); // STATUS_OVERDUE = 7 +// shopOrderGoodsService.updateById(orderGoods); // 更新数据库 + + // 2. 将商品从智能柜格口下架 + cabinetCell.setGoodsId(null); // 清除关联商品ID + cabinetCell.setUsageStatus(1); // 使用状态改为1-空闲 +// cabinetCellService.updateById(cabinetCell); // 更新数据库 + + log.info("成功处理逾期商品:订单商品ID[{}]状态更新为已逾期,并从格口ID[{}]下架。", orderGoods.getOrderGoodsId(), cabinetCell.getCellId()); + + try { + AddPaymentOperationLogCommand paymentOperationLogCommand = new AddPaymentOperationLogCommand(); + paymentOperationLogCommand.setOperationType("DeadlineOrder"); + paymentOperationLogCommand.setStatus(1); + paymentOperationLogCommand.setRemark( + String.format("逾期订单商品:订单号[%s], 商品ID[%s], 名称[%s]。创建时间[%s], 归还期限[%s天], 截止日期[%s].", + order.getOrderId(), orderGoods.getOrderGoodsId(), orderGoods.getGoodsName(), DateUtil.formatDateTime(orderCreateTime), returnDeadlineDays, DateUtil.formatDateTime(deadlineDate))); + paymentOperationLogCommand.initBaseEntity(); + paymentOperationLogApplicationService.addPaymentOperationLog(paymentOperationLogCommand); + } catch (Exception e) { + log.error("处理逾期订单商品[{}]时创建退款操作日志失败.", orderGoods.getOrderGoodsId(), e); + } + } + } + } catch (Exception globalException) { + // 记录整个定时任务的全局错误 + log.error("执行逾期订单处理及商品下架任务时发生全局错误:", globalException); + } + log.info("逾期订单处理及商品下架任务执行完毕."); + } +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/cabinet/smartCabinet/db/SmartCabinetEntity.java b/agileboot-domain/src/main/java/com/agileboot/domain/cabinet/smartCabinet/db/SmartCabinetEntity.java index 5ddf85c..99e6433 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/cabinet/smartCabinet/db/SmartCabinetEntity.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/cabinet/smartCabinet/db/SmartCabinetEntity.java @@ -79,6 +79,10 @@ public class SmartCabinetEntity extends BaseEntity { @TableField("location") private Integer location; + @ApiModelProperty("归还期限(天),0表示不限制") + @TableField("return_deadline") + private Integer returnDeadline; + @ApiModelProperty("已用格口数") @TableField(exist = false) private Integer usedCells; diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/cabinet/smartCabinet/dto/SmartCabinetDTO.java b/agileboot-domain/src/main/java/com/agileboot/domain/cabinet/smartCabinet/dto/SmartCabinetDTO.java index d1d7e7b..e8c0647 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/cabinet/smartCabinet/dto/SmartCabinetDTO.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/cabinet/smartCabinet/dto/SmartCabinetDTO.java @@ -62,6 +62,9 @@ public class SmartCabinetDTO { @ExcelColumn(name = "柜机位置") private Integer location; + @ExcelColumn(name = "归还期限(天),0表示不限制") + private Integer returnDeadline; + @ExcelColumn(name = "已用格口数") private Integer usedCells; diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/OrderApplicationService.java b/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/OrderApplicationService.java index 171a49d..03ee18b 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/OrderApplicationService.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/OrderApplicationService.java @@ -449,7 +449,7 @@ public class OrderApplicationService { if (Integer.valueOf(1).equals(hasReturn)) { orderGoodsQueryWrapper.eq("status", 2); } else if (Integer.valueOf(0).equals(hasReturn)) { - orderGoodsQueryWrapper.eq("status", 1); + orderGoodsQueryWrapper.ne("status", 2); } // 根据查询条件获取订单商品列表 List orderGoods = orderGoodsService.list(orderGoodsQueryWrapper); @@ -524,7 +524,7 @@ public class OrderApplicationService { if (Integer.valueOf(1).equals(hasReturn)) { orderGoodsQueryWrapper.eq("status", 2); } else if (Integer.valueOf(0).equals(hasReturn)) { - orderGoodsQueryWrapper.eq("status", 1); + orderGoodsQueryWrapper.ne("status", 2); } List orderGoods = orderGoodsService.list(orderGoodsQueryWrapper); diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderGoodsEntity.java b/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderGoodsEntity.java index f896cd7..fe27b95 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderGoodsEntity.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderGoodsEntity.java @@ -26,6 +26,14 @@ import lombok.Setter; @ApiModel(value = "ShopOrderGoodsEntity对象", description = "订单商品明细表") public class ShopOrderGoodsEntity extends BaseEntity { + public static final Integer STATUS_NORMAL_BORROWED = 1; + public static final Integer STATUS_RETURNED = 2; + public static final Integer STATUS_EXCHANGED = 3; + public static final Integer STATUS_COMPLETED = 4; + public static final Integer STATUS_UNDER_REVIEW = 5; + public static final Integer STATUS_RETURN_REJECTED = 6; + public static final Integer STATUS_OVERDUE = 7; + private static final long serialVersionUID = 1L; @ApiModelProperty("订单商品唯一ID") @@ -68,7 +76,7 @@ public class ShopOrderGoodsEntity extends BaseEntity { @TableField("cover_img") private String coverImg; - @ApiModelProperty("商品状态(1正常 2已退货 3已换货 4已完成 5审核中 6退货未通过)") + @ApiModelProperty("商品状态(1正常/借出中 2已退货 3已换货 4已完成 5审核中 6退货未通过 7已逾期)") @TableField("status") private Integer status; diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderMapper.java b/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderMapper.java index a2bbf47..060b1d5 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderMapper.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderMapper.java @@ -23,7 +23,7 @@ public interface ShopOrderMapper extends BaseMapper { @Select("SELECT o.*, " + "GROUP_CONCAT(DISTINCT og.goods_name) AS goodsNames, " + "GROUP_CONCAT(DISTINCT og.cover_img) AS coverImgs, " + - "CASE WHEN o.status = 2 AND MAX(CASE WHEN og.status = 1 THEN 1 ELSE 0 END) = 1 THEN 0 ELSE 1 END AS returnStatus " + + "CASE WHEN o.status = 2 AND MAX(CASE WHEN og.status = 2 THEN 0 ELSE 1 END) = 1 THEN 0 ELSE 1 END AS returnStatus " + "FROM shop_order o " + "LEFT JOIN shop_order_goods og ON o.order_id = og.order_id AND og.deleted = 0 " + "LEFT JOIN cabinet_cell cc ON cc.cell_id = og.cell_id " + diff --git a/sql/20250718.sql b/sql/20250718.sql new file mode 100644 index 0000000..7a66eeb --- /dev/null +++ b/sql/20250718.sql @@ -0,0 +1 @@ +ALTER TABLE `smart_cabinet` ADD COLUMN `return_deadline` INT NOT NULL DEFAULT 0 COMMENT '归还期限(天),0表示不限制'; \ No newline at end of file