Commit 66abe4a84b631f70fae8f8674e9eee6ebe77a831

Authored by YunaiV
1 parent 07f1681b

trade:完成交易订单的发货逻辑

Showing 18 changed files with 332 additions and 126 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java
... ... @@ -62,7 +62,7 @@ public class PayOrderUnifiedReqDTO {
62 62 */
63 63 @NotNull(message = "支付金额不能为空")
64 64 @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
65   - private Long amount;
  65 + private Integer amount;
66 66  
67 67 /**
68 68 * 支付过期时间
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedReqDTO.java
... ... @@ -63,7 +63,7 @@ public class PayRefundUnifiedReqDTO {
63 63 */
64 64 @NotNull(message = "退款金额不能为空")
65 65 @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
66   - private Long amount;
  66 + private Integer amount;
67 67  
68 68 /**
69 69 * 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
... ... @@ -69,7 +69,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
69 69 this.init();
70 70 }
71 71  
72   - protected Double calculateAmount(Long amount) {
  72 + protected Double calculateAmount(Integer amount) {
73 73 return amount / 100.0;
74 74 }
75 75  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn.iocoder.yudao.framework.pay.core.client.impl/PayClientFactoryImplIntegrationTest.java
... ... @@ -122,7 +122,7 @@ public class PayClientFactoryImplIntegrationTest {
122 122  
123 123 private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
124 124 PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
125   - reqDTO.setAmount(123L);
  125 + reqDTO.setAmount(123);
126 126 reqDTO.setSubject("IPhone 13");
127 127 reqDTO.setBody("biubiubiu");
128 128 reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn.iocoder.yudao.framework.pay.core.client.impl/alipay/AlipayQrPayClientTest.java
... ... @@ -73,7 +73,7 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
73 73 Long shopOrderId = System.currentTimeMillis();
74 74 PayOrderUnifiedReqDTO reqDTO=new PayOrderUnifiedReqDTO();
75 75 reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
76   - reqDTO.setAmount(1L);
  76 + reqDTO.setAmount(1);
77 77 reqDTO.setBody("内容:" + shopOrderId);
78 78 reqDTO.setSubject("标题:"+shopOrderId);
79 79 String notify="http://niubi.natapp1.cc/api/pay/order/notify";
... ...
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
... ... @@ -23,9 +23,10 @@ public interface ErrorCodeConstants {
23 23 ErrorCode ORDER_NOT_FOUND = new ErrorCode(1011000011, "交易订单不存在");
24 24 ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1011000012, "交易订单项更新售后状态失败,请重试");
25 25 ErrorCode ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1011000013, "交易订单更新支付状态失败,订单不是【未支付】状态");
26   - ErrorCode ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1011000014, "交易订单更新支付状态失败,支付单编号不匹配");
27   - ErrorCode ORDER_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1011000015, "交易订单更新支付状态失败,支付单状态不是【支付成功】状态");
28   - ErrorCode ORDER_UPDATE_PAID_PAY_PRICE_NOT_MATCH = new ErrorCode(1011000016, "交易订单更新支付状态失败,支付单金额不匹配");
  26 + ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1011000014, "交易订单更新支付状态失败,支付单编号不匹配");
  27 + ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1011000015, "交易订单更新支付状态失败,支付单状态不是【支付成功】状态");
  28 + ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1011000016, "交易订单更新支付状态失败,支付单金额不匹配");
  29 + ErrorCode ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED = new ErrorCode(1011000017, "交易订单发货失败,订单不是【待发货】状态");
29 30  
30 31 // ========== After Sale 模块 1-011-000-000 ==========
31 32 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");
... ...
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderDeliveryStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.enums.order;
  2 +
  3 +import lombok.Getter;
  4 +import lombok.RequiredArgsConstructor;
  5 +
  6 +/**
  7 + * 交易订单 - 发货状态
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +@RequiredArgsConstructor
  12 +@Getter
  13 +public enum TradeOrderDeliveryStatusEnum {
  14 +
  15 + UNDELIVERED(0, "未发货"),
  16 + DELIVERED(1, "已发货"),
  17 + RECEIVED(2, "已收货");
  18 +
  19 + /**
  20 + * 状态值
  21 + */
  22 + private final Integer status;
  23 + /**
  24 + * 状态名
  25 + */
  26 + private final String name;
  27 +
  28 +}
... ...
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderStatusEnum.java
... ... @@ -52,10 +52,31 @@ public enum TradeOrderStatusEnum implements IntArrayValuable {
52 52 * @return 是否
53 53 */
54 54 public static boolean isUnpaid(Integer status) {
  55 + return ObjectUtil.equal(UNPAID.getStatus(), status);
  56 + }
  57 +
  58 + /**
  59 + * 判断指定状态,是否正处于【已支付】状态
  60 + *
  61 + * @param status 指定状态
  62 + * @return 是否
  63 + */
  64 + public static boolean isPaid(Integer status) {
55 65 return ObjectUtil.equal(PAID.getStatus(), status);
56 66 }
57 67  
58 68 /**
  69 + * 判断指定状态,是否正处于【待发货】状态
  70 + *
  71 + * @param status 指定状态
  72 + * @return 是否
  73 + */
  74 + public static boolean isUndelivered(Integer status) {
  75 + return ObjectUtil.equal(UNDELIVERED.getStatus(), status);
  76 + }
  77 +
  78 +
  79 + /**
59 80 * 判断指定状态,是否正处于【已取消】状态
60 81 *
61 82 * @param status 指定状态
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.admin.order;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
  5 +import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
  6 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderService;
  7 +import io.swagger.annotations.Api;
  8 +import io.swagger.annotations.ApiOperation;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.springframework.security.access.prepost.PreAuthorize;
  11 +import org.springframework.validation.annotation.Validated;
  12 +import org.springframework.web.bind.annotation.PostMapping;
  13 +import org.springframework.web.bind.annotation.RequestBody;
  14 +import org.springframework.web.bind.annotation.RequestMapping;
  15 +import org.springframework.web.bind.annotation.RestController;
  16 +
  17 +import javax.annotation.Resource;
  18 +
  19 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  20 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  21 +
  22 +@Api(tags = "管理后台 - 交易订单")
  23 +@RestController
  24 +@RequestMapping("/trade/order")
  25 +@Validated
  26 +@Slf4j
  27 +public class TradeOrderController {
  28 +
  29 + @Resource
  30 + private TradeOrderService tradeOrderService;
  31 +
  32 + @PostMapping("/delivery")
  33 + @ApiOperation("发货订单")
  34 + @PreAuthorize("@ss.hasPermission('trade:order:delivery')")
  35 + public CommonResult<Boolean> deliveryOrder(@RequestBody TradeOrderDeliveryReqVO deliveryReqVO) {
  36 + tradeOrderService.deliveryOrder(getLoginUserId(), deliveryReqVO);
  37 + return success(true);
  38 + }
  39 +
  40 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotEmpty;
  8 +import javax.validation.constraints.NotNull;
  9 +
  10 +@ApiModel("管理后台 - 订单发货 Request VO")
  11 +@Data
  12 +public class TradeOrderDeliveryReqVO {
  13 +
  14 + @ApiModelProperty(name = "订单编号", required = true, example = "1024")
  15 + @NotNull(message = "订单编号不能为空")
  16 + private Long id;
  17 +
  18 + @ApiModelProperty(name = "发货物流公司编号", required = true, example = "1")
  19 + @NotNull(message = "发货物流公司编号不能为空")
  20 + private Long logisticsId;
  21 +
  22 + @ApiModelProperty(name = "发货物流单号", required = true, example = "SF123456789")
  23 + @NotEmpty(message = "发货物流单号不能为空")
  24 + private String logisticsNo;
  25 +
  26 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppTradeAfterSaleDeliveryReqVO.java
... ... @@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModel;
4 4 import io.swagger.annotations.ApiModelProperty;
5 5 import lombok.Data;
6 6  
  7 +import javax.validation.constraints.NotEmpty;
7 8 import javax.validation.constraints.NotNull;
8 9 import java.time.LocalDateTime;
9 10  
... ... @@ -24,7 +25,7 @@ public class AppTradeAfterSaleDeliveryReqVO {
24 25 private String logisticsNo;
25 26  
26 27 @ApiModelProperty(name = "退货时间", required = true)
27   - @NotNull(message = "退货时间不能为空")
  28 + @NotEmpty(message = "退货时间不能为空")
28 29 private LocalDateTime deliveryTime;
29 30  
30 31 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
... ... @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5 5 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO.OrderItem;
6 6 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum;
7 7 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderAfterSaleStatusEnum;
  8 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderDeliveryStatusEnum;
8 9 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
9 10 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
10 11 import com.baomidou.mybatisplus.annotation.KeySequence;
... ... @@ -178,20 +179,24 @@ public class TradeOrderDO extends BaseDO {
178 179 */
179 180 private Long deliveryTemplateId;
180 181 /**
181   - * 物流公司单号
  182 + * 发货物流公司编号
182 183 */
183   - private String expressNo;
  184 + private Long logisticsId;
  185 + /**
  186 + * 发货物流单号
  187 + */
  188 + private String logisticsNo;
184 189 /**
185 190 * 发货状态
186 191 *
187   - * true - 已发货
188   - * false - 未发货
  192 + * 枚举 {@link TradeOrderDeliveryStatusEnum}
189 193 */
190   - private Boolean deliveryStatus;
  194 + private Integer deliveryStatus;
191 195 /**
192 196 * 发货时间
193 197 */
194 198 private LocalDateTime deliveryTime;
  199 +
195 200 /**
196 201 * 收货时间
197 202 */
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java
1 1 package cn.iocoder.yudao.module.trade.service.order;
2 2  
  3 +import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
3 4 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
4 5 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
5 6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
... ... @@ -15,7 +16,7 @@ public interface TradeOrderService {
15 16 // =================== Order ===================
16 17  
17 18 /**
18   - * 创建交易订单
  19 + * 【会员】创建交易订单
19 20 *
20 21 * @param userId 登录用户
21 22 * @param userIp 用户 IP 地址
... ... @@ -25,6 +26,22 @@ public interface TradeOrderService {
25 26 Long createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO);
26 27  
27 28 /**
  29 + * 更新交易订单已支付
  30 + *
  31 + * @param id 交易订单编号
  32 + * @param payOrderId 支付订单编号
  33 + */
  34 + void updateOrderPaid(Long id, Long payOrderId);
  35 +
  36 + /**
  37 + * 【管理员】发货交易订单
  38 + *
  39 + * @param userId 管理员编号
  40 + * @param deliveryReqVO 发货请求
  41 + */
  42 + void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO);
  43 +
  44 + /**
28 45 * 获得指定用户,指定的交易订单
29 46 *
30 47 * @param userId 用户编号
... ... @@ -33,14 +50,6 @@ public interface TradeOrderService {
33 50 */
34 51 TradeOrderDO getOrder(Long userId, Long orderId);
35 52  
36   - /**
37   - * 更新交易订单已支付
38   - *
39   - * @param id 交易订单编号
40   - * @param payOrderId 支付订单编号
41   - */
42   - void updateOrderPaid(Long id, Long payOrderId);
43   -
44 53 // =================== Order Item ===================
45 54  
46 55 /**
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
... ... @@ -24,6 +24,7 @@ import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
24 24 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
25 25 import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
26 26 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
  27 +import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
27 28 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
28 29 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO.Item;
29 30 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
... ... @@ -107,94 +108,6 @@ public class TradeOrderServiceImpl implements TradeOrderService {
107 108 return tradeOrderDO.getId();
108 109 }
109 110  
110   - @Override
111   - public TradeOrderDO getOrder(Long userId, Long orderId) {
112   - TradeOrderDO order = tradeOrderMapper.selectById(orderId);
113   - if (order != null
114   - && ObjectUtil.notEqual(order.getUserId(), userId)) {
115   - return null;
116   - }
117   - return order;
118   - }
119   -
120   - @Override
121   - public void updateOrderPaid(Long id, Long payOrderId) {
122   - // 校验并获得交易订单
123   - KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPaid(id, payOrderId);
124   - TradeOrderDO order = orderResult.getKey();
125   - PayOrderRespDTO payOrder = orderResult.getValue();
126   -
127   - // 更新 TradeOrderDO 状态为已支付,等待发货
128   - int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
129   - new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayed(true)
130   - .setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
131   - if (updateCount == 0) {
132   - throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
133   - }
134   -
135   - // TODO 芋艿:发送订单变化的消息
136   -
137   - // TODO 芋艿:发送站内信
138   -
139   - // TODO 芋艿:OrderLog
140   - }
141   -
142   - /**
143   - * 校验交易订单满足被支付的条件
144   - *
145   - * 1. 交易订单未支付
146   - * 2. 支付单已支付
147   - *
148   - * @param id 交易订单编号
149   - * @param payOrderId 支付订单编号
150   - * @return 交易订单
151   - */
152   - private KeyValue<TradeOrderDO, PayOrderRespDTO> validateOrderPaid(Long id, Long payOrderId) {
153   - // 校验订单是否存在
154   - TradeOrderDO order = tradeOrderMapper.selectById(id);
155   - if (order == null) {
156   - throw exception(ORDER_NOT_FOUND);
157   - }
158   - // 校验订单未支付
159   - if (TradeOrderStatusEnum.isUnpaid(order.getStatus())) { // 状态
160   - log.error("[validateOrderPaid][order({}) 不处于待支付状态,请进行处理!order 数据是:{}]",
161   - id, JsonUtils.toJsonString(order));
162   - throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
163   - }
164   - // 校验支付订单匹配
165   - if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
166   - log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]",
167   - id, payOrderId, JsonUtils.toJsonString(order));
168   - throw exception(ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR);
169   - }
170   -
171   - // 校验支付单是否存在
172   - PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
173   - if (payOrder == null) {
174   - log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId);
175   - throw exception(PAY_ORDER_NOT_FOUND);
176   - }
177   - // 校验支付单已支付
178   - if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
179   - log.error("[validateOrderPaid][order({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]",
180   - id, payOrderId, JsonUtils.toJsonString(payOrder));
181   - throw exception(ORDER_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS);
182   - }
183   - // 校验支付金额一致
184   - if (ObjectUtil.notEqual(payOrder.getAmount(), order.getPayPrice())) {
185   - log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]",
186   - id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
187   - throw exception(ORDER_UPDATE_PAID_PAY_PRICE_NOT_MATCH);
188   - }
189   - // 校验支付订单匹配(二次)
190   - if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
191   - log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]",
192   - id, payOrderId, JsonUtils.toJsonString(payOrder));
193   - throw exception(ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR);
194   - }
195   - return new KeyValue<>(order, payOrder);
196   - }
197   -
198 111 /**
199 112 * 校验商品 SKU 是否可出售
200 113 *
... ... @@ -269,7 +182,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
269 182 tradeOrderDO.setProductCount(getSumValue(order.getItems(), PriceCalculateRespDTO.OrderItem::getCount, Integer::sum));
270 183 tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
271 184 tradeOrderDO.setAdjustPrice(0).setPayed(false); // 支付信息
272   - tradeOrderDO.setDeliveryStatus(false); // 物流信息
  185 + tradeOrderDO.setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus()); // 物流信息
273 186 tradeOrderDO.setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.NONE.getStatus()).setRefundPrice(0); // 退款信息
274 187 tradeOrderMapper.insert(tradeOrderDO);
275 188 return tradeOrderDO;
... ... @@ -324,7 +237,142 @@ public class TradeOrderServiceImpl implements TradeOrderService {
324 237 tradeOrderMapper.updateById(new TradeOrderDO().setId(tradeOrderDO.getId()).setPayOrderId(payOrderId));
325 238 }
326 239  
  240 + @Override
  241 + public void updateOrderPaid(Long id, Long payOrderId) {
  242 + // 校验并获得交易订单(可支付)
  243 + KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPayable(id, payOrderId);
  244 + TradeOrderDO order = orderResult.getKey();
  245 + PayOrderRespDTO payOrder = orderResult.getValue();
  246 +
  247 + // 更新 TradeOrderDO 状态为已支付,等待发货
  248 + int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
  249 + new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayed(true)
  250 + .setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
  251 + if (updateCount == 0) {
  252 + throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
  253 + }
  254 +
  255 + // TODO 芋艿:发送订单变化的消息
  256 +
  257 + // TODO 芋艿:发送站内信
  258 +
  259 + // TODO 芋艿:OrderLog
  260 + }
  261 +
  262 + /**
  263 + * 校验交易订单满足被支付的条件
  264 + *
  265 + * 1. 交易订单未支付
  266 + * 2. 支付单已支付
  267 + *
  268 + * @param id 交易订单编号
  269 + * @param payOrderId 支付订单编号
  270 + * @return 交易订单
  271 + */
  272 + private KeyValue<TradeOrderDO, PayOrderRespDTO> validateOrderPayable(Long id, Long payOrderId) {
  273 + // 校验订单是否存在
  274 + TradeOrderDO order = tradeOrderMapper.selectById(id);
  275 + if (order == null) {
  276 + throw exception(ORDER_NOT_FOUND);
  277 + }
  278 + // 校验订单未支付
  279 + if (!TradeOrderStatusEnum.isUnpaid(order.getStatus()) || order.getPayed()) {
  280 + log.error("[validateOrderPaid][order({}) 不处于待支付状态,请进行处理!order 数据是:{}]",
  281 + id, JsonUtils.toJsonString(order));
  282 + throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
  283 + }
  284 + // 校验支付订单匹配
  285 + if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
  286 + log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]",
  287 + id, payOrderId, JsonUtils.toJsonString(order));
  288 + throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
  289 + }
  290 +
  291 + // 校验支付单是否存在
  292 + PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
  293 + if (payOrder == null) {
  294 + log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId);
  295 + throw exception(PAY_ORDER_NOT_FOUND);
  296 + }
  297 + // 校验支付单已支付
  298 + if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
  299 + log.error("[validateOrderPaid][order({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]",
  300 + id, payOrderId, JsonUtils.toJsonString(payOrder));
  301 + throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
  302 + }
  303 + // 校验支付金额一致
  304 + if (ObjectUtil.notEqual(payOrder.getAmount(), order.getPayPrice())) {
  305 + log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]",
  306 + id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
  307 + throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
  308 + }
  309 + // 校验支付订单匹配(二次)
  310 + if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
  311 + log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]",
  312 + id, payOrderId, JsonUtils.toJsonString(payOrder));
  313 + throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
  314 + }
  315 + return new KeyValue<>(order, payOrder);
  316 + }
  317 +
  318 + @Override
  319 + public void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO) {
  320 + // 校验并获得交易订单(可发货)
  321 + TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
  322 +
  323 + // TODO 芋艿:logisticsId 校验存在
  324 +
  325 + // 更新 TradeOrderDO 状态为已发货,等待收货
  326 + int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
  327 + new TradeOrderDO().setStatus(TradeOrderStatusEnum.DELIVERED.getStatus())
  328 + .setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo())
  329 + .setDeliveryStatus(TradeOrderDeliveryStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now()));
  330 + if (updateCount == 0) {
  331 + throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
  332 + }
  333 +
  334 + // TODO 芋艿:发送订单变化的消息
  335 +
  336 + // TODO 芋艿:发送站内信
  337 +
  338 + // TODO 芋艿:OrderLog
  339 +
  340 + // TODO 设计:like:是否要单独一个 delivery 发货单表???
  341 + // TODO 设计:niu:要不要支持一个订单下,多个 order item 单独发货,类似有赞
  342 + // TODO 设计:lili:是不是发货后,才支持售后?
  343 + }
  344 +
  345 + /**
  346 + * 校验交易订单满足被发货的条件
  347 + *
  348 + * 1. 交易订单未发货
  349 + *
  350 + * @param id 交易订单编号
  351 + * @return 交易订单
  352 + */
  353 + private TradeOrderDO validateOrderDeliverable(Long id) {
  354 + // 校验订单是否存在
  355 + TradeOrderDO order = tradeOrderMapper.selectById(id);
  356 + if (order == null) {
  357 + throw exception(ORDER_NOT_FOUND);
  358 + }
  359 + // 校验订单是否是待发货状态
  360 + if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())
  361 + || ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus())) {
  362 + throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
  363 + }
  364 + return order;
  365 + }
327 366  
  367 + @Override
  368 + public TradeOrderDO getOrder(Long userId, Long orderId) {
  369 + TradeOrderDO order = tradeOrderMapper.selectById(orderId);
  370 + if (order != null
  371 + && ObjectUtil.notEqual(order.getUserId(), userId)) {
  372 + return null;
  373 + }
  374 + return order;
  375 + }
328 376  
329 377 // =================== Order Item ===================
330 378  
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java
... ... @@ -16,15 +16,13 @@ import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
16 16 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
17 17 import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
18 18 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
  19 +import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
19 20 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
20 21 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
21 22 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
22 23 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
23 24 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
24   -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderAfterSaleStatusEnum;
25   -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
26   -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
27   -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
  25 +import cn.iocoder.yudao.module.trade.enums.order.*;
28 26 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig;
29 27 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
30 28 import org.junit.jupiter.api.BeforeEach;
... ... @@ -38,6 +36,8 @@ import java.util.Arrays;
38 36 import java.util.List;
39 37  
40 38 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  39 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  40 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
41 41 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
42 42 import static java.util.Collections.singletonList;
43 43 import static org.junit.jupiter.api.Assertions.*;
... ... @@ -173,8 +173,8 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
173 173 assertEquals(tradeOrderDO.getPayOrderId(), 1000L);
174 174 assertNull(tradeOrderDO.getPayChannelCode());
175 175 assertNull(tradeOrderDO.getDeliveryTemplateId());
176   - assertNull(tradeOrderDO.getExpressNo());
177   - assertFalse(tradeOrderDO.getDeliveryStatus());
  176 + assertNull(tradeOrderDO.getLogisticsId());
  177 + assertEquals(tradeOrderDO.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus());
178 178 assertNull(tradeOrderDO.getDeliveryTime());
179 179 assertNull(tradeOrderDO.getReceiveTime());
180 180 assertEquals(tradeOrderDO.getReceiverName(), "芋艿");
... ... @@ -246,11 +246,11 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
246 246 }
247 247  
248 248 @Test
249   - public void updateOrderPaid() {
  249 + public void testUpdateOrderPaid() {
250 250 // mock 数据(TradeOrder)
251 251 TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> {
252 252 o.setId(1L).setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
253   - o.setPayOrderId(10L).setPayPrice(100).setPayTime(null);
  253 + o.setPayOrderId(10L).setPayed(false).setPayPrice(100).setPayTime(null);
254 254 });
255 255 tradeOrderMapper.insert(order);
256 256 // 准备参数
... ... @@ -260,13 +260,39 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
260 260 when(payOrderApi.getOrder(eq(10L))).thenReturn(randomPojo(PayOrderRespDTO.class,
261 261 o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()).setChannelCode("wx_pub")
262 262 .setMerchantOrderId("1")).setAmount(100));
  263 +
263 264 // 调用
264 265 tradeOrderService.updateOrderPaid(id, payOrderId);
265 266 // 断言
266 267 TradeOrderDO dbOrder = tradeOrderMapper.selectById(id);
267   - assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.PAID.getStatus());
  268 + assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
268 269 assertTrue(dbOrder.getPayed());
269 270 assertNotNull(dbOrder.getPayTime());
270 271 assertEquals(dbOrder.getPayChannelCode(), "wx_pub");
271 272 }
  273 +
  274 + @Test
  275 + public void testDeliveryOrder() {
  276 + // mock 数据(TradeOrder)
  277 + TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> {
  278 + o.setId(1L).setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
  279 + o.setLogisticsId(null).setLogisticsNo(null).setDeliveryTime(null)
  280 + .setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus());
  281 + });
  282 + tradeOrderMapper.insert(order);
  283 + // 准备参数
  284 + TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO().setId(1L)
  285 + .setLogisticsId(10L).setLogisticsNo("100");
  286 + // mock 方法(支付单)
  287 +
  288 + // 调用
  289 + tradeOrderService.deliveryOrder(randomLongId(), deliveryReqVO);
  290 + // 断言
  291 + TradeOrderDO dbOrder = tradeOrderMapper.selectById(1L);
  292 + assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.DELIVERED.getStatus());
  293 + assertEquals(dbOrder.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.DELIVERED.getStatus());
  294 + assertPojoEquals(dbOrder, deliveryReqVO);
  295 + assertNotNull(dbOrder.getDeliveryTime());
  296 + }
  297 +
272 298 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql
... ... @@ -20,10 +20,11 @@ CREATE TABLE IF NOT EXISTS &quot;trade_order&quot; (
20 20 "delivery_price" int NOT NULL,
21 21 "adjust_price" int NOT NULL,
22 22 "pay_price" int NOT NULL,
23   - "pay_order_id" int,
  23 + "pay_order_id" bigint,
24 24 "pay_channel_code" varchar,
25 25 "delivery_template_id" bigint,
26   - "express_no" varchar,
  26 + "logistics_id" bigint,
  27 + "logistics_no" varchar,
27 28 "delivery_status" bit NOT NULL,
28 29 "delivery_time" datetime,
29 30 "receive_time" datetime,
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java
... ... @@ -132,11 +132,11 @@ public class PayRefundDO extends BaseDO {
132 132 /**
133 133 * 支付金额,单位:分
134 134 */
135   - private Long payAmount;
  135 + private Integer payAmount;
136 136 /**
137 137 * 退款金额,单位:分
138 138 */
139   - private Long refundAmount;
  139 + private Integer refundAmount;
140 140  
141 141 /**
142 142 * 退款原因
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/dto/PayRefundReqDTO.java
... ... @@ -31,7 +31,7 @@ public class PayRefundReqDTO {
31 31 */
32 32 @NotNull(message = "退款金额不能为空")
33 33 @DecimalMin(value = "0", inclusive = false, message = "退款金额必须大于零")
34   - private Long amount;
  34 + private Integer amount;
35 35  
36 36 /**
37 37 * 退款原因
... ...