Commit a8c78f3097f482dd6df4e369f67e51c5a08c31ce

Authored by YunaiV
1 parent 9e99b043

trade:增加自动收货的 job

yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderOperateTypeEnum.java
... ... @@ -15,9 +15,10 @@ public enum TradeOrderOperateTypeEnum {
15 15  
16 16 MEMBER_CREATE(1, "用户下单"),
17 17 MEMBER_RECEIVE(30, "用户已收货"),
18   - MEMBER_COMMENT(31, "用户评价"),
19   - MEMBER_CANCEL(40, "手动取消订单"),
20   - SYSTEM_CANCEL(41, "系统取消订单"),
  18 + SYSTEM_RECEIVE(31, "到期未收货,系统自动确认收货"),
  19 + MEMBER_COMMENT(33, "用户评价"),
  20 + MEMBER_CANCEL(40, "取消订单"),
  21 + SYSTEM_CANCEL(41, "到期未支付,系统自动取消订单"),
21 22 // 42 预留:管理员取消订单
22 23 MEMBER_DELETE(43, "删除订单"),
23 24 ;
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
... ... @@ -110,7 +110,7 @@ public interface TradeOrderConvert {
110 110 createReqDTO.setSubject(subject);
111 111 createReqDTO.setBody(subject); // TODO 芋艿:临时写死
112 112 // 订单相关字段
113   - createReqDTO.setPrice(order.getPayPrice()).setExpireTime(addTime(orderProperties.getExpireTime()));
  113 + createReqDTO.setPrice(order.getPayPrice()).setExpireTime(addTime(orderProperties.getPayExpireTime()));
114 114 return createReqDTO;
115 115 }
116 116  
... ... @@ -180,7 +180,7 @@ public interface TradeOrderConvert {
180 180 TradeOrderProperties tradeOrderProperties,
181 181 DeliveryExpressDO express) {
182 182 AppTradeOrderDetailRespVO orderVO = convert3(order, orderItems);
183   - orderVO.setPayExpireTime(addTime(tradeOrderProperties.getExpireTime()));
  183 + orderVO.setPayExpireTime(addTime(tradeOrderProperties.getPayExpireTime()));
184 184 if (StrUtil.isNotEmpty(order.getPayChannelCode())) {
185 185 orderVO.setPayChannelName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.CHANNEL_CODE, order.getPayChannelCode()));
186 186 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java
... ... @@ -61,10 +61,16 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
61 61 .eq(TradeOrderDO::getUserId, loginUserId));
62 62 }
63 63  
64   - default List<TradeOrderDO> selectListByStatusAndCreateTimeLt(Integer status, LocalDateTime expireTime) {
  64 + default List<TradeOrderDO> selectListByStatusAndCreateTimeLt(Integer status, LocalDateTime createTime) {
65 65 return selectList(new LambdaUpdateWrapper<TradeOrderDO>()
66 66 .eq(TradeOrderDO::getStatus, status)
67   - .lt(TradeOrderDO::getCreateTime, expireTime));
  67 + .lt(TradeOrderDO::getCreateTime, createTime));
  68 + }
  69 +
  70 + default List<TradeOrderDO> selectListByStatusAndDeliveryTimeLt(Integer status, LocalDateTime deliveryTime) {
  71 + return selectList(new LambdaUpdateWrapper<TradeOrderDO>()
  72 + .eq(TradeOrderDO::getStatus, status)
  73 + .lt(TradeOrderDO::getDeliveryTime, deliveryTime));
68 74 }
69 75  
70 76 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java
... ... @@ -28,6 +28,12 @@ public class TradeOrderProperties {
28 28 * 支付超时时间
29 29 */
30 30 @NotNull(message = "支付超时时间不能为空")
31   - private Duration expireTime;
  31 + private Duration payExpireTime;
  32 +
  33 + /**
  34 + * 收货超时时间
  35 + */
  36 + @NotNull(message = "收货超时时间不能为空")
  37 + private Duration receiveExpireTime;
32 38  
33 39 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/job/order/TradeOrderAutoReceiveJob.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.job.order;
  2 +
  3 +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
  4 +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
  5 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
  6 +import org.springframework.stereotype.Component;
  7 +
  8 +import javax.annotation.Resource;
  9 +
  10 +/**
  11 + * 交易订单的自动收货 Job
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Component
  16 +@TenantJob
  17 +public class TradeOrderAutoReceiveJob implements JobHandler {
  18 +
  19 + @Resource
  20 + private TradeOrderUpdateService tradeOrderUpdateService;
  21 +
  22 + @Override
  23 + public String execute(String param) {
  24 + int count = tradeOrderUpdateService.autoReceiveOrder();
  25 + return String.format("自动收货 %s 个", count);
  26 + }
  27 +
  28 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java
... ... @@ -63,7 +63,14 @@ public interface TradeOrderUpdateService {
63 63 void receiveOrder(Long userId, Long id);
64 64  
65 65 /**
66   - * 【会员】取消订单
  66 + * 【系统】自动收货交易订单
  67 + *
  68 + * @return 收货数量
  69 + */
  70 + int autoReceiveOrder();
  71 +
  72 + /**
  73 + * 【会员】取消交易订单
67 74 *
68 75 * @param userId 用户编号
69 76 * @param id 订单编号
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
... ... @@ -492,6 +492,50 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
492 492 // 校验并获得交易订单(可收货)
493 493 TradeOrderDO order = validateOrderReceivable(userId, id);
494 494  
  495 + // 收货订单
  496 + receiveOrder0(order);
  497 + }
  498 +
  499 + @Override
  500 + public int autoReceiveOrder() {
  501 + // 1. 查询过期的待支付订单
  502 + LocalDateTime expireTime = addTime(tradeOrderProperties.getReceiveExpireTime());
  503 + List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndDeliveryTimeLt(
  504 + TradeOrderStatusEnum.DELIVERED.getStatus(), expireTime);
  505 + if (CollUtil.isEmpty(orders)) {
  506 + return 0;
  507 + }
  508 +
  509 + // 2. 遍历执行,逐个取消
  510 + int count = 0;
  511 + for (TradeOrderDO order : orders) {
  512 + try {
  513 + getSelf().autoReceiveOrder(order);
  514 + count ++;
  515 + } catch (Throwable e) {
  516 + log.error("[autoReceiveOrder][order({}) 自动收货订单异常]", order.getId(), e);
  517 + }
  518 + }
  519 + return count;
  520 + }
  521 +
  522 + /**
  523 + * 自动收货单个订单
  524 + *
  525 + * @param order 订单
  526 + */
  527 + @Transactional(rollbackFor = Exception.class)
  528 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_RECEIVE)
  529 + public void autoReceiveOrder(TradeOrderDO order) {
  530 + receiveOrder0(order);
  531 + }
  532 +
  533 + /**
  534 + * 收货订单的核心实现
  535 + *
  536 + * @param order 订单
  537 + */
  538 + private void receiveOrder0(TradeOrderDO order) {
495 539 // 更新 TradeOrderDO 状态为已完成
496 540 int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
497 541 new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(LocalDateTime.now()));
... ... @@ -499,14 +543,137 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
499 543 throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
500 544 }
501 545  
502   - // TODO 芋艿:lili 发送订单变化的消息
  546 + // 插入订单日志
  547 + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
  548 + }
503 549  
504   - // TODO 芋艿:lili 发送商品被购买完成的数据
  550 + /**
  551 + * 校验交易订单满足可售货的条件
  552 + *
  553 + * 1. 交易订单待收货
  554 + *
  555 + * @param userId 用户编号
  556 + * @param id 交易订单编号
  557 + * @return 交易订单
  558 + */
  559 + private TradeOrderDO validateOrderReceivable(Long userId, Long id) {
  560 + // 校验订单是否存在
  561 + TradeOrderDO order = tradeOrderMapper.selectByIdAndUserId(id, userId);
  562 + if (order == null) {
  563 + throw exception(ORDER_NOT_FOUND);
  564 + }
  565 + // 校验订单是否是待收货状态
  566 + if (!TradeOrderStatusEnum.isDelivered(order.getStatus())) {
  567 + throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
  568 + }
  569 + return order;
  570 + }
505 571  
506   - // TODO 芋艿:销售佣金的记录;
  572 + @Override
  573 + @Transactional(rollbackFor = Exception.class)
  574 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
  575 + public void cancelOrder(Long userId, Long id) {
  576 + // 1.1 校验存在
  577 + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
  578 + if (order == null) {
  579 + throw exception(ORDER_NOT_FOUND);
  580 + }
  581 + // 1.2 校验状态
  582 + if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
  583 + throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
  584 + }
507 585  
508   - // 插入订单日志
509   - TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
  586 + // 2. 取消订单
  587 + cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
  588 + }
  589 +
  590 + @Override
  591 + public int autoCancelOrder() {
  592 + // 1. 查询过期的待支付订单
  593 + LocalDateTime expireTime = addTime(tradeOrderProperties.getPayExpireTime());
  594 + List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndCreateTimeLt(
  595 + TradeOrderStatusEnum.UNPAID.getStatus(), expireTime);
  596 + if (CollUtil.isEmpty(orders)) {
  597 + return 0;
  598 + }
  599 +
  600 + // 2. 遍历执行,逐个取消
  601 + int count = 0;
  602 + for (TradeOrderDO order : orders) {
  603 + try {
  604 + getSelf().autoCancelOrder(order);
  605 + count ++;
  606 + } catch (Throwable e) {
  607 + log.error("[autoCancelOrder][order({}) 过期订单异常]", order.getId(), e);
  608 + }
  609 + }
  610 + return count;
  611 + }
  612 +
  613 + /**
  614 + * 自动取消单个订单
  615 + *
  616 + * @param order 订单
  617 + */
  618 + @Transactional(rollbackFor = Exception.class)
  619 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
  620 + public void autoCancelOrder(TradeOrderDO order) {
  621 + cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
  622 + }
  623 +
  624 + /**
  625 + * 取消订单的核心实现
  626 + *
  627 + * @param order 订单
  628 + * @param cancelType 取消类型
  629 + */
  630 + private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
  631 + Long id = order.getId();
  632 + // 1. 更新 TradeOrderDO 状态为已取消
  633 + int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
  634 + new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
  635 + .setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now()));
  636 + if (updateCount == 0) {
  637 + throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
  638 + }
  639 +
  640 + // 2. TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来;回复:从订单里拿呀
  641 + tradeOrderHandlers.forEach(handler -> handler.rollback());
  642 +
  643 + // 3. 回滚库存
  644 + List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
  645 + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
  646 +
  647 + // 4. 回滚优惠券
  648 + if (order.getCouponId() != null && order.getCouponId() > 0) {
  649 + couponApi.returnUsedCoupon(order.getCouponId());
  650 + }
  651 +
  652 + // 5. 回滚积分(抵扣的)
  653 + addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId());
  654 +
  655 + // 6. 增加订单日志
  656 + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
  657 + }
  658 +
  659 + @Override
  660 + @Transactional(rollbackFor = Exception.class)
  661 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_DELETE)
  662 + public void deleteOrder(Long userId, Long id) {
  663 + // 1.1 校验存在
  664 + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
  665 + if (order == null) {
  666 + throw exception(ORDER_NOT_FOUND);
  667 + }
  668 + // 1.2 校验状态
  669 + if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus())) {
  670 + throw exception(ORDER_DELETE_FAIL_STATUS_NOT_CANCEL);
  671 + }
  672 + // 2. 删除订单
  673 + tradeOrderMapper.deleteById(id);
  674 +
  675 + // 3. 记录日志
  676 + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
510 677 }
511 678  
512 679 @Override
... ... @@ -604,28 +771,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
604 771 tradeOrderMapper.updateById(update);
605 772 }
606 773  
607   - /**
608   - * 校验交易订单满足可售货的条件
609   - *
610   - * 1. 交易订单待收货
611   - *
612   - * @param userId 用户编号
613   - * @param id 交易订单编号
614   - * @return 交易订单
615   - */
616   - private TradeOrderDO validateOrderReceivable(Long userId, Long id) {
617   - // 校验订单是否存在
618   - TradeOrderDO order = tradeOrderMapper.selectByIdAndUserId(id, userId);
619   - if (order == null) {
620   - throw exception(ORDER_NOT_FOUND);
621   - }
622   - // 校验订单是否是待收货状态
623   - if (!TradeOrderStatusEnum.isDelivered(order.getStatus())) {
624   - throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
625   - }
626   - return order;
627   - }
628   -
629 774 // =================== Order Item ===================
630 775  
631 776 @Override
... ... @@ -723,113 +868,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
723 868 return comment;
724 869 }
725 870  
726   - @Override
727   - @Transactional(rollbackFor = Exception.class)
728   - @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
729   - public void cancelOrder(Long userId, Long id) {
730   - // 1.1 校验存在
731   - TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
732   - if (order == null) {
733   - throw exception(ORDER_NOT_FOUND);
734   - }
735   - // 1.2 校验状态
736   - if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
737   - throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
738   - }
739   -
740   - // 2. 取消订单
741   - cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
742   - }
743   -
744   - @Override
745   - public int autoCancelOrder() {
746   - // 1. 查询过期的待支付订单
747   - LocalDateTime expireTime = addTime(tradeOrderProperties.getExpireTime());
748   - List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndCreateTimeLt(
749   - TradeOrderStatusEnum.UNPAID.getStatus(), expireTime);
750   - if (CollUtil.isEmpty(orders)) {
751   - return 0;
752   - }
753   -
754   - // 2. 遍历执行,逐个取消
755   - int count = 0;
756   - for (TradeOrderDO order : orders) {
757   - try {
758   - getSelf().autoCancelOrder(order);
759   - count ++;
760   - } catch (Throwable e) {
761   - log.error("[autoCancelOrder][order({}) 过期订单异常]", order.getId(), e);
762   - }
763   - }
764   - return count;
765   - }
766   -
767   - /**
768   - * 自动取消单个订单
769   - *
770   - * @param order 订单
771   - */
772   - @Transactional(rollbackFor = Exception.class)
773   - @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
774   - public void autoCancelOrder(TradeOrderDO order) {
775   - cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
776   - }
777   -
778   - /**
779   - * 取消订单的核心实现
780   - *
781   - * @param order 订单
782   - * @param cancelType 取消类型
783   - */
784   - private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
785   - Long id = order.getId();
786   - // 1. 更新 TradeOrderDO 状态为已取消
787   - int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
788   - new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
789   - .setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now()));
790   - if (updateCount == 0) {
791   - throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
792   - }
793   -
794   - // 2. TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来;回复:从订单里拿呀
795   - tradeOrderHandlers.forEach(handler -> handler.rollback());
796   -
797   - // 3. 回滚库存
798   - List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
799   - productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
800   -
801   - // 4. 回滚优惠券
802   - if (order.getCouponId() != null && order.getCouponId() > 0) {
803   - couponApi.returnUsedCoupon(order.getCouponId());
804   - }
805   -
806   - // 5. 回滚积分(抵扣的)
807   - addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId());
808   -
809   - // 6. 增加订单日志
810   - TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
811   - }
812   -
813   - @Override
814   - @Transactional(rollbackFor = Exception.class)
815   - @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_DELETE)
816   - public void deleteOrder(Long userId, Long id) {
817   - // 1.1 校验存在
818   - TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
819   - if (order == null) {
820   - throw exception(ORDER_NOT_FOUND);
821   - }
822   - // 1.2 校验状态
823   - if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus())) {
824   - throw exception(ORDER_DELETE_FAIL_STATUS_NOT_CANCEL);
825   - }
826   - // 2. 删除订单
827   - tradeOrderMapper.deleteById(id);
828   -
829   - // 3. 记录日志
830   - TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
831   - }
832   -
833 871 /**
834 872 * 判断指定订单的所有订单项,是不是都售后成功
835 873 *
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceTest.java
... ... @@ -87,7 +87,7 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest {
87 87 @BeforeEach
88 88 public void setUp() {
89 89 when(tradeOrderProperties.getAppId()).thenReturn(888L);
90   - when(tradeOrderProperties.getExpireTime()).thenReturn(Duration.ofDays(1));
  90 + when(tradeOrderProperties.getPayExpireTime()).thenReturn(Duration.ofDays(1));
91 91 }
92 92  
93 93 @Test
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -203,7 +203,8 @@ yudao:
203 203 trade:
204 204 order:
205 205 app-id: 1 # 商户编号
206   - expire-time: 2h # 支付的过期时间
  206 + pay-expire-time: 2h # 支付的过期时间
  207 + receive-expire-time: 14d # 收货的过期时间
207 208 express:
208 209 client: kd_niao
209 210 kd-niao:
... ...