Commit cd2d0b708910d49399599446f2ec5c399234df13

Authored by owen
2 parents 77f77635 cf41347c

Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product

Showing 24 changed files with 323 additions and 188 deletions
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java
... ... @@ -111,17 +111,19 @@ public class ProductSpuRespDTO {
111 111 // ========== 物流相关字段 =========
112 112  
113 113 /**
114   - * 赠送积分
115   - */
116   - private Integer giveIntegral;
117   -
118   - /**
119 114 * 物流配置模板编号
120 115 *
121 116 * 对应 TradeDeliveryExpressTemplateDO 的 id 编号
122 117 */
123 118 private Long deliveryTemplateId;
124 119  
  120 + // ========== 营销相关字段 =========
  121 +
  122 + /**
  123 + * 赠送积分
  124 + */
  125 + private Integer giveIntegral;
  126 +
125 127 // ========== 统计相关字段 =========
126 128  
127 129 /**
... ...
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderOperateTypeEnum.java
... ... @@ -14,10 +14,12 @@ import lombok.RequiredArgsConstructor;
14 14 public enum TradeOrderOperateTypeEnum {
15 15  
16 16 MEMBER_CREATE(1, "用户下单"),
  17 + MEMBER_PAY(20, "用户付款成功"),
17 18 MEMBER_RECEIVE(30, "用户已收货"),
18   - MEMBER_COMMENT(31, "用户评价"),
19   - MEMBER_CANCEL(40, "手动取消订单"),
20   - SYSTEM_CANCEL(41, "系统取消订单"),
  19 + SYSTEM_RECEIVE(31, "到期未收货,系统自动确认收货"),
  20 + MEMBER_COMMENT(33, "用户评价"),
  21 + MEMBER_CANCEL(40, "取消订单"),
  22 + SYSTEM_CANCEL(41, "到期未支付,系统自动取消订单"),
21 23 // 42 预留:管理员取消订单
22 24 MEMBER_DELETE(43, "删除订单"),
23 25 ;
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
... ... @@ -140,7 +140,7 @@ public class AppTradeOrderController {
140 140 @Operation(summary = "确认交易订单收货")
141 141 @Parameter(name = "id", description = "交易订单编号")
142 142 public CommonResult<Boolean> receiveOrder(@RequestParam("id") Long id) {
143   - tradeOrderUpdateService.receiveOrder(getLoginUserId(), id);
  143 + tradeOrderUpdateService.receiveOrderByMember(getLoginUserId(), id);
144 144 return success(true);
145 145 }
146 146  
... ... @@ -148,7 +148,7 @@ public class AppTradeOrderController {
148 148 @Operation(summary = "取消交易订单")
149 149 @Parameter(name = "id", description = "交易订单编号")
150 150 public CommonResult<Boolean> cancelOrder(@RequestParam("id") Long id) {
151   - tradeOrderUpdateService.cancelOrder(getLoginUserId(), id);
  151 + tradeOrderUpdateService.cancelOrderByMember(getLoginUserId(), id);
152 152 return success(true);
153 153 }
154 154  
... ...
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/dataobject/order/TradeOrderItemDO.java
... ... @@ -146,18 +146,20 @@ public class TradeOrderItemDO extends BaseDO {
146 146 private Integer pointPrice;
147 147 /**
148 148 * 使用的积分
  149 + *
  150 + * 目的:用于后续取消或者售后订单时,需要归还赠送
149 151 */
150 152 private Integer usePoint;
151 153 /**
152 154 * 赠送的积分
  155 + *
  156 + * 目的:用于后续取消或者售后订单时,需要扣减赠送
153 157 */
154 158 private Integer givePoint;
155 159 /**
156 160 * VIP 减免金额,单位:分
157 161 */
158 162 private Integer vipPrice;
159   - // TODO @芋艿:如果商品 vip 折扣时,到底是新增一个 vipPrice 记录优惠记录,还是 vipDiscountPrice,记录 vip 的优惠;还是直接使用 vipPrice;
160   - // 目前 crmeb 的选择,单独一个 vipPrice 记录优惠价格;感觉不一定合理,可以在看看有赞的;
161 163  
162 164 // ========== 售后基本信息 ==========
163 165  
... ...
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&lt;TradeOrderDO&gt; {
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/framework/order/core/annotations/TradeOrderLog.java
... ... @@ -2,7 +2,10 @@ package cn.iocoder.yudao.module.trade.framework.order.core.annotations;
2 2  
3 3 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderOperateTypeEnum;
4 4  
5   -import java.lang.annotation.*;
  5 +import java.lang.annotation.Documented;
  6 +import java.lang.annotation.Retention;
  7 +import java.lang.annotation.RetentionPolicy;
  8 +import java.lang.annotation.Target;
6 9  
7 10 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
8 11 import static java.lang.annotation.ElementType.METHOD;
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/core/aop/TradeOrderLogAspect.java
... ... @@ -32,6 +32,16 @@ import static java.util.Collections.emptyMap;
32 32 public class TradeOrderLogAspect {
33 33  
34 34 /**
  35 + * 用户编号
  36 + *
  37 + * 目前的使用场景:支付回调时,需要强制设置下用户编号
  38 + */
  39 + private static final ThreadLocal<Long> USER_ID = new ThreadLocal<>();
  40 + /**
  41 + * 用户类型
  42 + */
  43 + private static final ThreadLocal<Integer> USER_TYPE = new ThreadLocal<>();
  44 + /**
35 45 * 订单编号
36 46 */
37 47 private static final ThreadLocal<Long> ORDER_ID = new ThreadLocal<>();
... ... @@ -112,4 +122,9 @@ public class TradeOrderLogAspect {
112 122 EXTS.set(exts);
113 123 }
114 124  
  125 + public static void setUserInfo(Long userId, Integer userType) {
  126 + USER_ID.set(userId);
  127 + USER_TYPE.set(userType);
  128 + }
  129 +
115 130 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/core/utils/TradeOrderLogUtils.java
... ... @@ -20,4 +20,8 @@ public class TradeOrderLogUtils {
20 20 TradeOrderLogAspect.setOrderInfo(id, beforeStatus, afterStatus, exts);
21 21 }
22 22  
  23 + public static void setUserInfo(Long userId, Integer userType) {
  24 + TradeOrderLogAspect.setUserInfo(userId, userType);
  25 + }
  26 +
23 27 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/job/order/TradeOrderAutoCancelJob.java
... ... @@ -21,7 +21,7 @@ public class TradeOrderAutoCancelJob implements JobHandler {
21 21  
22 22 @Override
23 23 public String execute(String param) {
24   - int count = tradeOrderUpdateService.autoCancelOrder();
  24 + int count = tradeOrderUpdateService.cancelOrderBySystem();
25 25 return String.format("过期订单 %s 个", count);
26 26 }
27 27  
... ...
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.receiveOrderBySystem();
  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
... ... @@ -60,22 +60,29 @@ public interface TradeOrderUpdateService {
60 60 * @param userId 用户编号
61 61 * @param id 订单编号
62 62 */
63   - void receiveOrder(Long userId, Long id);
  63 + void receiveOrderByMember(Long userId, Long id);
64 64  
65 65 /**
66   - * 【会员】取消订单
  66 + * 【系统】自动收货交易订单
  67 + *
  68 + * @return 收货数量
  69 + */
  70 + int receiveOrderBySystem();
  71 +
  72 + /**
  73 + * 【会员】取消交易订单
67 74 *
68 75 * @param userId 用户编号
69 76 * @param id 订单编号
70 77 */
71   - void cancelOrder(Long userId, Long id);
  78 + void cancelOrderByMember(Long userId, Long id);
72 79  
73 80 /**
74 81 * 【系统】自动取消订单
75 82 *
76 83 * @return 取消数量
77 84 */
78   - int autoCancelOrder();
  85 + int cancelOrderBySystem();
79 86  
80 87 /**
81 88 * 【会员】删除订单
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
... ... @@ -8,6 +8,7 @@ import cn.hutool.core.util.StrUtil;
8 8 import cn.hutool.extra.spring.SpringUtil;
9 9 import cn.iocoder.yudao.framework.common.core.KeyValue;
10 10 import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
  11 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
11 12 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
12 13 import cn.iocoder.yudao.module.member.api.address.AddressApi;
13 14 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
... ... @@ -321,38 +322,39 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
321 322  
322 323 @Override
323 324 @Transactional(rollbackFor = Exception.class)
  325 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_PAY)
324 326 public void updateOrderPaid(Long id, Long payOrderId) {
325   - // 校验并获得交易订单(可支付)
  327 + // 1. 校验并获得交易订单(可支付)
326 328 KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPayable(id, payOrderId);
327 329 TradeOrderDO order = orderResult.getKey();
328 330 PayOrderRespDTO payOrder = orderResult.getValue();
329 331  
330   - // 更新 TradeOrderDO 状态为已支付,等待发货
  332 + // 2. 更新 TradeOrderDO 状态为已支付,等待发货
331 333 int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
332 334 new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayStatus(true)
333 335 .setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
334 336 if (updateCount == 0) {
335 337 throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
336 338 }
337   - // 校验活动
  339 +
  340 + // 3. 校验活动
338 341 // 1、拼团活动
339 342 // TODO @puhui999:这块也抽象到 handler 里
340 343 if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
341 344 // 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录
342 345 combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), LocalDateTime.now());
343 346 }
344   - // TODO 芋艿:发送订单变化的消息
345   -
346   - // TODO 芋艿:发送站内信
347 347  
348   - // TODO 芋艿:OrderLog
349   -
350   - // 增加用户积分(赠送)
351   - addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_REWARD, order.getId());
352   - // 增加用户经验
  348 + // 4.1 增加用户积分(赠送)
  349 + addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE, order.getId());
  350 + // 4.2 增加用户经验
353 351 getSelf().addUserExperienceAsync(order.getUserId(), order.getPayPrice(), order.getId());
354   - // 增加用户佣金
  352 + // 4.3 增加用户佣金
355 353 getSelf().addBrokerageAsync(order.getUserId(), order.getId());
  354 +
  355 + // 5. 记录订单日志
  356 + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
  357 + TradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue());
356 358 }
357 359  
358 360 /**
... ... @@ -435,8 +437,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
435 437 throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
436 438 }
437 439  
438   - // TODO 芋艿:发送订单变化的消息
439   -
440 440 // 发送站内信
441 441 tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqBO().setOrderId(order.getId())
442 442 .setUserId(order.getUserId()).setMessage(null));
... ... @@ -488,10 +488,54 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
488 488 @Override
489 489 @Transactional(rollbackFor = Exception.class)
490 490 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_RECEIVE)
491   - public void receiveOrder(Long userId, Long id) {
  491 + public void receiveOrderByMember(Long userId, Long id) {
492 492 // 校验并获得交易订单(可收货)
493 493 TradeOrderDO order = validateOrderReceivable(userId, id);
494 494  
  495 + // 收货订单
  496 + receiveOrder0(order);
  497 + }
  498 +
  499 + @Override
  500 + public int receiveOrderBySystem() {
  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().receiveOrderBySystem(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 receiveOrderBySystem(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 + }
  549 +
  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 + }
  571 +
  572 + @Override
  573 + @Transactional(rollbackFor = Exception.class)
  574 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
  575 + public void cancelOrderByMember(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 + }
  585 +
  586 + // 2. 取消订单
  587 + cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
  588 + }
  589 +
  590 + @Override
  591 + public int cancelOrderBySystem() {
  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 + }
503 599  
504   - // TODO 芋艿:lili 发送商品被购买完成的数据
  600 + // 2. 遍历执行,逐个取消
  601 + int count = 0;
  602 + for (TradeOrderDO order : orders) {
  603 + try {
  604 + getSelf().cancelOrderBySystem(order);
  605 + count ++;
  606 + } catch (Throwable e) {
  607 + log.error("[autoCancelOrder][order({}) 过期订单异常]", order.getId(), e);
  608 + }
  609 + }
  610 + return count;
  611 + }
505 612  
506   - // TODO 芋艿:销售佣金的记录;
  613 + /**
  614 + * 自动取消单个订单
  615 + *
  616 + * @param order 订单
  617 + */
  618 + @Transactional(rollbackFor = Exception.class)
  619 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
  620 + public void cancelOrderBySystem(TradeOrderDO order) {
  621 + cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
  622 + }
507 623  
508   - // 插入订单日志
509   - TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
  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,33 +771,13 @@ 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  
  776 + // TODO 疯狂:帮我重构下:
  777 + // 1. updateOrderItemAfterSaleStatus 拆分成三个方法:发起;同意;拒绝。原因是,职责更清晰,操作日志也更容易记录;
  778 +
631 779 @Override
632 780 @Transactional(rollbackFor = Exception.class)
633   - // TODO 芋艿:
634 781 public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
635 782 Long afterSaleId, Integer refundPrice) {
636 783 // 如果退款成功,则 refundPrice 非空
... ... @@ -658,6 +805,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
658 805 TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
659 806 TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
660 807 Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
  808 + // TODO @疯狂:809 到 817 改成:cancelOrderByAfterSale:相当于全部售后成功后,就是要取消胆子;
661 809 if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单
662 810 tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
663 811 .setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus()).setRefundPrice(orderRefundPrice).setRefundPoint(order.getRefundPoint() + orderItem.getUsePoint())
... ... @@ -667,7 +815,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
667 815  
668 816 // TODO 芋艿:要不要退优惠劵
669 817  
670   - // TODO 芋艿:站内信?
671 818 } else { // 如果部分售后,则更新退款金额
672 819 tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
673 820 .setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice));
... ... @@ -676,6 +823,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
676 823 // TODO 芋艿:这块扣减规则,需要在考虑下
677 824 // 售后成功后,执行数据回滚逻辑
678 825 if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) {
  826 + // TODO @疯狂:这里库存也要扣减下;
679 827 // 扣减用户积分(赠送的)
680 828 reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, afterSaleId);
681 829 // 增加用户积分(返还抵扣)
... ... @@ -723,113 +871,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
723 871 return comment;
724 872 }
725 873  
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 874 /**
834 875 * 判断指定订单的所有订单项,是不是都售后成功
835 876 *
... ... @@ -842,6 +883,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
842 883 TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus()));
843 884 }
844 885  
  886 + // =================== 营销相关的操作 ===================
  887 +
845 888 @Async
846 889 protected void addUserExperienceAsync(Long userId, Integer payPrice, Long orderId) {
847 890 int bizType = MemberExperienceBizTypeEnum.ORDER.getType();
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointGiveCalculator.java
... ... @@ -16,6 +16,7 @@ import java.util.Optional;
16 16  
17 17 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
18 18  
  19 +// TODO @疯狂:这个可以搞个单测;
19 20 /**
20 21 * 赠送积分的 {@link TradePriceCalculator} 实现类
21 22 *
... ... @@ -25,6 +26,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
25 26 @Order(TradePriceCalculator.ORDER_POINT_GIVE)
26 27 @Slf4j
27 28 public class TradePointGiveCalculator implements TradePriceCalculator {
  29 +
28 30 @Resource
29 31 private MemberPointApi memberPointApi;
30 32  
... ... @@ -54,9 +56,10 @@ public class TradePointGiveCalculator implements TradePriceCalculator {
54 56 TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i);
55 57 // 商品可能赠送了积分,所以这里要加上
56 58 orderItem.setGivePoint(orderItem.getGivePoint() + dividePoints.get(i));
57   - TradePriceCalculatorHelper.recountPayPrice(orderItem);
  59 + TradePriceCalculatorHelper.recountPayPrice(orderItem); // TODO @疯狂:这个应该不用调用哇?不影响支付金额
58 60 }
59 61 // 3.3 更新订单赠送积分
60 62 TradePriceCalculatorHelper.recountAllGivePoint(result);
61 63 }
  64 +
62 65 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointUsePriceCalculator.java
... ... @@ -20,6 +20,7 @@ import java.util.List;
20 20  
21 21 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
22 22  
  23 +// TODO @疯狂:搞个单测,嘿嘿;
23 24 /**
24 25 * 使用积分的 {@link TradePriceCalculator} 实现类
25 26 *
... ... @@ -29,6 +30,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
29 30 @Order(TradePriceCalculator.ORDER_POINT_USE)
30 31 @Slf4j
31 32 public class TradePointUsePriceCalculator implements TradePriceCalculator {
  33 +
32 34 @Resource
33 35 private MemberPointApi memberPointApi;
34 36 @Resource
... ... @@ -48,13 +50,14 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
48 50 }
49 51 // 1.3 校验用户积分余额
50 52 MemberUserRespDTO user = memberUserApi.getUser(param.getUserId());
51   - if (user.getPoint() == null || user.getPoint() < 0) {
  53 + if (user.getPoint() == null || user.getPoint() <= 0) {
52 54 return;
53 55 }
54 56  
55 57 // 2.1 计算积分优惠金额
  58 + // TODO @疯狂:如果计算出来,优惠金额为 0,那是不是不用执行后续逻辑哈
56 59 int pointPrice = calculatePointPrice(config, user.getPoint(), result);
57   - // 2.1 计算分摊的积分、抵扣金额
  60 + // 2.2 计算分摊的积分、抵扣金额
58 61 List<TradePriceCalculateRespBO.OrderItem> orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected);
59 62 List<Integer> dividePointPrices = TradePriceCalculatorHelper.dividePrice(orderItems, pointPrice);
60 63 List<Integer> divideUsePoints = TradePriceCalculatorHelper.dividePrice(orderItems, result.getUsePoint());
... ... @@ -74,7 +77,9 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
74 77 TradePriceCalculatorHelper.recountAllPrice(result);
75 78 }
76 79  
  80 + // TODO @疯狂:这个最好是 is 开头;因为 check 或者 validator,更多失败,会抛出异常;
77 81 private boolean checkDeductPointEnable(MemberPointConfigRespDTO config) {
  82 + // TODO @疯狂:这个要不直接写成 return config != null && config .... 多行这样一个形式;
78 83 if (config == null) {
79 84 return false;
80 85 }
... ... @@ -93,7 +98,7 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
93 98 }
94 99 // 积分优惠金额(分)
95 100 int pointPrice = usePoint * config.getTradeDeductUnitPrice();
96   - // 0元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额, 这时需要根据支付金额反推使用多少积分
  101 + // 0 元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额,这时需要根据支付金额反推使用多少积分
97 102 if (result.getPrice().getPayPrice() < pointPrice) {
98 103 pointPrice = result.getPrice().getPayPrice();
99 104 // 反推需要扣除的积分
... ... @@ -103,7 +108,7 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
103 108 }
104 109 // 记录使用的积分
105 110 result.setUsePoint(usePoint);
106   -
107 111 return pointPrice;
108 112 }
  113 +
109 114 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java
... ... @@ -21,11 +21,13 @@ public interface TradePriceCalculator {
21 21 /**
22 22 * 快递运费的计算
23 23 *
24   - * 放在各种营销活动、优惠劵后面 TODO
  24 + * 放在各种营销活动、优惠劵后面
25 25 */
26 26 int ORDER_DELIVERY = 50;
27 27 /**
28 28 * 赠送积分,放最后
  29 + *
  30 + * 放在 {@link #ORDER_DELIVERY} 后面的原因,是运费也会产生费用,需要赠送对应积分
29 31 */
30 32 int ORDER_POINT_GIVE = 999;
31 33  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java
... ... @@ -199,6 +199,8 @@ public class TradePriceCalculatorHelper {
199 199 /**
200 200 * 按照支付金额,返回每个订单项的分摊金额数组
201 201 *
  202 + * 实际上 price 不仅仅可以传递的是金额,也可以是积分。因为它的实现逻辑,就是根据 payPrice 做分摊而已
  203 + *
202 204 * @param orderItems 订单项数组
203 205 * @param price 金额
204 206 * @return 分摊金额数组,和传入的 orderItems 一一对应
... ...
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
... ... @@ -306,7 +306,7 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest {
306 306 // mock 方法(支付单)
307 307  
308 308 // 调用
309   - tradeOrderUpdateService.receiveOrder(userId, id);
  309 + tradeOrderUpdateService.receiveOrderByMember(userId, id);
310 310 // 断言
311 311 TradeOrderDO dbOrder = tradeOrderMapper.selectById(1L);
312 312 assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
... ...
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java
... ... @@ -12,6 +12,7 @@ import javax.validation.constraints.Min;
12 12 */
13 13 public interface MemberPointApi {
14 14  
  15 + // TODO @疯狂:这个我们要不要搞成通用的会员配置?MemberConfig?
15 16 /**
16 17 * 获得积分配置
17 18 *
... ...
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/dto/MemberPointConfigRespDTO.java
... ... @@ -9,6 +9,7 @@ import lombok.Data;
9 9 */
10 10 @Data
11 11 public class MemberPointConfigRespDTO {
  12 +
12 13 /**
13 14 * 积分抵扣开关
14 15 */
... ... @@ -27,4 +28,5 @@ public class MemberPointConfigRespDTO {
27 28 * 1 元赠送多少分
28 29 */
29 30 private Integer tradeGivePoint;
  31 +
30 32 }
... ...
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java
... ... @@ -51,4 +51,5 @@ public class MemberUserRespDTO {
51 51 * 积分
52 52 */
53 53 private Integer point;
  54 +
54 55 }
... ...
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java
... ... @@ -17,11 +17,11 @@ import java.util.Objects;
17 17 public enum MemberPointBizTypeEnum implements IntArrayValuable {
18 18  
19 19 SIGN(1, "签到", "签到获得 {} 积分", true),
20   - ORDER_REWARD(10, "订单奖励", "下单获得 {} 积分", true),
21   - ORDER_CANCEL(11, "订单取消", "订单取消,退还 {} 积分", true), // 退回积分
22   - ORDER_USE(12, "订单使用", "下单使用 {} 积分", false), // 扣减积分
23   - AFTER_SALE_REFUND_USED(13, "订单退款", "订单退款,退还 {} 积分", true), // 退回积分
24   - AFTER_SALE_DEDUCT_GIVE(14, "订单退款", "订单退款,扣除赠送的 {} 积分", false), // 扣减积分
  20 + ORDER_GIVE(10, "订单奖励", "下单获得 {} 积分", true), // 支付订单时,赠送积分
  21 + ORDER_CANCEL(11, "订单取消", "订单取消,退还 {} 积分", true), // 取消订单时,退回积分
  22 + ORDER_USE(12, "订单使用", "下单使用 {} 积分", false), // 下单时,扣减积分
  23 + AFTER_SALE_REFUND_USED(13, "订单退款", "订单退款,退还 {} 积分", true), // 售后订单成功时,退回积分(对应 ORDER_USE 操作)
  24 + AFTER_SALE_DEDUCT_GIVE(14, "订单退款", "订单退款,扣除赠送的 {} 积分", false), // 售后订单成功时,扣减积分(对应 ORDER_GIVE 操作)
25 25 ;
26 26  
27 27 /**
... ...
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:
... ...