浏览代码

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

YunaiV 2 年之前
父节点
当前提交
66abe4a84b
共有 18 个文件被更改,包括 332 次插入126 次删除
  1. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java
  2. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedReqDTO.java
  3. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
  4. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn.iocoder.yudao.framework.pay.core.client.impl/PayClientFactoryImplIntegrationTest.java
  5. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn.iocoder.yudao.framework.pay.core.client.impl/alipay/AlipayQrPayClientTest.java
  6. 4 3
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
  7. 28 0
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderDeliveryStatusEnum.java
  8. 21 0
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderStatusEnum.java
  9. 40 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java
  10. 26 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java
  11. 2 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppTradeAfterSaleDeliveryReqVO.java
  12. 10 5
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
  13. 18 9
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java
  14. 137 89
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
  15. 35 9
      yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java
  16. 3 2
      yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql
  17. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java
  18. 1 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/dto/PayRefundReqDTO.java

+ 1 - 1
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 {
      */
     @NotNull(message = "支付金额不能为空")
     @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
-    private Long amount;
+    private Integer amount;
 
     /**
      * 支付过期时间

+ 1 - 1
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 {
      */
     @NotNull(message = "退款金额不能为空")
     @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
-    private Long amount;
+    private Integer amount;
 
     /**
      * 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要

+ 1 - 1
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
         this.init();
     }
 
-    protected Double calculateAmount(Long amount) {
+    protected Double calculateAmount(Integer amount) {
         return amount / 100.0;
     }
 

+ 1 - 1
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 {
 
     private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
         PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
-        reqDTO.setAmount(123L);
+        reqDTO.setAmount(123);
         reqDTO.setSubject("IPhone 13");
         reqDTO.setBody("biubiubiu");
         reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));

+ 1 - 1
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 {
         Long shopOrderId = System.currentTimeMillis();
         PayOrderUnifiedReqDTO reqDTO=new PayOrderUnifiedReqDTO();
         reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
-        reqDTO.setAmount(1L);
+        reqDTO.setAmount(1);
         reqDTO.setBody("内容:" + shopOrderId);
         reqDTO.setSubject("标题:"+shopOrderId);
         String notify="http://niubi.natapp1.cc/api/pay/order/notify";

+ 4 - 3
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 {
     ErrorCode ORDER_NOT_FOUND = new ErrorCode(1011000011, "交易订单不存在");
     ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1011000012, "交易订单项更新售后状态失败,请重试");
     ErrorCode ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1011000013, "交易订单更新支付状态失败,订单不是【未支付】状态");
-    ErrorCode ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1011000014, "交易订单更新支付状态失败,支付单编号不匹配");
-    ErrorCode ORDER_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1011000015, "交易订单更新支付状态失败,支付单状态不是【支付成功】状态");
-    ErrorCode ORDER_UPDATE_PAID_PAY_PRICE_NOT_MATCH = new ErrorCode(1011000016, "交易订单更新支付状态失败,支付单金额不匹配");
+    ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1011000014, "交易订单更新支付状态失败,支付单编号不匹配");
+    ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1011000015, "交易订单更新支付状态失败,支付单状态不是【支付成功】状态");
+    ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1011000016, "交易订单更新支付状态失败,支付单金额不匹配");
+    ErrorCode ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED = new ErrorCode(1011000017, "交易订单发货失败,订单不是【待发货】状态");
 
     // ==========  After Sale 模块 1-011-000-000 ==========
     ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");

+ 28 - 0
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderDeliveryStatusEnum.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.trade.enums.order;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 交易订单 - 发货状态
+ *
+ * @author 芋道源码
+ */
+@RequiredArgsConstructor
+@Getter
+public enum TradeOrderDeliveryStatusEnum {
+
+    UNDELIVERED(0, "未发货"),
+    DELIVERED(1, "已发货"),
+    RECEIVED(2, "已收货");
+
+    /**
+     * 状态值
+     */
+    private final Integer status;
+    /**
+     * 状态名
+     */
+    private final String name;
+
+}

+ 21 - 0
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderStatusEnum.java

@@ -52,9 +52,30 @@ public enum TradeOrderStatusEnum implements IntArrayValuable {
      * @return 是否
      */
     public static boolean isUnpaid(Integer status) {
+        return ObjectUtil.equal(UNPAID.getStatus(), status);
+    }
+
+    /**
+     * 判断指定状态,是否正处于【已支付】状态
+     *
+     * @param status 指定状态
+     * @return 是否
+     */
+    public static boolean isPaid(Integer status) {
         return ObjectUtil.equal(PAID.getStatus(), status);
     }
 
+    /**
+     * 判断指定状态,是否正处于【待发货】状态
+     *
+     * @param status 指定状态
+     * @return 是否
+     */
+    public static boolean isUndelivered(Integer status) {
+        return ObjectUtil.equal(UNDELIVERED.getStatus(), status);
+    }
+
+
     /**
      * 判断指定状态,是否正处于【已取消】状态
      *

+ 40 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.trade.controller.admin.order;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
+import cn.iocoder.yudao.module.trade.service.order.TradeOrderService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Api(tags = "管理后台 - 交易订单")
+@RestController
+@RequestMapping("/trade/order")
+@Validated
+@Slf4j
+public class TradeOrderController {
+
+    @Resource
+    private TradeOrderService tradeOrderService;
+
+    @PostMapping("/delivery")
+    @ApiOperation("发货订单")
+    @PreAuthorize("@ss.hasPermission('trade:order:delivery')")
+    public CommonResult<Boolean> deliveryOrder(@RequestBody TradeOrderDeliveryReqVO deliveryReqVO) {
+        tradeOrderService.deliveryOrder(getLoginUserId(), deliveryReqVO);
+        return success(true);
+    }
+
+}

+ 26 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@ApiModel("管理后台 - 订单发货 Request VO")
+@Data
+public class TradeOrderDeliveryReqVO {
+
+    @ApiModelProperty(name = "订单编号", required = true, example = "1024")
+    @NotNull(message = "订单编号不能为空")
+    private Long id;
+
+    @ApiModelProperty(name = "发货物流公司编号", required = true, example = "1")
+    @NotNull(message = "发货物流公司编号不能为空")
+    private Long logisticsId;
+
+    @ApiModelProperty(name = "发货物流单号", required = true, example = "SF123456789")
+    @NotEmpty(message = "发货物流单号不能为空")
+    private String logisticsNo;
+
+}

+ 2 - 1
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;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
 import java.time.LocalDateTime;
 
@@ -24,7 +25,7 @@ public class AppTradeAfterSaleDeliveryReqVO {
     private String logisticsNo;
 
     @ApiModelProperty(name = "退货时间", required = true)
-    @NotNull(message = "退货时间不能为空")
+    @NotEmpty(message = "退货时间不能为空")
     private LocalDateTime deliveryTime;
 
 }

+ 10 - 5
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;
 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO.OrderItem;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderAfterSaleStatusEnum;
+import cn.iocoder.yudao.module.trade.enums.order.TradeOrderDeliveryStatusEnum;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -178,20 +179,24 @@ public class TradeOrderDO extends BaseDO {
      */
     private Long deliveryTemplateId;
     /**
-     * 物流公司单
+     * 发货物流公司编
      */
-    private String expressNo;
+    private Long logisticsId;
+    /**
+     * 发货物流单号
+     */
+    private String logisticsNo;
     /**
      * 发货状态
      *
-     * true - 已发货
-     * false - 未发货
+     * 枚举 {@link TradeOrderDeliveryStatusEnum}
      */
-    private Boolean deliveryStatus;
+    private Integer deliveryStatus;
     /**
      * 发货时间
      */
     private LocalDateTime deliveryTime;
+
     /**
      * 收货时间
      */

+ 18 - 9
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.trade.service.order;
 
+import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
@@ -15,7 +16,7 @@ public interface TradeOrderService {
     // =================== Order ===================
 
     /**
-     * 创建交易订单
+     * 【会员】创建交易订单
      *
      * @param userId 登录用户
      * @param userIp 用户 IP 地址
@@ -24,6 +25,22 @@ public interface TradeOrderService {
      */
     Long createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO);
 
+    /**
+     * 更新交易订单已支付
+     *
+     * @param id 交易订单编号
+     * @param payOrderId 支付订单编号
+     */
+    void updateOrderPaid(Long id, Long payOrderId);
+
+    /**
+     * 【管理员】发货交易订单
+     *
+     * @param userId 管理员编号
+     * @param deliveryReqVO 发货请求
+     */
+    void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO);
+
     /**
      * 获得指定用户,指定的交易订单
      *
@@ -33,14 +50,6 @@ public interface TradeOrderService {
      */
     TradeOrderDO getOrder(Long userId, Long orderId);
 
-    /**
-     * 更新交易订单已支付
-     *
-     * @param id 交易订单编号
-     * @param payOrderId 支付订单编号
-     */
-    void updateOrderPaid(Long id, Long payOrderId);
-
     // =================== Order Item ===================
 
     /**

+ 137 - 89
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;
 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
 import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
+import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO.Item;
 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
@@ -107,94 +108,6 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         return tradeOrderDO.getId();
     }
 
-    @Override
-    public TradeOrderDO getOrder(Long userId, Long orderId) {
-        TradeOrderDO order = tradeOrderMapper.selectById(orderId);
-        if (order != null
-                && ObjectUtil.notEqual(order.getUserId(), userId)) {
-            return null;
-        }
-        return order;
-    }
-
-    @Override
-    public void updateOrderPaid(Long id, Long payOrderId) {
-        // 校验并获得交易订单
-        KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPaid(id, payOrderId);
-        TradeOrderDO order = orderResult.getKey();
-        PayOrderRespDTO payOrder = orderResult.getValue();
-
-        // 更新 TradeOrderDO 状态为已支付,等待发货
-        int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
-                new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayed(true)
-                        .setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
-        if (updateCount == 0) {
-            throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
-        }
-
-        // TODO 芋艿:发送订单变化的消息
-
-        // TODO 芋艿:发送站内信
-
-        // TODO 芋艿:OrderLog
-    }
-
-    /**
-     * 校验交易订单满足被支付的条件
-     *
-     * 1. 交易订单未支付
-     * 2. 支付单已支付
-     *
-     * @param id 交易订单编号
-     * @param payOrderId 支付订单编号
-     * @return 交易订单
-     */
-    private KeyValue<TradeOrderDO, PayOrderRespDTO> validateOrderPaid(Long id, Long payOrderId) {
-        // 校验订单是否存在
-        TradeOrderDO order = tradeOrderMapper.selectById(id);
-        if (order == null) {
-            throw exception(ORDER_NOT_FOUND);
-        }
-        // 校验订单未支付
-        if (TradeOrderStatusEnum.isUnpaid(order.getStatus())) { // 状态
-            log.error("[validateOrderPaid][order({}) 不处于待支付状态,请进行处理!order 数据是:{}]",
-                    id, JsonUtils.toJsonString(order));
-            throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
-        }
-        // 校验支付订单匹配
-        if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
-            log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]",
-                    id, payOrderId, JsonUtils.toJsonString(order));
-            throw exception(ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR);
-        }
-
-        // 校验支付单是否存在
-        PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
-        if (payOrder == null) {
-            log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId);
-            throw exception(PAY_ORDER_NOT_FOUND);
-        }
-        // 校验支付单已支付
-        if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
-            log.error("[validateOrderPaid][order({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]",
-                     id, payOrderId, JsonUtils.toJsonString(payOrder));
-            throw exception(ORDER_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS);
-        }
-        // 校验支付金额一致
-        if (ObjectUtil.notEqual(payOrder.getAmount(), order.getPayPrice())) {
-            log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]",
-                     id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
-            throw exception(ORDER_UPDATE_PAID_PAY_PRICE_NOT_MATCH);
-        }
-        // 校验支付订单匹配(二次)
-        if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
-            log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]",
-                    id, payOrderId, JsonUtils.toJsonString(payOrder));
-            throw exception(ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR);
-        }
-        return new KeyValue<>(order, payOrder);
-    }
-
     /**
      * 校验商品 SKU 是否可出售
      *
@@ -269,7 +182,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         tradeOrderDO.setProductCount(getSumValue(order.getItems(),  PriceCalculateRespDTO.OrderItem::getCount, Integer::sum));
         tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
         tradeOrderDO.setAdjustPrice(0).setPayed(false); // 支付信息
-        tradeOrderDO.setDeliveryStatus(false); // 物流信息
+        tradeOrderDO.setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus()); // 物流信息
         tradeOrderDO.setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.NONE.getStatus()).setRefundPrice(0); // 退款信息
         tradeOrderMapper.insert(tradeOrderDO);
         return tradeOrderDO;
@@ -324,7 +237,142 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         tradeOrderMapper.updateById(new TradeOrderDO().setId(tradeOrderDO.getId()).setPayOrderId(payOrderId));
     }
 
+    @Override
+    public void updateOrderPaid(Long id, Long payOrderId) {
+        // 校验并获得交易订单(可支付)
+        KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPayable(id, payOrderId);
+        TradeOrderDO order = orderResult.getKey();
+        PayOrderRespDTO payOrder = orderResult.getValue();
+
+        // 更新 TradeOrderDO 状态为已支付,等待发货
+        int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
+                new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayed(true)
+                        .setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
+        if (updateCount == 0) {
+            throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
+        }
+
+        // TODO 芋艿:发送订单变化的消息
+
+        // TODO 芋艿:发送站内信
+
+        // TODO 芋艿:OrderLog
+    }
+
+    /**
+     * 校验交易订单满足被支付的条件
+     *
+     * 1. 交易订单未支付
+     * 2. 支付单已支付
+     *
+     * @param id 交易订单编号
+     * @param payOrderId 支付订单编号
+     * @return 交易订单
+     */
+    private KeyValue<TradeOrderDO, PayOrderRespDTO> validateOrderPayable(Long id, Long payOrderId) {
+        // 校验订单是否存在
+        TradeOrderDO order = tradeOrderMapper.selectById(id);
+        if (order == null) {
+            throw exception(ORDER_NOT_FOUND);
+        }
+        // 校验订单未支付
+        if (!TradeOrderStatusEnum.isUnpaid(order.getStatus()) || order.getPayed()) {
+            log.error("[validateOrderPaid][order({}) 不处于待支付状态,请进行处理!order 数据是:{}]",
+                    id, JsonUtils.toJsonString(order));
+            throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
+        }
+        // 校验支付订单匹配
+        if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
+            log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]",
+                    id, payOrderId, JsonUtils.toJsonString(order));
+            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
+        }
+
+        // 校验支付单是否存在
+        PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
+        if (payOrder == null) {
+            log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId);
+            throw exception(PAY_ORDER_NOT_FOUND);
+        }
+        // 校验支付单已支付
+        if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
+            log.error("[validateOrderPaid][order({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]",
+                    id, payOrderId, JsonUtils.toJsonString(payOrder));
+            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
+        }
+        // 校验支付金额一致
+        if (ObjectUtil.notEqual(payOrder.getAmount(), order.getPayPrice())) {
+            log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]",
+                    id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
+            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
+        }
+        // 校验支付订单匹配(二次)
+        if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
+            log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]",
+                    id, payOrderId, JsonUtils.toJsonString(payOrder));
+            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
+        }
+        return new KeyValue<>(order, payOrder);
+    }
+
+    @Override
+    public void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO) {
+        // 校验并获得交易订单(可发货)
+        TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
+
+        // TODO 芋艿:logisticsId 校验存在
+
+        // 更新 TradeOrderDO 状态为已发货,等待收货
+        int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
+                new TradeOrderDO().setStatus(TradeOrderStatusEnum.DELIVERED.getStatus())
+                        .setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo())
+                        .setDeliveryStatus(TradeOrderDeliveryStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now()));
+        if (updateCount == 0) {
+            throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
+        }
+
+        // TODO 芋艿:发送订单变化的消息
+
+        // TODO 芋艿:发送站内信
+
+        // TODO 芋艿:OrderLog
+
+        // TODO 设计:like:是否要单独一个 delivery 发货单表???
+        // TODO 设计:niu:要不要支持一个订单下,多个 order item 单独发货,类似有赞
+        // TODO 设计:lili:是不是发货后,才支持售后?
+    }
+
+    /**
+     * 校验交易订单满足被发货的条件
+     *
+     * 1. 交易订单未发货
+     *
+     * @param id 交易订单编号
+     * @return 交易订单
+     */
+    private TradeOrderDO validateOrderDeliverable(Long id) {
+        // 校验订单是否存在
+        TradeOrderDO order = tradeOrderMapper.selectById(id);
+        if (order == null) {
+            throw exception(ORDER_NOT_FOUND);
+        }
+        // 校验订单是否是待发货状态
+        if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())
+            || ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus())) {
+            throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
+        }
+        return order;
+    }
 
+    @Override
+    public TradeOrderDO getOrder(Long userId, Long orderId) {
+        TradeOrderDO order = tradeOrderMapper.selectById(orderId);
+        if (order != null
+                && ObjectUtil.notEqual(order.getUserId(), userId)) {
+            return null;
+        }
+        return order;
+    }
 
     // =================== Order Item ===================
 

+ 35 - 9
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;
 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
 import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
+import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
-import cn.iocoder.yudao.module.trade.enums.order.TradeOrderAfterSaleStatusEnum;
-import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
-import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
-import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
+import cn.iocoder.yudao.module.trade.enums.order.*;
 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig;
 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
 import org.junit.jupiter.api.BeforeEach;
@@ -38,6 +36,8 @@ import java.util.Arrays;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
 import static java.util.Collections.singletonList;
 import static org.junit.jupiter.api.Assertions.*;
@@ -173,8 +173,8 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
         assertEquals(tradeOrderDO.getPayOrderId(), 1000L);
         assertNull(tradeOrderDO.getPayChannelCode());
         assertNull(tradeOrderDO.getDeliveryTemplateId());
-        assertNull(tradeOrderDO.getExpressNo());
-        assertFalse(tradeOrderDO.getDeliveryStatus());
+        assertNull(tradeOrderDO.getLogisticsId());
+        assertEquals(tradeOrderDO.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus());
         assertNull(tradeOrderDO.getDeliveryTime());
         assertNull(tradeOrderDO.getReceiveTime());
         assertEquals(tradeOrderDO.getReceiverName(), "芋艿");
@@ -246,11 +246,11 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
     }
 
     @Test
-    public void updateOrderPaid() {
+    public void testUpdateOrderPaid() {
         // mock 数据(TradeOrder)
         TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> {
             o.setId(1L).setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
-            o.setPayOrderId(10L).setPayPrice(100).setPayTime(null);
+            o.setPayOrderId(10L).setPayed(false).setPayPrice(100).setPayTime(null);
         });
         tradeOrderMapper.insert(order);
         // 准备参数
@@ -260,13 +260,39 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
         when(payOrderApi.getOrder(eq(10L))).thenReturn(randomPojo(PayOrderRespDTO.class,
                 o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()).setChannelCode("wx_pub")
                         .setMerchantOrderId("1")).setAmount(100));
+
         // 调用
         tradeOrderService.updateOrderPaid(id, payOrderId);
         // 断言
         TradeOrderDO dbOrder = tradeOrderMapper.selectById(id);
-        assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.PAID.getStatus());
+        assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
         assertTrue(dbOrder.getPayed());
         assertNotNull(dbOrder.getPayTime());
         assertEquals(dbOrder.getPayChannelCode(), "wx_pub");
     }
+
+    @Test
+    public void testDeliveryOrder() {
+        // mock 数据(TradeOrder)
+        TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> {
+            o.setId(1L).setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
+            o.setLogisticsId(null).setLogisticsNo(null).setDeliveryTime(null)
+                    .setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus());
+        });
+        tradeOrderMapper.insert(order);
+        // 准备参数
+        TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO().setId(1L)
+                .setLogisticsId(10L).setLogisticsNo("100");
+        // mock 方法(支付单)
+
+        // 调用
+        tradeOrderService.deliveryOrder(randomLongId(), deliveryReqVO);
+        // 断言
+        TradeOrderDO dbOrder = tradeOrderMapper.selectById(1L);
+        assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.DELIVERED.getStatus());
+        assertEquals(dbOrder.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.DELIVERED.getStatus());
+        assertPojoEquals(dbOrder, deliveryReqVO);
+        assertNotNull(dbOrder.getDeliveryTime());
+    }
+
 }

+ 3 - 2
yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql

@@ -20,10 +20,11 @@ CREATE TABLE IF NOT EXISTS "trade_order" (
      "delivery_price" int NOT NULL,
      "adjust_price" int NOT NULL,
      "pay_price" int NOT NULL,
-     "pay_order_id" int,
+     "pay_order_id" bigint,
      "pay_channel_code" varchar,
      "delivery_template_id" bigint,
-     "express_no" varchar,
+     "logistics_id" bigint,
+     "logistics_no" varchar,
      "delivery_status" bit NOT NULL,
      "delivery_time" datetime,
      "receive_time" datetime,

+ 2 - 2
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 {
     /**
      * 支付金额,单位:分
      */
-    private Long payAmount;
+    private Integer payAmount;
     /**
      * 退款金额,单位:分
      */
-    private Long refundAmount;
+    private Integer refundAmount;
 
     /**
      * 退款原因

+ 1 - 1
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 {
      */
     @NotNull(message = "退款金额不能为空")
     @DecimalMin(value = "0", inclusive = false, message = "退款金额必须大于零")
-    private Long amount;
+    private Integer amount;
 
     /**
      * 退款原因