Procházet zdrojové kódy

feat(order): 添加API订单管理功能
- 新增apiCreateOrder方法实现API外部订单下单功能
- 新增queryByOrderNo方法实现根据订单号查询订单详情
- 新增apiCancelOrder方法实现API取消订单功能
- 更新OrderSourceEnum枚举添加小程序、H5、API订单来源类型

hurx před 2 týdny
rodič
revize
341b8b8ecb
28 změnil soubory, kde provedl 2488 přidání a 25 odebrání
  1. 3 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemotePartnerPreparedService.java
  2. 104 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/domain/dto/PartnerPreparedDTO.java
  3. 122 0
      ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/domain/dto/PartnerPreparedProductDTO.java
  4. 82 0
      ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/RemoteApiOrderService.java
  5. 166 0
      ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/bo/ApiApplyReturnBo.java
  6. 90 0
      ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/bo/ApiOrderDeliverBo.java
  7. 35 0
      ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/bo/ApiWithdrawReturnBo.java
  8. 83 0
      ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/bo/RemoteOrderBo.java
  9. 181 0
      ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/dto/ApiOrderDeliverDto.java
  10. 69 0
      ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/dto/ApiTrackDto.java
  11. 265 0
      ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/dto/OrderDto.java
  12. 19 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/OrderSourceEnum.java
  13. 8 2
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemotePartnerPreparedServiceImpl.java
  14. 2 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IPartnerPreparedService.java
  15. 5 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/PartnerPreparedServiceImpl.java
  16. 1 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/OrderMainController.java
  17. 1 1
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/mini/MiniOrderController.java
  18. 1 1
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/pc/PcOrderController.java
  19. 160 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/ApiOrderReturnBo.java
  20. 75 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/ApiSubmitOrderBo.java
  21. 2 1
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderMainBo.java
  22. 266 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/dubbo/RemoteApiOrderServiceImpl.java
  23. 21 4
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderDeliverService.java
  24. 34 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderMainService.java
  25. 22 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderReturnService.java
  26. 83 0
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderDeliverServiceImpl.java
  27. 417 9
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderMainServiceImpl.java
  28. 171 6
      ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderReturnServiceImpl.java

+ 3 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/RemotePartnerPreparedService.java

@@ -12,6 +12,9 @@ import java.util.Set;
  */
 public interface RemotePartnerPreparedService {
 
+    /*查询备货单详情*/
+    PartnerPreparedDTO getPrepareOrderDetail(String prepareOrderNo);
+
     PartnerPreparedDTO updatePartnerPreparedById(String id);
 
     PartnerPreparedDTO cancelPartnerPreparedById(String prepareOrderNo, String cancelReason);

+ 104 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/domain/dto/PartnerPreparedDTO.java

@@ -3,6 +3,7 @@ package org.dromara.customer.api.domain.dto;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.util.Date;
 
 /**
  * author
@@ -12,7 +13,110 @@ import java.io.Serializable;
 public class PartnerPreparedDTO implements Serializable {
     private static final long serialVersionUID = 1L;
 
+    /**
+     * ID
+     */
+    private Long id;
+
+    /**
+     * 伙伴商ID(关联partner_info.id)
+     */
+    private Long partnerId;
+
+    /**
+     * 备货单编号
+     */
+    private String preparedNo;
+
+    /**
+     * 合作项目代码(关联字典 cooperation_code)
+     */
+    private String cooperationCode;
+
+    /**
+     * 客户名称
+     */
+    private String customerName;
+
+    /**
+     * 采购单位名称
+     */
+    private String purchasingUnitName;
+
+    /**
+     * 备货金额
+     */
+    private Long amount;
+
+    /**
+     * 区域
+     */
+    private String area;
+
+    /**
+     * 预计时间
+     */
+    private Date estimatedTime;
+
+    /**
+     * 到期日期
+     */
+    private Date dueDate;
+
+    /**
+     * 发票附件
+     */
+    private String invoiceAttachment;
+
+    /**
+     * 采购单位
+     */
+    private String purchasingUnit;
+
+    /**
+     * 联系人
+     */
+    private String person;
+
+    /**
+     * 部门
+     */
+    private String deptName;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 备货状态(0=待审核 1=已通过 2=已驳回)
+     */
+    private Long preparedStatus;
+
+    /**
+     * 处理状态(0=未处理 1=处理中 2=已完成)
+     */
+    private Long dealStatus;
+
+    /**
+     * 驳回原因
+     */
+    private String reject;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    private Date createTime;
+
     private boolean flag;
 
     private String message;
+
 }

+ 122 - 0
ruoyi-api/ruoyi-api-customer/src/main/java/org/dromara/customer/api/domain/dto/PartnerPreparedProductDTO.java

@@ -0,0 +1,122 @@
+package org.dromara.customer.api.domain.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * author
+ * 时间:2026/2/3,17:51
+ */
+@Data
+public class PartnerPreparedProductDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    private Long id;
+
+    /**
+     * 伙伴商ID(关联partner_info.id)
+     */
+    private Long partnerId;
+
+    /**
+     * 备货单编号
+     */
+    private String preparedNo;
+
+    /**
+     * 合作项目代码(关联字典 cooperation_code)
+     */
+    private String cooperationCode;
+
+    /**
+     * 客户名称
+     */
+    private String customerName;
+
+    /**
+     * 采购单位名称
+     */
+    private String purchasingUnitName;
+
+    /**
+     * 备货金额
+     */
+    private Long amount;
+
+    /**
+     * 区域
+     */
+    private String area;
+
+    /**
+     * 预计时间
+     */
+    private Date estimatedTime;
+
+    /**
+     * 到期日期
+     */
+    private Date dueDate;
+
+    /**
+     * 发票附件
+     */
+    private String invoiceAttachment;
+
+    /**
+     * 采购单位
+     */
+    private String purchasingUnit;
+
+    /**
+     * 联系人
+     */
+    private String person;
+
+    /**
+     * 部门
+     */
+    private String deptName;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 备货状态(0=待审核 1=已通过 2=已驳回)
+     */
+    private Long preparedStatus;
+
+    /**
+     * 处理状态(0=未处理 1=处理中 2=已完成)
+     */
+    private Long dealStatus;
+
+    /**
+     * 驳回原因
+     */
+    private String reject;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    private Date createTime;
+
+    private boolean flag;
+
+    private String message;
+
+}

+ 82 - 0
ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/RemoteApiOrderService.java

@@ -0,0 +1,82 @@
+package org.dromara.product.api;
+
+import org.dromara.product.api.domain.bo.ApiApplyReturnBo;
+import org.dromara.product.api.domain.bo.ApiOrderDeliverBo;
+import org.dromara.product.api.domain.bo.ApiWithdrawReturnBo;
+import org.dromara.product.api.domain.bo.RemoteOrderBo;
+import org.dromara.product.api.domain.dto.ApiOrderDeliverDto;
+import org.dromara.product.api.domain.dto.ApiTrackDto;
+import org.dromara.product.api.domain.dto.OrderDto;
+
+import java.util.List;
+
+/**
+ * @author
+ * @date 2026/5/15 上午9:08
+ */
+public interface RemoteApiOrderService {
+
+    /**
+     * 创建订单
+     *
+     * @param remoteOrderBo 订单信息
+     */
+    Long apiCreateOrder(RemoteOrderBo remoteOrderBo);
+
+    /**
+     * 获取订单信息
+     *
+     * @param orderNo 订单编号
+     */
+    OrderDto apiGetOrder(String orderNo);
+
+    /*订单确认*/
+    void confirmOrder(RemoteOrderBo remoteOrderBo);
+
+    /**
+     * 确认收货
+     *
+     * @param remoteOrderBo 订单信息(包含orderId或orderNo)
+     * @return 是否成功
+     */
+    Boolean apiConfirmReceipt(RemoteOrderBo remoteOrderBo);
+
+    /**
+     * 取消订单
+     *
+     * @param remoteOrderBo 订单信息
+     */
+    int apiCancelOrder(RemoteOrderBo remoteOrderBo);
+
+    /**
+     * 申请售后
+     *
+     * @param apiApplyReturnBo 售后申请信息
+     * @return 售后单ID
+     */
+    Long apiApplyReturn(ApiApplyReturnBo apiApplyReturnBo);
+
+    /**
+     * 撤回售后
+     *
+     * @param apiWithdrawReturnBo 撤回售后信息(包含returnId或returnNo)
+     * @return 影响行数
+     */
+    int apiWithdrawReturn(ApiWithdrawReturnBo apiWithdrawReturnBo);
+
+    /**
+     * 查看当前订单的发货记录信息
+     *
+     * @param apiOrderDeliverBo 订单发货查询信息(包含orderId或orderNo)
+     * @return 发货记录列表
+     */
+    List<ApiOrderDeliverDto> selectOrderDeliver(ApiOrderDeliverBo apiOrderDeliverBo);
+
+    /**
+     * 查看物流信息
+     *
+     * @param apiOrderDeliverBo 售后单编号
+     * @return 物流轨迹信息
+     */
+    ApiTrackDto selectTrack(ApiOrderDeliverBo apiOrderDeliverBo);
+}

+ 166 - 0
ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/bo/ApiApplyReturnBo.java

@@ -0,0 +1,166 @@
+package org.dromara.product.api.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * API申请售后业务对象
+ *
+ * @author System
+ * @date 2026-05-15
+ */
+@Data
+public class ApiApplyReturnBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 原订单ID(orderId和orderNo二选一)
+     */
+    private Long orderId;
+
+    /**
+     * 原订单编号(orderId和orderNo二选一)
+     */
+    private String orderNo;
+
+    /**
+     * 售后服务类型(如:退货、换货、仅退款等)
+     */
+    @NotBlank(message = "售后服务类型不能为空")
+    private String serviceType;
+
+    /**
+     * 退货原因id
+     */
+    private Long returnReasonId;
+
+    /**
+     * 退货原因
+     */
+    private String returnReason;
+
+    /**
+     * 问题描述
+     */
+    private String problemDescription;
+
+    /**
+     * 凭证图片URL(多张图片用逗号分隔)
+     */
+    private String voucherPhoto;
+
+    /**
+     * 运费金额
+     */
+    private BigDecimal shippingFee;
+
+    /**
+     * 是否承担运费(0-否 1-是)
+     */
+    private String isShippingFee;
+
+    /**
+     * 售后备注
+     */
+    private String afterSalesRemarks;
+
+    /**
+     * 退货方式(如:上门取件、自行寄回)
+     */
+    private String returnMethod;
+
+    /**
+     * 取件人姓名(上门取件时必填)
+     */
+    private String chargebackName;
+
+    /**
+     * 取件联系电话(上门取件时必填)
+     */
+    private String chargebackPhone;
+
+    /**
+     * 预约取件时间(上门取件时必填,格式:yyyy-MM-dd HH:mm:ss)
+     */
+    private String chargebackPickupTime;
+
+    /**
+     * 取件省(上门取件时必填)
+     */
+    private String chargebackProvincial;
+
+    /**
+     * 取件市(上门取件时必填)
+     */
+    private String chargebackCity;
+
+    /**
+     * 取件区/县(上门取件时必填)
+     */
+    private String chargebackCounty;
+
+    /**
+     * 取件详细地址(上门取件时必填)
+     */
+    private String chargebackAddress;
+
+    /**
+     * 退货商品列表
+     */
+    @NotNull(message = "退货商品列表不能为空")
+    private List<ApiReturnItem> returnItems;
+
+    /**
+     * API退货商品项
+     */
+    @Data
+    public static class ApiReturnItem implements Serializable {
+        @Serial
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 原订单明细ID
+         */
+        @NotNull(message = "订单明细ID不能为空")
+        private Long orderProductId;
+
+        /**
+         * 商品ID
+         */
+        private Long productId;
+
+        /**
+         * 商品名称
+         */
+        private String productName;
+
+        /**
+         * 本次退货数量
+         */
+        @NotNull(message = "退货数量不能为空")
+        private Long returnQuantity;
+
+        /**
+         * 商品单价
+         */
+        private BigDecimal unitPrice;
+
+        /**
+         * 该商品的具体退货原因
+         */
+        private String reasonDetail;
+
+        /**
+         * 备注
+         */
+        private String remark;
+    }
+}

+ 90 - 0
ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/bo/ApiOrderDeliverBo.java

@@ -0,0 +1,90 @@
+package org.dromara.product.api.domain.bo;
+
+import lombok.Data;
+
+@Data
+public class ApiOrderDeliverBo {
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 订单id
+     */
+    private Long orderId;
+
+    /**
+     * 订单编号
+     */
+    private String orderCode;
+
+    /**
+     * 订单发货编号
+     */
+    private String deliverCode;
+
+    /**
+     * 物流包裹号
+     */
+    private String logisticPackNo;
+
+    /**
+     * 发货方式
+     */
+    private String deliverMethod;
+
+    /**
+     * 送货人姓名
+     */
+    private String deliverMan;
+
+    /**
+     * 送货人联系电话
+     */
+    private String phone;
+
+    /**
+     * 物流状态
+     */
+    private String logisticsStatus;
+
+    /**
+     * 发货备注
+     */
+    private String deliverRemark;
+
+    /**
+     * 装箱/验货备注
+     */
+    private String checklistRemark;
+
+    /**
+     * 承运物流公司
+     */
+    private Long logisticsCompanyId;
+
+    private String logisticsCompanyCode;
+
+    /**
+     * 物流单号
+     */
+    private String logisticNo;
+
+    /**
+     * 包裹状态
+     */
+    private String logisticPackStatus;
+
+    /**
+     * 收货人电话
+     */
+    private String consigneePhone;
+
+    private String dataSource;
+
+    /**
+     * 备注
+     */
+    private String remark;
+}

+ 35 - 0
ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/bo/ApiWithdrawReturnBo.java

@@ -0,0 +1,35 @@
+package org.dromara.product.api.domain.bo;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * API撤回售后业务对象
+ *
+ * @author System
+ * @date 2026-05-15
+ */
+@Data
+public class ApiWithdrawReturnBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 售后单ID(returnId和returnNo二选一)
+     */
+    private Long returnId;
+
+    /**
+     * 售后单号(returnId和returnNo二选一)
+     */
+    private String returnNo;
+
+    /**
+     * 撤回原因
+     */
+    private String withdrawReason;
+}

+ 83 - 0
ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/bo/RemoteOrderBo.java

@@ -0,0 +1,83 @@
+package org.dromara.product.api.domain.bo;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Set;
+
+@Data
+public class RemoteOrderBo implements Serializable {
+    /**
+     * 订单ID(用于取消订单等操作)
+     */
+    private Long orderId;
+
+    /**
+     * 订单编号(用于取消订单等操作)
+     */
+    private String orderNo;
+
+    /**
+     * 收货地址ID
+     */
+    @NotNull(message = "收货地址不能为空")
+    private Long shippingAddressId;
+
+    /**
+     * 配送时间(格式:yyyy-MM-dd)
+     */
+//    @NotBlank(message = "配送时间不能为空")
+    private String deliveryDate; // 前端传字符串,后端转为 Date
+
+
+    /*支付方式*/
+    private String payType;
+
+    /*费用类型*/
+    private String expenseType;
+
+    /**
+     * 采购事由
+     */
+//    @NotBlank(message = "采购事由不能为空")
+    private String purchaseReason;
+
+    /**
+     * 订单备注
+     */
+    private String remark;
+
+    /**
+     * 运费(元)
+     */
+    private BigDecimal shippingFee;
+
+    /**
+     * 订单来源
+     */
+    private String orderSource;
+
+    /**
+     * 商品信息
+     */
+    private List<ApiOrderProduct> productInfo;
+
+    @Data
+    public class ApiOrderProduct {
+
+        /**
+         * 商品ID
+         */
+        @NotNull(message = "商品ID不能为空")
+        private Long productId;
+
+        /**
+         * 商品数量
+         */
+        @NotNull(message = "商品数量不能为空")
+        private Long productNum;
+    }
+}

+ 181 - 0
ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/dto/ApiOrderDeliverDto.java

@@ -0,0 +1,181 @@
+package org.dromara.product.api.domain.dto;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.dromara.product.api.domain.bo.ApiApplyReturnBo;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * API订单发货记录DTO
+ *
+ * @author System
+ * @date 2026-05-15
+ */
+@Data
+public class ApiOrderDeliverDto implements Serializable {
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 订单id
+     */
+    private Long orderId;
+
+    /**
+     * 订单编号
+     */
+    private String orderCode;
+
+    /**
+     * 订单发货编号
+     */
+    private String deliverCode;
+
+    /**
+     * 物流包裹号
+     */
+    private String logisticPackNo;
+
+    /**
+     * 发货方式
+     */
+    private String deliverMethod;
+
+    /**
+     * 送货人姓名
+     */
+    private String deliverMan;
+
+    /**
+     * 送货人联系电话
+     */
+    private String phone;
+
+    /**
+     * 物流状态
+     */
+    private String logisticsStatus;
+
+    /**
+     * 发货备注
+     */
+    private String deliverRemark;
+
+    /**
+     * 装箱/验货备注
+     */
+    private String checklistRemark;
+
+    /**
+     * 承运物流公司
+     */
+    private Long logisticsCompanyId;
+
+    private String logisticsCompanyCode;
+
+    /**
+     * 物流单号
+     */
+    private String logisticNo;
+
+    /**
+     * 包裹状态
+     */
+    private String logisticPackStatus;
+
+    /**
+     * 收货人电话
+     */
+    private String consigneePhone;
+
+    private String dataSource;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 退货商品列表
+     */
+    private List<ApiOrderDeliverProduct> deliverProductList;
+
+    @Data
+    public class ApiOrderDeliverProduct implements Serializable {
+
+        @Serial
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 主键ID
+         */
+        private Long id;
+
+        /**
+         * 发货单
+         */
+        private Long deliverId;
+
+        /**
+         * 商品编号
+         */
+        private Long productId;
+
+        /**
+         * 产品编号(业务编码)
+         */
+        private String productNo;
+
+        /**
+         * 商品名称
+         */
+        private String productName;
+
+        /**
+         * 商品单位
+         */
+        private Long productUnitId;
+
+        /**
+         * 商品单位
+         */
+        private String productUnit;
+
+        /**
+         * 本次发货数量
+         */
+        private Long deliverNum;
+
+        /**
+         * 订单单价(元)
+         */
+        private BigDecimal orderPrice;
+
+        /**
+         * 行小计金额(元)
+         */
+        private BigDecimal subtotal;
+
+        /**
+         * 备注
+         */
+        private String remark;
+
+        private String dataSource;
+
+        private String orderNo;
+
+
+    }
+
+    private boolean flag;
+
+    private String message;
+}

+ 69 - 0
ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/dto/ApiTrackDto.java

@@ -0,0 +1,69 @@
+package org.dromara.product.api.domain.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class ApiTrackDto implements Serializable {
+    /**
+     * 消息体
+     */
+    private String message;
+
+    /**
+     * 快递单当前状态,默认为0在途,1揽收,2疑难,3签收,4退签,5派件,8清关,14拒签等10个基础物流状态
+     */
+    private String state;
+
+    /**
+     * 是否签收标记,0未签收,1已签收,请忽略,明细状态请参考state字段
+     */
+    private String ischeck;
+
+    /**
+     * 快递单明细状态标记,暂未实现,请忽略
+     */
+    private String condition;
+
+    /**
+     * 快递公司编码,一律用小写字母
+     */
+    private String com;
+
+    /**
+     * 单号
+     */
+    private String nu;
+
+    /**
+     * 通讯状态,请忽略
+     */
+    private String status;
+
+    /**
+     * 最新查询结果,数组,包含多项,全量,倒序(即时间最新的在最前),每项都是对象,对象包含字段请展开
+     */
+    private List<TrackData> data;
+
+    @Data
+    public class TrackData implements Serializable {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 时间,原始格式
+         */
+        private String time;
+
+        /**
+         * 格式化后时间
+         */
+        private String ftime;
+
+        /**
+         * 内容
+         */
+        private String context;
+    }
+}

+ 265 - 0
ruoyi-api/ruoyi-api-order/src/main/java/org/dromara/product/api/domain/dto/OrderDto.java

@@ -0,0 +1,265 @@
+package org.dromara.product.api.domain.dto;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * API订单DTO
+ *
+ * @author System
+ * @date 2026-05-15
+ */
+@Data
+public class OrderDto implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 订单编号
+     */
+    private String orderNo;
+
+    /**
+     * 客户ID
+     */
+    private Long customerId;
+
+    /**
+     * 客户编号
+     */
+    private String customerCode;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 收货地址ID
+     */
+    private Long shippingAddressId;
+
+    /**
+     * 采购事由
+     */
+    private String purchaseReason;
+
+    /**
+     * 支付方式
+     */
+    private String payType;
+
+    /**
+     * 发货仓库
+     */
+    private Long warehouseId;
+
+    /**
+     * 预计送达时间
+     */
+    private Date expectedDeliveryTime;
+
+    /**
+     * 商品总数量
+     */
+    private Long productQuantity;
+
+    /**
+     * 运费(元)
+     */
+    private BigDecimal shippingFee;
+
+    /**
+     * 订单总金额(元)
+     */
+    private BigDecimal totalAmount;
+
+    /**
+     * 应付金额(元)
+     */
+    private BigDecimal payableAmount;
+
+    /**
+     * 支付状态
+     */
+    private String paymentStatus;
+
+    /**
+     * 订单来源
+     */
+    private String orderSource;
+
+    /**
+     * 订单状态
+     */
+    private String orderStatus;
+
+    /**
+     * 下单时间
+     */
+    private Date orderTime;
+
+    /**
+     * 确认时间
+     */
+    private Date confirmTime;
+
+    /**
+     * 发货时间
+     */
+    private Date shippingTime;
+
+    /**
+     * 收货时间
+     */
+    private Date receivingTime;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 审核状态(0 待审核 1已通过 2已驳回)
+     */
+    private String checkStatus;
+
+    /**
+     * 是否需要审核(0 需要审核 1不需要)
+     */
+    private String isNeedCheck;
+
+    /**
+     * 费用类型
+     */
+    private String expenseType;
+
+    /**
+     * 订单商品列表
+     */
+    private List<OrderProductDto> orderProductList;
+
+    /**
+     * 订单商品DTO
+     */
+    @Data
+    public static class OrderProductDto implements Serializable {
+        @Serial
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 主键ID
+         */
+        private Long id;
+
+        /**
+         * 订单ID
+         */
+        private Long orderId;
+
+        /**
+         * 订单编号
+         */
+        private String orderNo;
+
+        /**
+         * 商品ID
+         */
+        private Long productId;
+
+        /**
+         * 商品编码
+         */
+        private String productNo;
+
+        /**
+         * 商品名称
+         */
+        private String productName;
+
+        /**
+         * 商品单位ID
+         */
+        private Long productUnitId;
+
+        /**
+         * 商品单位
+         */
+        private String productUnit;
+
+        /**
+         * 商品图片
+         */
+        private String productImage;
+
+        /**
+         * 下单价格
+         */
+        private BigDecimal orderPrice;
+
+        /**
+         * 市场价格
+         */
+        private BigDecimal marketPrice;
+
+        /**
+         * 税率
+         */
+        private BigDecimal taxRate;
+
+        /**
+         * 分类名称
+         */
+        private String categoryName;
+
+        /**
+         * 采购价
+         */
+        private BigDecimal purchasingPrice;
+
+        /**
+         * 最小起订量
+         */
+        private Long minOrderQuantity;
+
+        /**
+         * 最低售价
+         */
+        private BigDecimal minSellingPrice;
+
+        /**
+         * 下单数量
+         */
+        private Long orderQuantity;
+
+        /**
+         * 已发货数量
+         */
+        private Long deliveredQuantity;
+
+        /**
+         * 未发货数量
+         */
+        private Long undeliveredQuantity;
+
+        /**
+         * 小计
+         */
+        private BigDecimal subtotal;
+    }
+
+    private boolean flag;
+
+    private String message;
+}

+ 19 - 1
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/OrderSourceEnum.java

@@ -9,7 +9,23 @@ public enum OrderSourceEnum {
     /**
      * 前台提交
      */
-    BEFORE_ADD("0", "前台提交"),
+    PC_ADD("0", "pc"),
+
+    /**
+     * 小程序提交
+     */
+    MINI_ADD("2", "mini小程序"),
+
+    /**
+     * h5提交
+     */
+    H5_ADD("3", "h5"),
+
+    /**
+     * api提交
+     */
+    Api_ADD("4", "api"),
+
 
     /**
      * 后台新增
@@ -17,6 +33,8 @@ public enum OrderSourceEnum {
     AFTER_ADD("1", "后台新增");
 
 
+
+
     private final String code;
     private final String info;
 

+ 8 - 2
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/dubbo/RemotePartnerPreparedServiceImpl.java

@@ -5,13 +5,14 @@ import lombok.RequiredArgsConstructor;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.dromara.common.core.domain.zhongche.domain.ZhongChePrepareOrderGoods;
 import org.dromara.common.core.domain.zhongche.vo.PrepareOrderDetailRespVo;
+import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.customer.api.RemotePartnerPreparedService;
 import org.dromara.customer.api.domain.dto.PartnerPreparedDTO;
 import org.dromara.customer.domain.PartnerPrepared;
 import org.dromara.customer.domain.PartnerPreparedProduct;
 import org.dromara.customer.domain.PartnerPreparedProductThird;
 import org.dromara.customer.domain.PartnerPreparedThird;
-import org.dromara.customer.enums.PartnerPreparedStatusEnum;
+import org.dromara.customer.domain.vo.PartnerPreparedVo;
 import org.dromara.customer.service.IPartnerPreparedProductService;
 import org.dromara.customer.service.IPartnerPreparedProductThirdService;
 import org.dromara.customer.service.IPartnerPreparedService;
@@ -19,7 +20,6 @@ import org.dromara.customer.service.IPartnerPreparedThirdService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
@@ -44,6 +44,12 @@ public class RemotePartnerPreparedServiceImpl implements RemotePartnerPreparedSe
     private final IPartnerPreparedProductThirdService partnerPreparedProductThirdService;
 
 
+    @Override
+    public PartnerPreparedDTO getPrepareOrderDetail(String prepareOrderNo) {
+        PartnerPreparedVo prepareOrderDetail = partnerPreparedService.getPrepareOrderDetail(prepareOrderNo);
+        return MapstructUtils.convert(prepareOrderDetail, PartnerPreparedDTO.class);
+    }
+
     @Override
     public PartnerPreparedDTO updatePartnerPreparedById(String id) {
         PartnerPreparedDTO partnerPreparedDTO = new PartnerPreparedDTO();

+ 2 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IPartnerPreparedService.java

@@ -73,4 +73,6 @@ public interface IPartnerPreparedService extends IService<PartnerPrepared> {
     String generatePreparedNo();
 
     Map<Long, String> selectPreparedOrderNoByIds(Set<Long> ids);
+
+    PartnerPreparedVo getPrepareOrderDetail(String prepareOrderNo);
 }

+ 5 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/PartnerPreparedServiceImpl.java

@@ -235,4 +235,9 @@ public class PartnerPreparedServiceImpl extends ServiceImpl<PartnerPreparedMappe
         }
         return resultMap;
     }
+
+    @Override
+    public PartnerPreparedVo getPrepareOrderDetail(String prepareOrderNo) {
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<PartnerPrepared>().eq(PartnerPrepared::getPreparedNo, prepareOrderNo).last("LIMIT 1"));
+    }
 }

+ 1 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/OrderMainController.java

@@ -132,6 +132,7 @@ public class OrderMainController extends BaseController {
     @Log(title = "订单主信息", businessType = BusinessType.INSERT)
     @PostMapping()
     public R<Void> add(@Validated(AddGroup.class) @RequestBody OrderMainBo bo) {
+        bo.setOrderNoPrefix("Mo");
         orderMainService.insertByBo(bo); // 成功则继续,失败则抛异常
         return R.ok();
     }

+ 1 - 1
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/mini/MiniOrderController.java

@@ -239,7 +239,7 @@ public class MiniOrderController extends BaseController {
             //商城下单默认需要审核--如果设置审批流则不需要
             mainBo.setIsNeedCheck(SysPlatformYesNo.YES.getCode());
 
-            mainBo.setOrderSource(OrderSourceEnum.BEFORE_ADD.getCode());
+            mainBo.setOrderSource(OrderSourceEnum.MINI_ADD.getCode());
 
             // 设置订单商品列表
             // mainBo.setOrderProductBos(bo.getOrderProductBos());

+ 1 - 1
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/controller/pc/PcOrderController.java

@@ -354,7 +354,7 @@ public class PcOrderController extends BaseController {
             //商城下单默认需要审核--如果设置审批流则不需要
             mainBo.setIsNeedCheck(SysPlatformYesNo.YES.getCode());
 
-            mainBo.setOrderSource(OrderSourceEnum.BEFORE_ADD.getCode());
+            mainBo.setOrderSource(OrderSourceEnum.PC_ADD.getCode());
 
             // 设置订单商品列表
             // mainBo.setOrderProductBos(bo.getOrderProductBos());

+ 160 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/ApiOrderReturnBo.java

@@ -0,0 +1,160 @@
+package org.dromara.order.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.dromara.product.api.domain.bo.ApiApplyReturnBo;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class ApiOrderReturnBo {
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 原订单ID(orderId和orderNo二选一)
+     */
+    private Long orderId;
+
+    /**
+     * 原订单编号(orderId和orderNo二选一)
+     */
+    private String orderNo;
+
+    /**
+     * 售后服务类型(如:退货、换货、仅退款等)
+     */
+    @NotBlank(message = "售后服务类型不能为空")
+    private String serviceType;
+
+    /**
+     * 退货原因id
+     */
+    private Long returnReasonId;
+
+    /**
+     * 退货原因
+     */
+    private String returnReason;
+
+    /**
+     * 问题描述
+     */
+    private String problemDescription;
+
+    /**
+     * 凭证图片URL(多张图片用逗号分隔)
+     */
+    private String voucherPhoto;
+
+    /**
+     * 运费金额
+     */
+    private BigDecimal shippingFee;
+
+    /**
+     * 是否承担运费(0-否 1-是)
+     */
+    private String isShippingFee;
+
+    /**
+     * 售后备注
+     */
+    private String afterSalesRemarks;
+
+    /**
+     * 退货方式(如:上门取件、自行寄回)
+     */
+    private String returnMethod;
+
+    /**
+     * 取件人姓名(上门取件时必填)
+     */
+    private String chargebackName;
+
+    /**
+     * 取件联系电话(上门取件时必填)
+     */
+    private String chargebackPhone;
+
+    /**
+     * 预约取件时间(上门取件时必填,格式:yyyy-MM-dd HH:mm:ss)
+     */
+    private String chargebackPickupTime;
+
+    /**
+     * 取件省(上门取件时必填)
+     */
+    private String chargebackProvincial;
+
+    /**
+     * 取件市(上门取件时必填)
+     */
+    private String chargebackCity;
+
+    /**
+     * 取件区/县(上门取件时必填)
+     */
+    private String chargebackCounty;
+
+    /**
+     * 取件详细地址(上门取件时必填)
+     */
+    private String chargebackAddress;
+
+    /**
+     * 退货商品列表
+     */
+    @NotNull(message = "退货商品列表不能为空")
+    private List<ApiApplyReturnBo.ApiReturnItem> returnItems;
+
+    /**
+     * API退货商品项
+     */
+    @Data
+    public static class ApiReturnItem implements Serializable {
+        @Serial
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 原订单明细ID
+         */
+        @NotNull(message = "订单明细ID不能为空")
+        private Long orderProductId;
+
+        /**
+         * 商品ID
+         */
+        private Long productId;
+
+        /**
+         * 商品名称
+         */
+        private String productName;
+
+        /**
+         * 本次退货数量
+         */
+        @NotNull(message = "退货数量不能为空")
+        private Long returnQuantity;
+
+        /**
+         * 商品单价
+         */
+        private BigDecimal unitPrice;
+
+        /**
+         * 该商品的具体退货原因
+         */
+        private String reasonDetail;
+
+        /**
+         * 备注
+         */
+        private String remark;
+    }
+}

+ 75 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/ApiSubmitOrderBo.java

@@ -0,0 +1,75 @@
+package org.dromara.order.domain.bo;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Set;
+
+@Data
+public class ApiSubmitOrderBo {
+
+    /**
+     * 收货地址ID
+     */
+    @NotNull(message = "收货地址不能为空")
+    private Long shippingAddressId;
+
+    /**
+     * 配送时间(格式:yyyy-MM-dd)
+     */
+//    @NotBlank(message = "配送时间不能为空")
+    private String deliveryDate; // 前端传字符串,后端转为 Date
+
+
+    /*支付方式*/
+    private String payType;
+
+    /*费用类型*/
+    private String expenseType;
+
+    /**
+     * 采购事由
+     */
+//    @NotBlank(message = "采购事由不能为空")
+    private String purchaseReason;
+
+    /**
+     * 订单备注
+     */
+    private String remark;
+
+    /**
+     * 运费(元)
+     */
+    private BigDecimal shippingFee;
+
+    /**
+     * 订单来源
+     */
+    private String orderSource;
+
+    /**
+    * 商品信息
+    * */
+    private List<PcOrderProduct> productInfo;
+
+
+    @Data
+    public class PcOrderProduct {
+        /**
+         * 商品ID
+         */
+        @NotNull(message = "商品ID不能为空")
+        private Long productId;
+
+        /**
+         * 商品数量
+         */
+        @NotNull(message = "商品数量不能为空")
+        private Long productNum;
+     }
+}
+
+

+ 2 - 1
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/domain/bo/OrderMainBo.java

@@ -318,5 +318,6 @@ public class OrderMainBo extends BaseEntity {
 
     List<OrderProductBo> orderProductBos;
 
-
+    /*订单编号前缀*/
+    private String orderNoPrefix;
 }

+ 266 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/dubbo/RemoteApiOrderServiceImpl.java

@@ -0,0 +1,266 @@
+package org.dromara.order.dubbo;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.order.domain.bo.*;
+import org.dromara.order.domain.vo.OrderDeliverVo;
+import org.dromara.order.domain.vo.OrderMainVo;
+import org.dromara.order.domain.vo.OrderProductVo;
+import org.dromara.order.service.IOrderMainService;
+import org.dromara.order.service.IOrderDeliverService;
+import org.dromara.order.service.IOrderReturnService;
+import org.dromara.order.utils.kd100.domain.TrackVO;
+import org.dromara.product.api.RemoteApiOrderService;
+import org.dromara.product.api.RemoteOrderInfoService;
+import org.dromara.product.api.domain.bo.ApiApplyReturnBo;
+import org.dromara.product.api.domain.bo.ApiOrderDeliverBo;
+import org.dromara.product.api.domain.bo.ApiWithdrawReturnBo;
+import org.dromara.product.api.domain.bo.RemoteOrderBo;
+import org.dromara.product.api.domain.dto.ApiOrderDeliverDto;
+import org.dromara.product.api.domain.dto.ApiTrackDto;
+import org.dromara.product.api.domain.dto.OrderDto;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@DubboService
+public class RemoteApiOrderServiceImpl implements RemoteApiOrderService {
+
+    private final IOrderMainService orderMainService;
+
+    private final IOrderDeliverService orderDeliverService;
+
+    private final IOrderReturnService orderReturnService;
+
+    @Override
+    public Long apiCreateOrder(RemoteOrderBo remoteOrderBo) {
+        ApiSubmitOrderBo apiSubmitOrderBo = MapstructUtils.convert(remoteOrderBo, ApiSubmitOrderBo.class);
+        return orderMainService.apiCreateOrder(apiSubmitOrderBo);
+    }
+
+    @Override
+    public OrderDto apiGetOrder(String orderNo) {
+        try {
+            // 根据订单号查询订单详情(不绑定customerId、userId)
+            OrderMainVo orderMainVo = orderMainService.queryByOrderNo(orderNo);
+
+            if (orderMainVo == null) {
+                log.warn("API查询订单失败,订单号 {} 不存在", orderNo);
+                return null;
+            }
+
+            // 转换为OrderDto
+            OrderDto orderDto = new OrderDto();
+            orderDto.setId(orderMainVo.getId());
+            orderDto.setOrderNo(orderMainVo.getOrderNo());
+            orderDto.setCustomerId(orderMainVo.getCustomerId());
+            orderDto.setCustomerCode(orderMainVo.getCustomerCode());
+            orderDto.setUserId(orderMainVo.getUserId());
+            orderDto.setShippingAddressId(orderMainVo.getShippingAddressId());
+            orderDto.setPurchaseReason(orderMainVo.getPurchaseReason());
+            orderDto.setPayType(orderMainVo.getPayType());
+            orderDto.setWarehouseId(orderMainVo.getWarehouseId());
+            orderDto.setExpectedDeliveryTime(orderMainVo.getExpectedDeliveryTime());
+            orderDto.setProductQuantity(orderMainVo.getProductQuantity());
+            orderDto.setShippingFee(orderMainVo.getShippingFee());
+            orderDto.setTotalAmount(orderMainVo.getTotalAmount());
+            orderDto.setPayableAmount(orderMainVo.getPayableAmount());
+            orderDto.setPaymentStatus(orderMainVo.getPaymentStatus());
+            orderDto.setOrderSource(orderMainVo.getOrderSource());
+            orderDto.setOrderStatus(orderMainVo.getOrderStatus());
+            orderDto.setOrderTime(orderMainVo.getOrderTime());
+            orderDto.setConfirmTime(orderMainVo.getConfirmTime());
+            orderDto.setShippingTime(orderMainVo.getShippingTime());
+            orderDto.setReceivingTime(orderMainVo.getReceivingTime());
+            orderDto.setRemark(orderMainVo.getRemark());
+            orderDto.setCheckStatus(orderMainVo.getCheckStatus());
+            orderDto.setIsNeedCheck(orderMainVo.getIsNeedCheck());
+            orderDto.setExpenseType(orderMainVo.getExpenseType());
+
+            // 转换订单商品列表
+            if (orderMainVo.getOrderProductList() != null && !orderMainVo.getOrderProductList().isEmpty()) {
+                List<OrderDto.OrderProductDto> productDtos = orderMainVo.getOrderProductList().stream()
+                    .map(this::convertToProductDto)
+                    .collect(Collectors.toList());
+                orderDto.setOrderProductList(productDtos);
+            }
+
+            log.info("API成功查询订单详情,订单号: {}", orderNo);
+            return orderDto;
+        } catch (Exception e) {
+            log.error("API查询订单失败,订单号: {}", orderNo, e);
+            throw new RuntimeException("API查询订单失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public void confirmOrder(RemoteOrderBo remoteOrderBo) {
+        String orderNo = remoteOrderBo.getOrderNo();
+        Long orderId = remoteOrderBo.getOrderId();
+        orderMainService.apiConfirmOrder(orderId, orderNo);
+    }
+
+    @Override
+    public int apiCancelOrder(RemoteOrderBo remoteOrderBo) {
+        try {
+            Long orderId = remoteOrderBo.getOrderId();
+            String orderNo = remoteOrderBo.getOrderNo();
+
+            if (orderId == null && orderNo == null) {
+                throw new IllegalArgumentException("订单ID和订单号不能同时为空");
+            }
+
+            // 调用服务层取消订单(不绑定customerId、userId)
+            int result = orderMainService.apiCancelOrder(orderId, orderNo);
+
+            if (result > 0) {
+                log.info("API成功取消订单,订单ID: {},订单号: {}", orderId, orderNo);
+            } else {
+                log.warn("API取消订单失败,订单可能不存在或状态不允许取消,订单ID: {},订单号: {}", orderId, orderNo);
+            }
+
+            return result;
+        } catch (Exception e) {
+            log.error("API取消订单失败,订单ID: {},订单号: {}", remoteOrderBo.getOrderId(), remoteOrderBo.getOrderNo(), e);
+            throw new RuntimeException("API取消订单失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public Boolean apiConfirmReceipt(RemoteOrderBo remoteOrderBo) {
+        try {
+            Long orderId = remoteOrderBo.getOrderId();
+            String orderNo = remoteOrderBo.getOrderNo();
+
+            if (orderId == null && orderNo == null) {
+                throw new IllegalArgumentException("订单ID和订单号不能同时为空");
+            }
+
+            // 调用服务层确认收货(不绑定customerId、userId)
+            Boolean result = orderMainService.apiConfirmReceipt(orderId, orderNo);
+
+            if (result) {
+                log.info("API成功确认收货,订单ID: {},订单号: {}", orderId, orderNo);
+            } else {
+                log.warn("API确认收货失败,订单可能不存在或状态不允许确认收货,订单ID: {},订单号: {}", orderId, orderNo);
+            }
+
+            return result;
+        } catch (Exception e) {
+            log.error("API确认收货失败,订单ID: {},订单号: {}", remoteOrderBo.getOrderId(), remoteOrderBo.getOrderNo(), e);
+            throw new RuntimeException("API确认收货失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public Long apiApplyReturn(ApiApplyReturnBo apiApplyReturnBo) {
+        try {
+            // 直接调用服务层方法,传入ApiApplyReturnBo
+            ApiOrderReturnBo apiOrderReturnBo = MapstructUtils.convert(apiApplyReturnBo, ApiOrderReturnBo.class);
+            return orderReturnService.apiApplyReturn(apiOrderReturnBo);
+        } catch (Exception e) {
+            log.error("API申请售后失败,订单ID: {},订单号: {}", apiApplyReturnBo.getOrderId(), apiApplyReturnBo.getOrderNo(), e);
+            throw new RuntimeException("API申请售后失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public int apiWithdrawReturn(ApiWithdrawReturnBo apiWithdrawReturnBo) {
+        try {
+            Long returnId = apiWithdrawReturnBo.getReturnId();
+            String returnNo = apiWithdrawReturnBo.getReturnNo();
+            String withdrawReason = apiWithdrawReturnBo.getWithdrawReason();
+
+            if (returnId == null && returnNo == null) {
+                throw new IllegalArgumentException("售后单ID和售后单号不能同时为空");
+            }
+
+            // 调用服务层撤回售后(不绑定customerId、userId)
+            int result = orderReturnService.apiWithdrawReturn(returnId, returnNo, withdrawReason);
+
+            if (result > 0) {
+                log.info("API成功撤回售后,售后单ID: {},售后单号: {}", returnId, returnNo);
+            } else {
+                log.warn("API撤回售后失败,售后单可能不存在,售后单ID: {},售后单号: {}", returnId, returnNo);
+            }
+
+            return result;
+        } catch (Exception e) {
+            log.error("API撤回售后失败,售后单ID: {},售后单号: {}", apiWithdrawReturnBo.getReturnId(), apiWithdrawReturnBo.getReturnNo(), e);
+            throw new RuntimeException("API撤回售后失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public List<ApiOrderDeliverDto> selectOrderDeliver(ApiOrderDeliverBo apiOrderDeliverBo) {
+        try {
+            Long orderId = apiOrderDeliverBo.getOrderId();
+            String orderNo = apiOrderDeliverBo.getOrderCode();
+
+            if (orderId == null && orderNo == null) {
+                throw new IllegalArgumentException("订单ID和订单号不能同时为空");
+            }
+
+            // 调用服务层查询发货记录(不绑定customerId、userId)
+            List<OrderDeliverVo> deliverVos = orderDeliverService.apiSelectOrderDeliver(orderId, orderNo);
+
+            // 转换为ApiOrderDeliverDto
+            List<ApiOrderDeliverDto> deliverDtos = deliverVos.stream()
+                .map(this::convertToDeliverDto)
+                .collect(Collectors.toList());
+
+            log.info("API成功查询发货记录,订单ID: {},订单号: {},发货记录数: {}", orderId, orderNo, deliverDtos.size());
+            return deliverDtos;
+        } catch (Exception e) {
+            log.error("API查询发货记录失败,订单ID: {},订单号: {}", apiOrderDeliverBo.getOrderId(), apiOrderDeliverBo.getOrderCode(), e);
+            throw new RuntimeException("API查询发货记录失败:" + e.getMessage());
+        }
+    }
+
+
+
+    @Override
+    public ApiTrackDto selectTrack(ApiOrderDeliverBo apiOrderDeliverBo) {
+        TrackVO trackVO = orderDeliverService.apiQueryTrack(MapstructUtils.convert(apiOrderDeliverBo, OrderDeliverBo.class));
+        return MapstructUtils.convert(trackVO, ApiTrackDto.class);
+    }
+
+    /**
+     * 将OrderDeliverVo转换为ApiOrderDeliverDto
+     */
+    private ApiOrderDeliverDto convertToDeliverDto(OrderDeliverVo vo) {
+        return MapstructUtils.convert(vo, ApiOrderDeliverDto.class);
+    }
+
+    /**
+     * 将OrderProductVo转换为OrderProductDto
+     */
+    private OrderDto.OrderProductDto convertToProductDto(OrderProductVo vo) {
+        OrderDto.OrderProductDto dto = new OrderDto.OrderProductDto();
+        dto.setId(vo.getId());
+        dto.setOrderId(vo.getOrderId());
+        dto.setOrderNo(vo.getOrderNo());
+        dto.setProductId(vo.getProductId());
+        dto.setProductNo(vo.getProductNo());
+        dto.setProductName(vo.getProductName());
+        dto.setProductUnitId(vo.getProductUnitId());
+        dto.setProductUnit(vo.getProductUnit());
+        dto.setProductImage(vo.getProductImage());
+        dto.setOrderPrice(vo.getOrderPrice());
+        dto.setMarketPrice(vo.getMarketPrice());
+        dto.setTaxRate(vo.getTaxRate());
+        dto.setCategoryName(vo.getCategoryName());
+        dto.setPurchasingPrice(vo.getPurchasingPrice());
+        dto.setMinOrderQuantity(vo.getMinOrderQuantity());
+        dto.setMinSellingPrice(vo.getMinSellingPrice());
+        dto.setOrderQuantity(vo.getOrderQuantity());
+        dto.setSubtotal(vo.getSubtotal());
+        return dto;
+    }
+}

+ 21 - 4
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderDeliverService.java

@@ -1,13 +1,12 @@
 package org.dromara.order.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.order.domain.OrderDeliver;
+import org.dromara.order.domain.bo.OrderDeliverBo;
 import org.dromara.order.domain.vo.OrderDeliverProductVo;
 import org.dromara.order.domain.vo.OrderDeliverVo;
-import org.dromara.order.domain.bo.OrderDeliverBo;
-import org.dromara.common.mybatis.core.page.TableDataInfo;
-import org.dromara.common.mybatis.core.page.PageQuery;
-import org.dromara.order.domain.vo.OrderProductVo;
 import org.dromara.order.utils.kd100.domain.TrackVO;
 
 import java.util.Collection;
@@ -93,4 +92,22 @@ public interface IOrderDeliverService extends IService<OrderDeliver> {
     TableDataInfo<OrderDeliverVo> listDeliverOrderByCustomerIdPage(Long customerId, PageQuery pageQuery);
 
     TableDataInfo<OrderDeliverProductVo> getCustomerOrderProductList(Set<Long> orderIdList);
+
+    /**
+     * API查看订单发货记录
+     *
+     * @param orderId 订单ID
+     * @param orderNo 订单编号
+     * @return 发货记录列表
+     */
+    List<OrderDeliverVo> apiSelectOrderDeliver(Long orderId, String orderNo);
+
+    /**
+     * API查看订单物流信息
+     * 不绑定customerId、userId,直接根据物流单号和物流公司代码查询物流信息
+     *
+     * @param bo
+     * @return 物流轨迹信息
+     */
+    TrackVO apiQueryTrack(OrderDeliverBo bo);
 }

+ 34 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderMainService.java

@@ -5,6 +5,7 @@ import org.dromara.common.core.exception.api.ZhongcheException;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.order.domain.OrderMain;
+import org.dromara.order.domain.bo.ApiSubmitOrderBo;
 import org.dromara.order.domain.bo.OrderMainBo;
 import org.dromara.order.domain.bo.OrderProductBo;
 import org.dromara.order.domain.bo.PcSubmitOrderBo;
@@ -130,4 +131,37 @@ public interface IOrderMainService extends IService<OrderMain> {
 
     Boolean changeReturnedStatusByIds(List<Long> ids, String returnedStatus);
 
+    Long apiCreateOrder(ApiSubmitOrderBo bo);
+
+    /**
+     * 根据订单号查询订单详情(API专用)
+     * 不绑定customerId、userId,直接根据orderNo查询
+     *
+     * @param orderNo 订单编号
+     * @return 订单详情
+     */
+    OrderMainVo queryByOrderNo(String orderNo);
+
+    /**
+     * API取消订单
+     * 不绑定customerId、userId,直接根据orderId或orderNo取消
+     *
+     * @param orderId 订单ID
+     * @param orderNo 订单编号
+     * @return 影响行数
+     */
+    int apiCancelOrder(Long orderId, String orderNo);
+
+    /**
+     * API确认收货
+     * 不绑定customerId、userId,直接根据orderId或orderNo确认收货
+     *
+     * @param orderId 订单ID
+     * @param orderNo 订单编号
+     * @return 是否成功
+     */
+    Boolean apiConfirmReceipt(Long orderId, String orderNo);
+
+    void apiConfirmOrder(Long orderId, String orderNo);
+
 }

+ 22 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/IOrderReturnService.java

@@ -6,6 +6,7 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.order.domain.OrderReturn;
 import org.dromara.order.domain.OrderReturnPickCrrc;
+import org.dromara.order.domain.bo.ApiOrderReturnBo;
 import org.dromara.order.domain.bo.OrderReturnBo;
 import org.dromara.order.domain.vo.OrderReturnVo;
 
@@ -62,6 +63,7 @@ public interface IOrderReturnService extends IService<OrderReturn> {
     Boolean updateByBo(OrderReturnBo bo);
 
     int updateReturnStatus(OrderReturnBo bo);
+
     /**
      * 修改退货状态
      *
@@ -91,4 +93,24 @@ public interface IOrderReturnService extends IService<OrderReturn> {
     OrderReturnVo queryZhongCheById(@NotNull(message = "主键不能为空") Long id);
 
     boolean updateOrderReturnPickCrrc(OrderReturnPickCrrc orderReturnPickCrrc);
+
+    /**
+     * API申请售后
+     * 不绑定customerId、userId,直接根据orderId或orderNo申请售后
+     *
+     * @param apiOrderReturnBo 售后申请信息
+     * @return 售后单ID
+     */
+    Long apiApplyReturn(ApiOrderReturnBo apiOrderReturnBo);
+
+    /**
+     * API撤回售后
+     * 不绑定customerId、userId,直接根据returnId或returnNo撤回售后
+     *
+     * @param returnId       售后单ID
+     * @param returnNo       售后单号
+     * @param withdrawReason 撤回原因
+     * @return 影响行数
+     */
+    int apiWithdrawReturn(Long returnId, String returnNo, String withdrawReason);
 }

+ 83 - 0
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderDeliverServiceImpl.java

@@ -546,4 +546,87 @@ public class OrderDeliverServiceImpl extends ServiceImpl<OrderDeliverMapper, Ord
         }
         return baseMapper.deleteByIds(ids) > 0;
     }
+
+    @Override
+    public List<OrderDeliverVo> apiSelectOrderDeliver(Long orderId, String orderNo) {
+        if (orderId == null && orderNo == null) {
+            throw new IllegalArgumentException("订单ID和订单号不能同时为空");
+        }
+
+        // 先确定最终的订单ID(只赋值一次,保证是effectively final)
+        Long finalOrderId;
+        if (orderId != null) {
+            finalOrderId = orderId;
+        } else {
+            // 通过订单号查询,先获取订单ID
+            OrderMain orderMain = orderMainMapper.selectOne(
+                new LambdaQueryWrapper<OrderMain>()
+                    .eq(OrderMain::getOrderNo, orderNo)
+                    .select(OrderMain::getId)
+            );
+
+            if (orderMain == null) {
+                log.warn("API查询发货记录失败,订单号 {} 不存在", orderNo);
+                throw new IllegalArgumentException("订单不存在");
+            }
+
+            finalOrderId = orderMain.getId();
+        }
+
+        // 构建查询订单ID的条件:当前订单ID 或 父订单ID为当前订单ID
+        List<Long> targetOrderIds = orderMainMapper.selectObjs(
+            new LambdaQueryWrapper<OrderMain>()
+                .select(OrderMain::getId)
+                .and(wrapper -> wrapper
+                    .eq(OrderMain::getId, finalOrderId) // 自己是父订单
+                    .or()
+                    .eq(OrderMain::getParentOrderId, finalOrderId) // 或者是该父订单的子订单
+                )
+        ).stream().map(obj -> (Long) obj).collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(targetOrderIds)) {
+            log.info("API查询发货记录,订单ID {} 没有发货记录", finalOrderId);
+            return new ArrayList<>();
+        }
+
+        // 查询发货记录
+        List<OrderDeliverVo> orderDeliverVos = baseMapper.selectVoList(
+            new LambdaQueryWrapper<OrderDeliver>()
+                .in(OrderDeliver::getOrderId, targetOrderIds)
+                .orderByDesc(OrderDeliver::getCreateTime)
+        );
+
+        log.info("API成功查询发货记录,订单ID: {},订单号: {},发货记录数: {}", finalOrderId, orderNo, orderDeliverVos.size());
+        return orderDeliverVos;
+    }
+
+    @Override
+    public TrackVO apiQueryTrack(OrderDeliverBo bo) {
+        String logisticNo = bo.getLogisticNo();
+        String logisticsCompanyCode = bo.getLogisticsCompanyCode();
+        String phone = bo.getPhone();
+        TrackVO trackVO = null;
+        // 1. 参数校验
+        if (StringUtils.isNotBlank(logisticNo)) {
+            // 2. 构建查询 DTO
+            QueryTrackDTO dto = QueryTrackDTO.builder()
+                .com(logisticsCompanyCode)
+                .num(logisticNo)
+                .phone(phone)
+                .build();
+
+            // 3. 调用物流查询
+            try {
+                log.info("API开始查询物流信息,单号: {}, 公司代码: {}", dto.getNum(), dto.getCom());
+                trackVO = Kd100Util.queryTrack(dto);
+                log.info("API成功查询物流信息,单号: {}, 状态: {}", logisticNo, trackVO != null ? trackVO.getState() : "null");
+                return trackVO;
+
+            } catch (Exception e) {
+                log.error("API查询物流信息失败,dto={}", dto, e);
+                throw new ServiceException("物流信息查询失败,请稍后重试");
+            }
+        }
+        return trackVO;
+    }
 }

+ 417 - 9
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderMainServiceImpl.java

@@ -13,10 +13,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.common.core.context.PlatformContext;
-import org.dromara.common.core.enums.AssigneeTypeConstants;
-import org.dromara.common.core.enums.OrderPayType;
-import org.dromara.common.core.enums.OrderStatus;
-import org.dromara.common.core.enums.SysPlatformYesNo;
+import org.dromara.common.core.enums.*;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.exception.api.ZhongcheException;
 import org.dromara.common.core.utils.MapstructUtils;
@@ -34,10 +31,7 @@ import org.dromara.external.api.zhongche.domain.bo.OrderConfirmBo;
 import org.dromara.external.api.zhongche.domain.bo.OrderRejectBo;
 import org.dromara.external.api.zhongche.domain.vo.GoodsUpdateVo;
 import org.dromara.order.domain.*;
-import org.dromara.order.domain.bo.OrderMainBo;
-import org.dromara.order.domain.bo.OrderProductBo;
-import org.dromara.order.domain.bo.OrderSplitAssignBo;
-import org.dromara.order.domain.bo.PcSubmitOrderBo;
+import org.dromara.order.domain.bo.*;
 import org.dromara.order.domain.dto.AssignmentStatsDto;
 import org.dromara.order.domain.vo.*;
 import org.dromara.order.mapper.*;
@@ -58,6 +52,8 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.sql.SQLIntegrityConstraintViolationException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -466,7 +462,7 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
                     retryCount++;
 
                     // --- 步骤 A: 生成订单号 ---
-                    orderNo = SequenceUtils.generateOrderCode("RS");
+                    orderNo = SequenceUtils.generateOrderCode(bo.getOrderNoPrefix());
                     bo.setOrderNo(orderNo);
 
                     bo.setProductQuantity(productQuantity);
@@ -647,6 +643,7 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
             mainBo.setCustomerService(staffMap.get(serviceStaffId));
             mainBo.setBusinessDept(deptMap.get(belongingDepartmentId));
         }
+        mainBo.setOrderNoPrefix("Mo");
         // 1. 插入主订单,获取生成的 ID
         Long orderId = this.insertByBo(mainBo);
         // 2. 校验插入是否成功
@@ -1314,4 +1311,415 @@ public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain
         }
         return true;
     }
+
+    /*api外部订单下单*/
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long apiCreateOrder(ApiSubmitOrderBo bo) {
+        try {
+            // 构造 OrderMainBo 对象
+            OrderMainBo mainBo = new OrderMainBo();
+
+            // API订单不需要绑定customerId、userId、contactId
+            mainBo.setCustomerId(null);
+            mainBo.setUserId(null);
+            mainBo.setContactId(null);
+
+            mainBo.setShippingAddressId(bo.getShippingAddressId());
+            mainBo.setPurchaseReason(bo.getPurchaseReason());
+            mainBo.setRemark(bo.getRemark());
+            mainBo.setExpenseType(bo.getExpenseType());
+            mainBo.setShippingFee(bo.getShippingFee() != null ? bo.getShippingFee() : BigDecimal.ZERO);
+
+            // 设置订单来源为API
+            mainBo.setOrderSource(bo.getOrderSource() != null ? bo.getOrderSource() : "API");
+
+            // 解析配送时间
+            if (bo.getDeliveryDate() != null && !bo.getDeliveryDate().isEmpty()) {
+                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+                LocalDate deliveryLocalDate = LocalDate.parse(bo.getDeliveryDate(), formatter);
+                Date expectedDeliveryTime = Date.from(deliveryLocalDate.atStartOfDay().toInstant(java.time.ZoneOffset.UTC));
+                mainBo.setExpectedDeliveryTime(expectedDeliveryTime);
+            }
+
+            // 设置仓库(假设默认仓库)
+            mainBo.setWarehouseId(1L); // TODO: 后续可配置或根据客户动态获取
+
+            // 设置订单状态为待支付
+            mainBo.setOrderStatus(OrderStatus.PENDING_PAYMENT.getCode());
+
+            // 设置审核状态为待审核
+            mainBo.setCheckStatus("0");
+
+
+            mainBo.setOrderStatuses(OrderSourceEnum.Api_ADD.getCode());
+
+            mainBo.setOrderNoPrefix("Po");
+
+
+            // 处理商品信息
+            if (bo.getProductInfo() == null || bo.getProductInfo().isEmpty()) {
+                throw new IllegalArgumentException("商品信息不能为空");
+            }
+
+            // 构建 productId -> productNum 映射
+            Map<Long, Long> productNumMap = bo.getProductInfo().stream()
+                .filter(item -> item.getProductId() != null && item.getProductNum() != null)
+                .collect(Collectors.toMap(
+                    ApiSubmitOrderBo.PcOrderProduct::getProductId,
+                    ApiSubmitOrderBo.PcOrderProduct::getProductNum,
+                    (existing, replacement) -> existing // 防止重复 key 冲突
+                ));
+
+            // 提取所有商品 ID
+            List<Long> productIds = new ArrayList<>(productNumMap.keySet());
+
+            // 获取商品详情
+            List<ProductVo> productDetails = remoteProductService.getProductDetails(productIds);
+
+            // 构建订单商品列表
+            List<OrderProductBo> orderProductBos = productDetails.stream()
+                .map(productVo -> {
+                    Long productId = productVo.getId();
+                    Long quantity = productNumMap.get(productId);
+                    if (quantity == null) {
+                        // 理论上不会发生,但防御性处理
+                        log.warn("商品 {} 在请求中无数量信息,跳过", productId);
+                        return null;
+                    }
+
+                    OrderProductBo orderProductBo = new OrderProductBo();
+                    orderProductBo.setProductId(productId);
+                    orderProductBo.setProductNo(productVo.getProductNo());
+                    orderProductBo.setProductName(productVo.getItemName());
+                    orderProductBo.setProductUnitId(Long.valueOf(productVo.getUnitId()));
+                    orderProductBo.setProductUnit(productVo.getUnitName());
+                    orderProductBo.setProductImage(productVo.getProductImage());
+                    orderProductBo.setOrderPrice(productVo.getMemberPrice());
+                    orderProductBo.setMarketPrice(productVo.getMarketPrice());
+                    orderProductBo.setTaxRate(productVo.getTaxRate());
+                    orderProductBo.setCategoryName(productVo.getCategoryName());
+                    orderProductBo.setPurchasingPrice(productVo.getPurchasingPrice());
+                    orderProductBo.setMinOrderQuantity(productVo.getMinOrderQuantity());
+                    orderProductBo.setMinSellingPrice(productVo.getMinSellingPrice());
+                    orderProductBo.setOrderQuantity(quantity);
+                    orderProductBo.setSubtotal(productVo.getMemberPrice().multiply(BigDecimal.valueOf(quantity)));
+                    return orderProductBo;
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+            if (orderProductBos.isEmpty()) {
+                throw new IllegalStateException("无可下单的商品");
+            }
+
+            mainBo.setOrderProductBos(orderProductBos);
+
+            // 保存订单
+            Long orderId = this.insertByBo(mainBo);
+
+            // 初始化审批流程(由于没有contactId,可能需要特殊处理)
+            Boolean initOrderFlow = orderCustomerFlowService.initOrderFlow(orderId, null);
+
+            // 如果流程初始化成功,更新订单状态
+            if (initOrderFlow) {
+                log.info("成功初始化API订单审批流程,订单ID: {}", orderId);
+                this.update(Wrappers.lambdaUpdate(OrderMain.class)
+                    .eq(OrderMain::getId, orderId)
+                    .set(OrderMain::getOrderStatus, 1) // 假设 1 代表某种特定状态,需确认枚举值
+                    .set(OrderMain::getIsNeedCheck, SysPlatformYesNo.YES.getCode())
+                );
+            } else {
+                log.info("失败初始化API订单审批流程,订单ID: {}", orderId);
+                this.update(Wrappers.lambdaUpdate(OrderMain.class)
+                    .eq(OrderMain::getId, orderId)
+                    .set(OrderMain::getCheckStatus, "1")
+                    .set(OrderMain::getIsNeedCheck, SysPlatformYesNo.NO.getCode())
+                );
+            }
+
+            return orderId;
+        } catch (Exception e) {
+            log.error("API端提交订单失败", e);
+            throw new RuntimeException("API下单失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据订单号查询订单详情(API专用)
+     * 不绑定customerId、userId,直接根据orderNo查询
+     *
+     * @param orderNo 订单编号
+     * @return 订单详情
+     */
+    @Override
+    public OrderMainVo queryByOrderNo(String orderNo) {
+        if (StringUtils.isBlank(orderNo)) {
+            throw new IllegalArgumentException("订单号不能为空");
+        }
+
+        // 1. 根据订单号查询主订单
+        OrderMainVo orderMainVo = baseMapper.selectVoOne(new LambdaQueryWrapper<OrderMain>()
+            .eq(OrderMain::getOrderNo, orderNo)
+        );
+
+        if (orderMainVo == null) {
+            log.warn("订单号 {} 不存在", orderNo);
+            return null;
+        }
+
+        // 2. 查询关联的商品列表--并且查询商品已发货数量与未发货数量
+        List<OrderProductVo> orderProductVoList = orderProductMapper.selectProductsWithDelivered(orderMainVo.getId());
+        if (orderProductVoList != null && !orderProductVoList.isEmpty()) {
+            // 收集所有需要处理的 childOrderId (去重)
+            List<Long> childOrderIds = orderProductVoList.stream()
+                .map(OrderProductVo::getAssignedChildOrderId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+            if (!childOrderIds.isEmpty()) {
+                // 批量查询子订单信息 (避免 N+1 数据库查询)
+                List<OrderMainVo> childOrderList = baseMapper.selectVoByIds(childOrderIds);
+
+                // 将子订单放入 Map 方便快速查找: Key = childOrderId, Value = OrderMainVo
+                Map<Long, OrderMainVo> childOrderMap = childOrderList.stream()
+                    .collect(Collectors.toMap(OrderMainVo::getId, Function.identity(), (k1, k2) -> k1));
+
+                // 按分配类型分组收集 ID,准备批量调用远程服务
+                List<Long> customerIds = new ArrayList<>();
+                List<Long> supplierIds = new ArrayList<>();
+                List<Long> partnerIds = new ArrayList<>();
+
+                for (OrderMainVo childOrder : childOrderList) {
+                    if (childOrder.getAssigneeId() == null) continue;
+
+                    String type = childOrder.getAssigneeType();
+                    if (AssigneeTypeConstants.CUSTOMER.getCode().equals(type)) {
+                        customerIds.add(childOrder.getAssigneeId());
+                    } else if (AssigneeTypeConstants.SUPPLIER.getCode().equals(type)) {
+                        supplierIds.add(childOrder.getAssigneeId());
+                    } else if (AssigneeTypeConstants.PARTNER.getCode().equals(type)) {
+                        partnerIds.add(childOrder.getAssigneeId());
+                    }
+                }
+
+                // 批量调用远程服务 (每种类型只调用一次)
+                Map<Long, String> customerNameMap = Collections.emptyMap();
+                Map<Long, String> supplierNameMap = Collections.emptyMap();
+                Map<Long, String> partnerNameMap = Collections.emptyMap();
+
+                if (!customerIds.isEmpty()) {
+                    customerNameMap = remoteCustomerService.selectCustomerNameByIds(new HashSet<>(customerIds));
+                }
+                if (!supplierIds.isEmpty()) {
+                    supplierNameMap = remoteSupplierInfoService.selectSupplierNameByIds(new HashSet<>(supplierIds));
+                }
+                if (!partnerIds.isEmpty()) {
+                    partnerNameMap = remotePartnerInfoService.selectPartnerNameByIds(new HashSet<>(partnerIds));
+                }
+
+                // 内存组装数据
+                for (OrderProductVo productVo : orderProductVoList) {
+                    Long childOrderId = productVo.getAssignedChildOrderId();
+                    if (childOrderId != null) {
+                        OrderMainVo childOrder = childOrderMap.get(childOrderId);
+                        if (childOrder != null && childOrder.getAssigneeId() != null) {
+                            String assigneeName = null;
+                            String type = childOrder.getAssigneeType();
+
+                            if (AssigneeTypeConstants.CUSTOMER.getCode().equals(type)) {
+                                assigneeName = customerNameMap.get(childOrder.getAssigneeId());
+                            } else if (AssigneeTypeConstants.SUPPLIER.getCode().equals(type)) {
+                                assigneeName = supplierNameMap.get(childOrder.getAssigneeId());
+                            } else if (AssigneeTypeConstants.PARTNER.getCode().equals(type)) {
+                                assigneeName = partnerNameMap.get(childOrder.getAssigneeId());
+                            }
+                            productVo.setAssigneeName(assigneeName);
+                        }
+                    }
+                }
+            }
+        }
+
+        // 3. 组装数据
+        orderMainVo.setOrderProductList(orderProductVoList);
+
+        // 如果有关联的客户,查询客户名称
+        if (orderMainVo.getCustomerId() != null) {
+            Set<Long> customerIds = new HashSet<>();
+            customerIds.add(orderMainVo.getCustomerId());
+            Map<Long, String> map = remoteCustomerService.selectCustomerNameByIds(customerIds);
+            orderMainVo.setCustomerName(map.get(orderMainVo.getCustomerId()));
+        }
+
+        // 查询发货商品列表
+        orderMainVo.setDeliverProductList(
+            orderDeliverProductMapper.selectDeliverProductsByOrderId(orderMainVo.getId())
+        );
+
+        // 查询主订单原始商品的总行数与已分配行数
+        AssignmentStatsDto stats = orderProductMapper.getAssignmentStats(orderMainVo.getId());
+        Long total = stats.getTotal();
+        Long assigned = stats.getAssigned();
+
+        orderMainVo.setProductTotal(total);
+        orderMainVo.setUnassigned(total - assigned);
+        orderMainVo.setAssigned(assigned);
+
+        log.info("成功查询订单详情,订单号: {}", orderNo);
+        return orderMainVo;
+    }
+
+    @Override
+    public int apiCancelOrder(Long orderId, String orderNo) {
+        if (orderId == null && orderNo == null) {
+            throw new IllegalArgumentException("订单ID和订单号不能同时为空");
+        }
+
+        LambdaUpdateWrapper<OrderMain> updateWrapper = new LambdaUpdateWrapper<>();
+
+        // 设置取消状态
+        updateWrapper.set(OrderMain::getOrderStatus, OrderStatus.CANCEL.getCode());
+
+        // 设置更新时间
+        updateWrapper.set(OrderMain::getUpdateTime, new Date());
+
+        // 根据orderId或orderNo进行更新
+        if (orderId != null) {
+            updateWrapper.eq(OrderMain::getId, orderId);
+        } else {
+            updateWrapper.eq(OrderMain::getOrderNo, orderNo);
+        }
+
+        // 只有特定状态的订单可以取消
+        updateWrapper.in(OrderMain::getOrderStatus,
+            OrderStatus.PENDING_PAYMENT.getCode(),
+            OrderStatus.PENDING_CONFIRMATION.getCode(),
+            OrderStatus.PENDING_SHIPMENT.getCode());
+
+        int result = baseMapper.update(updateWrapper);
+
+        if (result > 0) {
+            log.info("API成功取消订单,订单ID: {},订单号: {}", orderId, orderNo);
+        } else {
+            log.warn("API取消订单失败,订单可能不存在或状态不允许取消,订单ID: {},订单号: {}", orderId, orderNo);
+        }
+
+        return result;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean apiConfirmReceipt(Long orderId, String orderNo) {
+        if (orderId == null && orderNo == null) {
+            throw new IllegalArgumentException("订单ID和订单号不能同时为空");
+        }
+
+        // 构造更新条件
+        LambdaUpdateWrapper<OrderMain> updateWrapper = new LambdaUpdateWrapper<>();
+
+        // 设置更新后的值为已完成
+        updateWrapper.set(OrderMain::getOrderStatus, OrderStatus.COMPLETED.getCode());
+
+        // 设置收货时间
+        updateWrapper.set(OrderMain::getReceivingTime, new Date());
+
+        // 设置更新时间
+        updateWrapper.set(OrderMain::getUpdateTime, new Date());
+
+        // 根据orderId或orderNo进行更新
+        if (orderId != null) {
+            updateWrapper.and(wrapper -> wrapper
+                .eq(OrderMain::getId, orderId)
+                .or()
+                .eq(OrderMain::getParentOrderId, orderId)
+            );
+        } else {
+            // 如果通过订单号查询,先获取订单ID
+            OrderMain orderMain = baseMapper.selectOne(new LambdaQueryWrapper<OrderMain>()
+                .eq(OrderMain::getOrderNo, orderNo)
+                .select(OrderMain::getId)
+            );
+
+            if (orderMain == null) {
+                log.warn("API确认收货失败,订单号 {} 不存在", orderNo);
+                return false;
+            }
+
+            Long id = orderMain.getId();
+            updateWrapper.and(wrapper -> wrapper
+                .eq(OrderMain::getId, id)
+                .or()
+                .eq(OrderMain::getParentOrderId, id)
+            );
+        }
+
+        // 只有已发货的订单才能确认收货
+        updateWrapper.eq(OrderMain::getOrderStatus, OrderStatus.SHIPPED.getCode());
+
+        // 执行更新
+        int updateCount = baseMapper.update(updateWrapper);
+
+        boolean success = updateCount > 0;
+
+        if (success) {
+            log.info("API成功确认收货,订单ID: {},订单号: {}", orderId, orderNo);
+        } else {
+            log.warn("API确认收货失败,订单可能不存在或状态不允许确认收货,订单ID: {},订单号: {}", orderId, orderNo);
+        }
+
+        return success;
+    }
+
+    @Override
+    public void apiConfirmOrder(Long orderId, String orderNo) {
+        if (orderId == null && orderNo == null) {
+            throw new IllegalArgumentException("订单ID和订单号不能同时为空");
+        }
+        // 构造更新条件
+        LambdaUpdateWrapper<OrderMain> updateWrapper = new LambdaUpdateWrapper<>();
+
+        // 设置更新后的值为已完成
+        updateWrapper.set(OrderMain::getOrderStatus, OrderStatus.PENDING_SHIPMENT.getCode());
+
+        // 设置更新时间
+        updateWrapper.set(OrderMain::getUpdateTime, new Date());
+
+        // 根据orderId或orderNo进行更新
+        if (orderId != null) {
+            updateWrapper.and(wrapper -> wrapper
+                .eq(OrderMain::getId, orderId)
+                .or()
+                .eq(OrderMain::getParentOrderId, orderId)
+            );
+        } else {
+            // 如果通过订单号查询,先获取订单ID
+            OrderMain orderMain = baseMapper.selectOne(new LambdaQueryWrapper<OrderMain>()
+                .eq(OrderMain::getOrderNo, orderNo)
+                .select(OrderMain::getId)
+            );
+
+            if (orderMain == null) {
+                log.warn("API确认收货失败,订单号 {} 不存在", orderNo);
+            }
+
+            Long id = orderMain.getId();
+            updateWrapper.and(wrapper -> wrapper
+                .eq(OrderMain::getId, id)
+                .or()
+                .eq(OrderMain::getParentOrderId, id)
+            );
+        }
+        // 执行更新
+        int updateCount = baseMapper.update(updateWrapper);
+
+        boolean success = updateCount > 0;
+
+        if (success) {
+            log.info("API订单确认成功,订单ID: {},订单号: {}", orderId, orderNo);
+        } else {
+            log.warn("API订单确认失败,订单可能不存在,订单ID: {},订单号: {}", orderId, orderNo);
+        }
+    }
 }

+ 171 - 6
ruoyi-modules/ruoyi-order/src/main/java/org/dromara/order/service/impl/OrderReturnServiceImpl.java

@@ -14,14 +14,13 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.redis.utils.SequenceUtils;
 import org.dromara.customer.api.RemoteCustomerService;
-import org.dromara.order.domain.OrderMainCrrcExt;
-import org.dromara.order.domain.OrderReturn;
-import org.dromara.order.domain.OrderReturnItem;
-import org.dromara.order.domain.OrderReturnPickCrrc;
+import org.dromara.order.domain.*;
+import org.dromara.order.domain.bo.ApiOrderReturnBo;
 import org.dromara.order.domain.bo.OrderReturnBo;
 import org.dromara.order.domain.bo.OrderReturnItemBo;
 import org.dromara.order.domain.vo.OrderReturnItemVo;
 import org.dromara.order.domain.vo.OrderReturnVo;
+import org.dromara.order.mapper.OrderMainMapper;
 import org.dromara.order.mapper.OrderReturnItemMapper;
 import org.dromara.order.mapper.OrderReturnMapper;
 import org.dromara.order.service.IOrderMainCrrcExtService;
@@ -50,6 +49,8 @@ public class OrderReturnServiceImpl extends ServiceImpl<OrderReturnMapper, Order
 
     private final OrderReturnMapper baseMapper;
 
+    private final OrderMainMapper orderMainMapper;
+
     private final OrderReturnItemMapper orderReturnItemMapper;
 
     private final IOrderMainCrrcExtService orderMainCrrcExtService;
@@ -137,9 +138,9 @@ public class OrderReturnServiceImpl extends ServiceImpl<OrderReturnMapper, Order
         if (CollUtil.isNotEmpty(records)) {
             Set<Long> customerIds = records.stream().map(OrderReturnVo::getCustomerId).collect(Collectors.toSet());
             Map<Long, String> customerMap = remoteCustomerService.selectCustomerNameByIds(customerIds);
-            List<OrderReturnItemVo> orderReturnItemList=null;
+            List<OrderReturnItemVo> orderReturnItemList = null;
             for (OrderReturnVo record : records) {
-                orderReturnItemList=orderReturnItemMapper.selectVoList(new LambdaQueryWrapper<OrderReturnItem>()
+                orderReturnItemList = orderReturnItemMapper.selectVoList(new LambdaQueryWrapper<OrderReturnItem>()
                     .eq(OrderReturnItem::getReturnId, record.getId()));
                 record.setOrderReturnItemList(orderReturnItemList);
             }
@@ -436,5 +437,169 @@ public class OrderReturnServiceImpl extends ServiceImpl<OrderReturnMapper, Order
         return TableDataInfo.build(result);
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long apiApplyReturn(ApiOrderReturnBo apiApplyReturnBo) {
+
+        if (apiApplyReturnBo == null) {
+            log.warn("API申请售后失败:入参为空");
+            throw new IllegalArgumentException("售后申请信息不能为空");
+        }
+
+        Long orderId = apiApplyReturnBo.getOrderId();
+        String orderNo = apiApplyReturnBo.getOrderNo();
+
+        if (orderId == null && orderNo == null) {
+            throw new IllegalArgumentException("订单ID和订单号不能同时为空");
+        }
+
+        // 如果通过订单号查询,先获取订单ID
+        Long finalOrderId = orderId;
+        String finalOrderNo = orderNo;
+
+        if (finalOrderId == null) {
+            // 通过订单号查询订单
+            OrderMain orderMain = orderMainMapper.selectOne(
+                new LambdaQueryWrapper<OrderMain>()
+                    .eq(OrderMain::getOrderNo, finalOrderNo)
+                    .select(OrderMain::getId, OrderMain::getCustomerId)
+            );
+
+            if (orderMain == null) {
+                log.warn("API申请售后失败,订单号 {} 不存在", finalOrderNo);
+                throw new IllegalArgumentException("订单不存在");
+            }
+
+            finalOrderId = orderMain.getId();
+        }
+
+        // 转换退货商品列表
+        List<OrderReturnItemBo> returnItems = apiApplyReturnBo.getReturnItems().stream()
+            .map(item -> {
+                OrderReturnItemBo itemBo = new OrderReturnItemBo();
+                itemBo.setOrderProductId(item.getOrderProductId());
+                itemBo.setProductId(item.getProductId());
+                itemBo.setProductName(item.getProductName());
+                itemBo.setReturnQuantity(item.getReturnQuantity());
+                itemBo.setUnitPrice(item.getUnitPrice());
+
+                // 计算小计
+                if (item.getUnitPrice() != null && item.getReturnQuantity() != null) {
+                    itemBo.setTotalAmount(item.getUnitPrice().multiply(new BigDecimal(item.getReturnQuantity())));
+                }
+
+                itemBo.setReasonDetail(item.getReasonDetail());
+                itemBo.setRemark(item.getRemark());
+                return itemBo;
+            })
+            .collect(Collectors.toList());
+
+        // 构造 OrderReturnBo
+        OrderReturnBo returnBo = new OrderReturnBo();
+        returnBo.setOrderId(finalOrderId);
+        returnBo.setOrderNo(finalOrderNo);
+        returnBo.setServiceType(apiApplyReturnBo.getServiceType());
+        returnBo.setReturnReasonId(apiApplyReturnBo.getReturnReasonId());
+        returnBo.setReturnReason(apiApplyReturnBo.getReturnReason());
+        returnBo.setProblemDescription(apiApplyReturnBo.getProblemDescription());
+        returnBo.setVoucherPhoto(apiApplyReturnBo.getVoucherPhoto());
+        returnBo.setShippingFee(apiApplyReturnBo.getShippingFee() != null ? apiApplyReturnBo.getShippingFee() : BigDecimal.ZERO);
+        returnBo.setIsShippingFee(apiApplyReturnBo.getIsShippingFee());
+        returnBo.setAfterSalesRemarks(apiApplyReturnBo.getAfterSalesRemarks());
+        returnBo.setReturnMethod(apiApplyReturnBo.getReturnMethod());
+        returnBo.setChargebackName(apiApplyReturnBo.getChargebackName());
+        returnBo.setChargebackPhone(apiApplyReturnBo.getChargebackPhone());
+        returnBo.setChargebackPickupTime(apiApplyReturnBo.getChargebackPickupTime());
+        returnBo.setChargebackProvincial(apiApplyReturnBo.getChargebackProvincial());
+        returnBo.setChargebackCity(apiApplyReturnBo.getChargebackCity());
+        returnBo.setChargebackCounty(apiApplyReturnBo.getChargebackCounty());
+        returnBo.setChargebackAddress(apiApplyReturnBo.getChargebackAddress());
+        returnBo.setOrderReturnItemList(returnItems);
+
+        // 计算售后金额和退货商品总数量
+        BigDecimal afterSaleAmount = returnItems.stream()
+            .map(item -> item.getTotalAmount() != null ? item.getTotalAmount() : BigDecimal.ZERO)
+            .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        Long returnProductNum = returnItems.stream()
+            .mapToLong(item -> item.getReturnQuantity() != null ? item.getReturnQuantity() : 0L)
+            .sum();
+
+        returnBo.setAfterSaleAmount(afterSaleAmount);
+        returnBo.setReturnProductNum(returnProductNum);
+
+        // 设置初始状态
+        returnBo.setReturnStatus("0"); // 待处理
+        returnBo.setStatus("0"); // 正常
+
+        // 生成单号并插入
+        String returnNo = SequenceUtils.generateOrderCode("OR");
+        returnBo.setReturnNo(returnNo);
+        returnBo.setReturnTime(new Date());
+
+        // 转换并校验
+        OrderReturn entity = MapstructUtils.convert(returnBo, OrderReturn.class);
+        validEntityBeforeSave(entity);
+
+        // 插入主表
+        boolean inserted = baseMapper.insert(entity) > 0;
+        if (!inserted) {
+            log.warn("API插入退货单主表失败");
+            throw new RuntimeException("创建售后单失败");
+        }
+
+        // 保存明细
+        saveOrderReturnItems(entity.getId(), returnItems, false);
+
+        log.info("API成功创建售后单,单号: {}", returnNo);
+        return entity.getId();
+    }
+
+    @Override
+    public int apiWithdrawReturn(Long returnId, String returnNo, String withdrawReason) {
+        if (returnId == null && returnNo == null) {
+            throw new IllegalArgumentException("售后单ID和售后单号不能同时为空");
+        }
+
+        // 构造OrderReturnBo
+        OrderReturnBo bo = new OrderReturnBo();
+
+        // 如果通过售后单号查询,先获取售后单ID
+        Long finalReturnId = returnId;
+        if (finalReturnId == null) {
+            // 通过售后单号查询售后单
+            OrderReturn orderReturn = baseMapper.selectOne(
+                new LambdaQueryWrapper<OrderReturn>()
+                    .eq(OrderReturn::getReturnNo, returnNo)
+                    .select(OrderReturn::getId)
+            );
+
+            if (orderReturn == null) {
+                log.warn("API撤回售后失败,售后单号 {} 不存在", returnNo);
+                throw new IllegalArgumentException("售后单不存在");
+            }
+
+            finalReturnId = orderReturn.getId();
+        }
+
+        bo.setId(finalReturnId);
+        // 设置状态为已撤回(假设1表示停用/已撤回)
+        bo.setStatus("1");
+
+        // 如果有撤回原因,可以添加到remark中
+        if (StringUtils.isNotBlank(withdrawReason)) {
+            bo.setRemark("撤回原因:" + withdrawReason);
+        }
+
+        int result = updateStatus(bo);
+
+        if (result > 0) {
+            log.info("API成功撤回售后,售后单ID: {},售后单号: {}", finalReturnId, returnNo);
+        } else {
+            log.warn("API撤回售后失败,售后单可能不存在,售后单ID: {},售后单号: {}", finalReturnId, returnNo);
+        }
+
+        return result;
+    }
 
 }