feat(order): 新增订单管理功能 V2

- 新增订单创建、查询、备注更新、申请退款等接口
- 添加订单相关实体类和枚举类
- 实现订单事件监听器,处理支付、退款、订单状态变化
- 优化移动端订单创建逻辑,集成订单服务
This commit is contained in:
2025-08-28 18:42:47 +08:00
parent af79a5ffa6
commit e95e0a04ff
28 changed files with 2515 additions and 12 deletions

View File

@@ -0,0 +1,89 @@
package com.ycwl.basic.order.controller;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.order.dto.*;
import com.ycwl.basic.order.service.IOrderService;
import com.ycwl.basic.utils.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
* 订单管理控制器V2 - 管理端
*/
@Slf4j
@RestController
@RequestMapping("/api/order/v2")
@RequiredArgsConstructor
public class OrderV2Controller {
private final IOrderService orderService;
/**
* 分页查询订单列表
*/
@PostMapping("/page")
public ApiResponse<PageInfo<OrderV2ListResponse>> pageOrders(@RequestBody OrderV2PageRequest request) {
log.info("分页查询订单列表: {}", request);
try {
PageInfo<OrderV2ListResponse> pageInfo = orderService.pageOrders(request);
return ApiResponse.success(pageInfo);
} catch (Exception e) {
log.error("分页查询订单列表失败", e);
return ApiResponse.fail("查询失败:" + e.getMessage());
}
}
/**
* 查询订单详情
*/
@GetMapping("/detail/{orderId}")
public ApiResponse<OrderV2DetailResponse> getOrderDetail(@PathVariable("orderId") Long orderId) {
log.info("查询订单详情: orderId={}", orderId);
try {
OrderV2DetailResponse detail = orderService.getOrderDetail(orderId);
if (detail == null) {
return ApiResponse.fail("订单不存在");
}
return ApiResponse.success(detail);
} catch (Exception e) {
log.error("查询订单详情失败: orderId={}", orderId, e);
return ApiResponse.fail("查询失败:" + e.getMessage());
}
}
/**
* 更新订单备注
*/
@PutMapping("/remark/{orderId}")
public ApiResponse<String> updateOrderRemarks(@PathVariable("orderId") Long orderId,
@RequestBody OrderRemarkRequest request) {
log.info("更新订单备注: orderId={}, remarks={}", orderId, request.getRemarks());
try {
boolean success = orderService.updateOrderRemarks(orderId, request.getRemarks());
if (success) {
return ApiResponse.success("备注更新成功");
} else {
return ApiResponse.fail("备注更新失败");
}
} catch (Exception e) {
log.error("更新订单备注失败: orderId={}", orderId, e);
return ApiResponse.fail("更新失败:" + e.getMessage());
}
}
/**
* 申请退款
*/
@PostMapping("/refund")
public ApiResponse<String> createRefund(@RequestBody RefundRequest request) {
log.info("申请退款: {}", request);
try {
Long refundId = orderService.createRefundRecord(request);
return ApiResponse.success("退款申请已提交,退款ID:" + refundId);
} catch (Exception e) {
log.error("申请退款失败: {}", request, e);
return ApiResponse.fail("退款申请失败:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,15 @@
package com.ycwl.basic.order.dto;
import lombok.Data;
/**
* 订单备注修改请求DTO
*/
@Data
public class OrderRemarkRequest {
/**
* 订单备注
*/
private String remarks;
}

View File

@@ -0,0 +1,150 @@
package com.ycwl.basic.order.dto;
import com.ycwl.basic.order.entity.OrderDiscountV2;
import com.ycwl.basic.order.entity.OrderItemV2;
import com.ycwl.basic.order.entity.OrderRefundV2;
import com.ycwl.basic.order.enums.OrderStatus;
import com.ycwl.basic.order.enums.PaymentStatus;
import com.ycwl.basic.order.enums.RefundStatus;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 订单详情响应DTO
*/
@Data
public class OrderV2DetailResponse {
/**
* 订单ID
*/
private Long id;
/**
* 订单号
*/
private String orderNo;
/**
* 会员ID
*/
private Long memberId;
/**
* 小程序openId
*/
private String openId;
/**
* 人脸ID
*/
private Long faceId;
/**
* 景区ID
*/
private Long scenicId;
/**
* 原始金额
*/
private BigDecimal originalAmount;
/**
* 优惠金额
*/
private BigDecimal discountAmount;
/**
* 最终金额
*/
private BigDecimal finalAmount;
/**
* 订单状态
*/
private OrderStatus orderStatus;
/**
* 订单状态描述
*/
private String orderStatusDesc;
/**
* 支付状态
*/
private PaymentStatus paymentStatus;
/**
* 支付状态描述
*/
private String paymentStatusDesc;
/**
* 退款状态
*/
private RefundStatus refundStatus;
/**
* 退款状态描述
*/
private String refundStatusDesc;
/**
* 总退款金额
*/
private BigDecimal totalRefundAmount;
/**
* 订单备注
*/
private String remarks;
/**
* 支付时间
*/
private Date payTime;
/**
* 完成时间
*/
private Date completeTime;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 创建人
*/
private Long createBy;
/**
* 更新人
*/
private Long updateBy;
/**
* 订单商品明细列表
*/
private List<OrderItemV2> orderItems;
/**
* 订单优惠记录列表
*/
private List<OrderDiscountV2> orderDiscounts;
/**
* 订单退款记录列表
*/
private List<OrderRefundV2> orderRefunds;
}

View File

@@ -0,0 +1,131 @@
package com.ycwl.basic.order.dto;
import com.ycwl.basic.order.enums.OrderStatus;
import com.ycwl.basic.order.enums.PaymentStatus;
import com.ycwl.basic.order.enums.RefundStatus;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 订单列表响应DTO
*/
@Data
public class OrderV2ListResponse {
/**
* 订单ID
*/
private Long id;
/**
* 订单号
*/
private String orderNo;
/**
* 会员ID
*/
private Long memberId;
/**
* 小程序openId
*/
private String openId;
/**
* 人脸ID
*/
private Long faceId;
/**
* 景区ID
*/
private Long scenicId;
/**
* 原始金额
*/
private BigDecimal originalAmount;
/**
* 优惠金额
*/
private BigDecimal discountAmount;
/**
* 最终金额
*/
private BigDecimal finalAmount;
/**
* 订单状态
*/
private OrderStatus orderStatus;
/**
* 订单状态描述
*/
private String orderStatusDesc;
/**
* 支付状态
*/
private PaymentStatus paymentStatus;
/**
* 支付状态描述
*/
private String paymentStatusDesc;
/**
* 退款状态
*/
private RefundStatus refundStatus;
/**
* 退款状态描述
*/
private String refundStatusDesc;
/**
* 总退款金额
*/
private BigDecimal totalRefundAmount;
/**
* 订单备注
*/
private String remarks;
/**
* 支付时间
*/
private Date payTime;
/**
* 完成时间
*/
private Date completeTime;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 商品数量(订单中商品种类数)
*/
private Integer itemCount;
/**
* 商品总数量(所有商品数量之和)
*/
private Integer totalQuantity;
}

View File

@@ -0,0 +1,72 @@
package com.ycwl.basic.order.dto;
import lombok.Data;
import java.util.Date;
/**
* 订单分页查询请求DTO
*/
@Data
public class OrderV2PageRequest {
/**
* 页码(从1开始)
*/
private Integer pageNum = 1;
/**
* 每页大小
*/
private Integer pageSize = 10;
/**
* 订单号
*/
private String orderNo;
/**
* 用户ID(移动端查询时自动设置)
*/
private Long memberId;
/**
* 景区ID
*/
private Long scenicId;
/**
* 订单状态
*/
private String orderStatus;
/**
* 支付状态
*/
private String paymentStatus;
/**
* 退款状态
*/
private String refundStatus;
/**
* 开始创建时间
*/
private Date createTimeStart;
/**
* 结束创建时间
*/
private Date createTimeEnd;
/**
* 开始支付时间
*/
private Date payTimeStart;
/**
* 结束支付时间
*/
private Date payTimeEnd;
}

View File

@@ -0,0 +1,52 @@
package com.ycwl.basic.order.dto;
import lombok.Data;
import java.math.BigDecimal;
/**
* 退款申请请求DTO
*/
@Data
public class RefundRequest {
/**
* 订单ID
*/
private Long orderId;
/**
* 退款类型
*/
private String refundType;
/**
* 退款金额
*/
private BigDecimal refundAmount;
/**
* 退款手续费
*/
private BigDecimal refundFee = BigDecimal.ZERO;
/**
* 退款原因
*/
private String refundReason;
/**
* 退款详细说明
*/
private String refundDescription;
/**
* 操作备注
*/
private String operatorRemarks;
/**
* 退款渠道
*/
private String refundChannel = "ORIGINAL";
}

View File

@@ -0,0 +1,91 @@
package com.ycwl.basic.order.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ycwl.basic.order.enums.DiscountType;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 订单优惠记录表实体V2
*/
@Data
@TableName("order_discount_v2")
public class OrderDiscountV2 {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 订单ID
*/
@TableField("order_id")
private Long orderId;
/**
* 优惠类型
*/
@TableField("discount_type")
private DiscountType discountType;
/**
* 优惠名称
*/
@TableField("discount_name")
private String discountName;
/**
* 优惠金额
*/
@TableField("discount_amount")
private BigDecimal discountAmount;
/**
* 优惠比例
*/
@TableField("discount_rate")
private BigDecimal discountRate;
/**
* 显示顺序
*/
@TableField("sort_order")
private Integer sortOrder;
/**
* 优惠券ID
*/
@TableField("coupon_id")
private Long couponId;
/**
* 优惠券码
*/
@TableField("coupon_code")
private String couponCode;
/**
* 券码
*/
@TableField("voucher_code")
private String voucherCode;
/**
* 券码批次ID
*/
@TableField("voucher_batch_id")
private Long voucherBatchId;
/**
* 创建时间
*/
@TableField("create_time")
private Date createTime;
}

View File

@@ -0,0 +1,84 @@
package com.ycwl.basic.order.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 订单商品明细表实体V2
*/
@Data
@TableName("order_item_v2")
public class OrderItemV2 {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 订单ID
*/
@TableField("order_id")
private Long orderId;
/**
* 商品类型
*/
@TableField("product_type")
private String productType;
/**
* 商品ID
*/
@TableField("product_id")
private String productId;
/**
* 商品名称
*/
@TableField("product_name")
private String productName;
/**
* 商品数量
*/
@TableField("quantity")
private Integer quantity;
/**
* 单价
*/
@TableField("unit_price")
private BigDecimal unitPrice;
/**
* 原始小计
*/
@TableField("original_amount")
private BigDecimal originalAmount;
/**
* 最终小计
*/
@TableField("final_amount")
private BigDecimal finalAmount;
/**
* 创建时间
*/
@TableField("create_time")
private Date createTime;
/**
* 更新时间
*/
@TableField("update_time")
private Date updateTime;
}

View File

@@ -0,0 +1,146 @@
package com.ycwl.basic.order.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ycwl.basic.order.enums.RefundStatus;
import com.ycwl.basic.order.enums.RefundType;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 订单退款记录表实体V2
*/
@Data
@TableName("order_refund_v2")
public class OrderRefundV2 {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 订单ID
*/
@TableField("order_id")
private Long orderId;
/**
* 退款单号
*/
@TableField("refund_no")
private String refundNo;
/**
* 退款类型
*/
@TableField("refund_type")
private RefundType refundType;
/**
* 退款金额
*/
@TableField("refund_amount")
private BigDecimal refundAmount;
/**
* 退款手续费
*/
@TableField("refund_fee")
private BigDecimal refundFee;
/**
* 退款状态
*/
@TableField("refund_status")
private RefundStatus refundStatus;
/**
* 退款原因
*/
@TableField("refund_reason")
private String refundReason;
/**
* 退款详细说明
*/
@TableField("refund_description")
private String refundDescription;
/**
* 申请人ID
*/
@TableField("apply_by")
private Long applyBy;
/**
* 审批人ID
*/
@TableField("approve_by")
private Long approveBy;
/**
* 操作备注
*/
@TableField("operator_remarks")
private String operatorRemarks;
/**
* 支付平台退款单号
*/
@TableField("payment_refund_id")
private String paymentRefundId;
/**
* 退款渠道
*/
@TableField("refund_channel")
private String refundChannel;
/**
* 申请时间
*/
@TableField("apply_time")
private Date applyTime;
/**
* 审批时间
*/
@TableField("approve_time")
private Date approveTime;
/**
* 完成时间
*/
@TableField("complete_time")
private Date completeTime;
/**
* 创建时间
*/
@TableField("create_time")
private Date createTime;
/**
* 更新时间
*/
@TableField("update_time")
private Date updateTime;
/**
* 创建人
*/
@TableField("create_by")
private Long createBy;
/**
* 更新人
*/
@TableField("update_by")
private Long updateBy;
}

View File

@@ -0,0 +1,153 @@
package com.ycwl.basic.order.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ycwl.basic.order.enums.OrderStatus;
import com.ycwl.basic.order.enums.PaymentStatus;
import com.ycwl.basic.order.enums.RefundStatus;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 订单主表实体V2
*/
@Data
@TableName("order_v2")
public class OrderV2 {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 订单号
*/
@TableField("order_no")
private String orderNo;
/**
* 会员ID
*/
@TableField("member_id")
private Long memberId;
/**
* 小程序openId
*/
@TableField("open_id")
private String openId;
/**
* 人脸ID
*/
@TableField("face_id")
private Long faceId;
/**
* 景区ID
*/
@TableField("scenic_id")
private Long scenicId;
/**
* 原始金额
*/
@TableField("original_amount")
private BigDecimal originalAmount;
/**
* 优惠金额
*/
@TableField("discount_amount")
private BigDecimal discountAmount;
/**
* 最终金额
*/
@TableField("final_amount")
private BigDecimal finalAmount;
/**
* 订单状态
*/
@TableField("order_status")
private OrderStatus orderStatus;
/**
* 支付状态
*/
@TableField("payment_status")
private PaymentStatus paymentStatus;
/**
* 退款状态
*/
@TableField("refund_status")
private RefundStatus refundStatus;
/**
* 总退款金额
*/
@TableField("total_refund_amount")
private BigDecimal totalRefundAmount;
/**
* 订单备注
*/
@TableField("remarks")
private String remarks;
/**
* 支付时间
*/
@TableField("pay_time")
private Date payTime;
/**
* 完成时间
*/
@TableField("complete_time")
private Date completeTime;
/**
* 创建时间
*/
@TableField("create_time")
private Date createTime;
/**
* 更新时间
*/
@TableField("update_time")
private Date updateTime;
/**
* 创建人
*/
@TableField("create_by")
private Long createBy;
/**
* 更新人
*/
@TableField("update_by")
private Long updateBy;
/**
* 是否删除
*/
@TableField("deleted")
private Integer deleted;
/**
* 删除时间
*/
@TableField("deleted_at")
private Date deletedAt;
}

View File

@@ -0,0 +1,60 @@
package com.ycwl.basic.order.enums;
/**
* 优惠类型枚举
*/
public enum DiscountType {
/**
* 优惠券
*/
COUPON("COUPON", "优惠券"),
/**
* 券码
*/
VOUCHER("VOUCHER", "券码"),
/**
* 限时立减
*/
FLASH_SALE("FLASH_SALE", "限时立减"),
/**
* 套餐优惠
*/
BUNDLE("BUNDLE", "套餐优惠"),
/**
* 一口价优惠
*/
FIXED_PRICE("FIXED_PRICE", "一口价优惠");
private final String code;
private final String description;
DiscountType(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
/**
* 根据code获取枚举
*/
public static DiscountType fromCode(String code) {
for (DiscountType type : values()) {
if (type.code.equals(code)) {
return type;
}
}
throw new IllegalArgumentException("Unknown DiscountType code: " + code);
}
}

View File

@@ -0,0 +1,70 @@
package com.ycwl.basic.order.enums;
/**
* 订单状态枚举
*/
public enum OrderStatus {
/**
* 待支付
*/
PENDING_PAYMENT("PENDING_PAYMENT", "待支付"),
/**
* 已支付
*/
PAID("PAID", "已支付"),
/**
* 处理中
*/
PROCESSING("PROCESSING", "处理中"),
/**
* 已完成
*/
COMPLETED("COMPLETED", "已完成"),
/**
* 已取消
*/
CANCELLED("CANCELLED", "已取消"),
/**
* 退款中
*/
REFUNDING("REFUNDING", "退款中"),
/**
* 已退款
*/
REFUNDED("REFUNDED", "已退款");
private final String code;
private final String description;
OrderStatus(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
/**
* 根据code获取枚举
*/
public static OrderStatus fromCode(String code) {
for (OrderStatus status : values()) {
if (status.code.equals(code)) {
return status;
}
}
throw new IllegalArgumentException("Unknown OrderStatus code: " + code);
}
}

View File

@@ -0,0 +1,50 @@
package com.ycwl.basic.order.enums;
/**
* 支付状态枚举
*/
public enum PaymentStatus {
/**
* 未支付
*/
UNPAID("UNPAID", "未支付"),
/**
* 已支付
*/
PAID("PAID", "已支付"),
/**
* 已退款
*/
REFUNDED("REFUNDED", "已退款");
private final String code;
private final String description;
PaymentStatus(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
/**
* 根据code获取枚举
*/
public static PaymentStatus fromCode(String code) {
for (PaymentStatus status : values()) {
if (status.code.equals(code)) {
return status;
}
}
throw new IllegalArgumentException("Unknown PaymentStatus code: " + code);
}
}

View File

@@ -0,0 +1,55 @@
package com.ycwl.basic.order.enums;
/**
* 退款状态枚举
*/
public enum RefundStatus {
/**
* 无退款
*/
NO_REFUND("NO_REFUND", "无退款"),
/**
* 部分退款
*/
PARTIAL_REFUND("PARTIAL_REFUND", "部分退款"),
/**
* 全额退款
*/
FULL_REFUND("FULL_REFUND", "全额退款"),
/**
* 退款处理中
*/
REFUND_PROCESSING("REFUND_PROCESSING", "退款处理中");
private final String code;
private final String description;
RefundStatus(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
/**
* 根据code获取枚举
*/
public static RefundStatus fromCode(String code) {
for (RefundStatus status : values()) {
if (status.code.equals(code)) {
return status;
}
}
throw new IllegalArgumentException("Unknown RefundStatus code: " + code);
}
}

View File

@@ -0,0 +1,50 @@
package com.ycwl.basic.order.enums;
/**
* 退款类型枚举
*/
public enum RefundType {
/**
* 全额退款
*/
FULL_REFUND("FULL_REFUND", "全额退款"),
/**
* 部分退款
*/
PARTIAL_REFUND("PARTIAL_REFUND", "部分退款"),
/**
* 商品退款
*/
ITEM_REFUND("ITEM_REFUND", "商品退款");
private final String code;
private final String description;
RefundType(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
/**
* 根据code获取枚举
*/
public static RefundType fromCode(String code) {
for (RefundType type : values()) {
if (type.code.equals(code)) {
return type;
}
}
throw new IllegalArgumentException("Unknown RefundType code: " + code);
}
}

View File

@@ -0,0 +1,28 @@
package com.ycwl.basic.order.event;
/**
* 订单事件监听器接口
*/
public interface OrderEventListener {
/**
* 支付状态变更事件
*
* @param event 支付状态变更事件
*/
void onPaymentStatusChanged(PaymentStatusChangeEvent event);
/**
* 退款状态变更事件
*
* @param event 退款状态变更事件
*/
void onRefundStatusChanged(RefundStatusChangeEvent event);
/**
* 订单状态变更事件
*
* @param event 订单状态变更事件
*/
void onOrderStatusChanged(OrderStatusChangeEvent event);
}

View File

@@ -0,0 +1,125 @@
package com.ycwl.basic.order.event;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* 订单事件管理器
* 管理事件监听器的注册和事件分发
*/
@Slf4j
@Component
public class OrderEventManager {
private final List<OrderEventListener> listeners = new ArrayList<>();
private final Executor eventExecutor = Executors.newFixedThreadPool(5,
r -> new Thread(r, "order-event-thread"));
@PostConstruct
public void init() {
log.info("订单事件管理器初始化完成");
}
/**
* 注册事件监听器
*
* @param listener 监听器实例
*/
public void registerListener(OrderEventListener listener) {
if (listener != null && !listeners.contains(listener)) {
listeners.add(listener);
log.info("注册订单事件监听器: {}", listener.getClass().getSimpleName());
}
}
/**
* 移除事件监听器
*
* @param listener 监听器实例
*/
public void removeListener(OrderEventListener listener) {
if (listeners.remove(listener)) {
log.info("移除订单事件监听器: {}", listener.getClass().getSimpleName());
}
}
/**
* 发布支付状态变更事件
*
* @param event 支付状态变更事件
*/
public void publishPaymentStatusChangeEvent(PaymentStatusChangeEvent event) {
log.info("发布支付状态变更事件: orderId={}, {} -> {}",
event.getOrderId(), event.getOldPaymentStatus(), event.getNewPaymentStatus());
CompletableFuture.runAsync(() -> {
for (OrderEventListener listener : listeners) {
try {
listener.onPaymentStatusChanged(event);
} catch (Exception e) {
log.error("处理支付状态变更事件失败: listener={}, orderId={}",
listener.getClass().getSimpleName(), event.getOrderId(), e);
}
}
}, eventExecutor);
}
/**
* 发布退款状态变更事件
*
* @param event 退款状态变更事件
*/
public void publishRefundStatusChangeEvent(RefundStatusChangeEvent event) {
log.info("发布退款状态变更事件: orderId={}, refundId={}, {} -> {}",
event.getOrderId(), event.getRefundId(),
event.getOldRefundStatus(), event.getNewRefundStatus());
CompletableFuture.runAsync(() -> {
for (OrderEventListener listener : listeners) {
try {
listener.onRefundStatusChanged(event);
} catch (Exception e) {
log.error("处理退款状态变更事件失败: listener={}, orderId={}, refundId={}",
listener.getClass().getSimpleName(), event.getOrderId(), event.getRefundId(), e);
}
}
}, eventExecutor);
}
/**
* 发布订单状态变更事件
*
* @param event 订单状态变更事件
*/
public void publishOrderStatusChangeEvent(OrderStatusChangeEvent event) {
log.info("发布订单状态变更事件: orderId={}, {} -> {}",
event.getOrderId(), event.getOldOrderStatus(), event.getNewOrderStatus());
CompletableFuture.runAsync(() -> {
for (OrderEventListener listener : listeners) {
try {
listener.onOrderStatusChanged(event);
} catch (Exception e) {
log.error("处理订单状态变更事件失败: listener={}, orderId={}",
listener.getClass().getSimpleName(), event.getOrderId(), e);
}
}
}, eventExecutor);
}
/**
* 获取已注册的监听器数量
*
* @return 监听器数量
*/
public int getListenerCount() {
return listeners.size();
}
}

View File

@@ -0,0 +1,60 @@
package com.ycwl.basic.order.event;
import com.ycwl.basic.order.enums.OrderStatus;
import lombok.Data;
import java.util.Date;
/**
* 订单状态变更事件
*/
@Data
public class OrderStatusChangeEvent {
/**
* 订单ID
*/
private Long orderId;
/**
* 订单号
*/
private String orderNo;
/**
* 旧的订单状态
*/
private OrderStatus oldOrderStatus;
/**
* 新的订单状态
*/
private OrderStatus newOrderStatus;
/**
* 变更时间
*/
private Date changeTime;
/**
* 变更原因
*/
private String changeReason;
/**
* 操作人ID
*/
private Long operatorId;
public OrderStatusChangeEvent(Long orderId, String orderNo,
OrderStatus oldStatus, OrderStatus newStatus,
String changeReason, Long operatorId) {
this.orderId = orderId;
this.orderNo = orderNo;
this.oldOrderStatus = oldStatus;
this.newOrderStatus = newStatus;
this.changeReason = changeReason;
this.operatorId = operatorId;
this.changeTime = new Date();
}
}

View File

@@ -0,0 +1,60 @@
package com.ycwl.basic.order.event;
import com.ycwl.basic.order.enums.PaymentStatus;
import lombok.Data;
import java.util.Date;
/**
* 支付状态变更事件
*/
@Data
public class PaymentStatusChangeEvent {
/**
* 订单ID
*/
private Long orderId;
/**
* 订单号
*/
private String orderNo;
/**
* 旧的支付状态
*/
private PaymentStatus oldPaymentStatus;
/**
* 新的支付状态
*/
private PaymentStatus newPaymentStatus;
/**
* 变更时间
*/
private Date changeTime;
/**
* 变更原因
*/
private String changeReason;
/**
* 操作人ID
*/
private Long operatorId;
public PaymentStatusChangeEvent(Long orderId, String orderNo,
PaymentStatus oldStatus, PaymentStatus newStatus,
String changeReason, Long operatorId) {
this.orderId = orderId;
this.orderNo = orderNo;
this.oldPaymentStatus = oldStatus;
this.newPaymentStatus = newStatus;
this.changeReason = changeReason;
this.operatorId = operatorId;
this.changeTime = new Date();
}
}

View File

@@ -0,0 +1,79 @@
package com.ycwl.basic.order.event;
import com.ycwl.basic.order.enums.RefundStatus;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 退款状态变更事件
*/
@Data
public class RefundStatusChangeEvent {
/**
* 订单ID
*/
private Long orderId;
/**
* 订单号
*/
private String orderNo;
/**
* 退款记录ID
*/
private Long refundId;
/**
* 退款单号
*/
private String refundNo;
/**
* 旧的退款状态
*/
private RefundStatus oldRefundStatus;
/**
* 新的退款状态
*/
private RefundStatus newRefundStatus;
/**
* 退款金额
*/
private BigDecimal refundAmount;
/**
* 变更时间
*/
private Date changeTime;
/**
* 变更原因
*/
private String changeReason;
/**
* 操作人ID
*/
private Long operatorId;
public RefundStatusChangeEvent(Long orderId, String orderNo, Long refundId, String refundNo,
RefundStatus oldStatus, RefundStatus newStatus,
BigDecimal refundAmount, String changeReason, Long operatorId) {
this.orderId = orderId;
this.orderNo = orderNo;
this.refundId = refundId;
this.refundNo = refundNo;
this.oldRefundStatus = oldStatus;
this.newRefundStatus = newStatus;
this.refundAmount = refundAmount;
this.changeReason = changeReason;
this.operatorId = operatorId;
this.changeTime = new Date();
}
}

View File

@@ -0,0 +1,120 @@
package com.ycwl.basic.order.event.impl;
import com.ycwl.basic.order.event.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
/**
* 支付状态变更监听器示例
* 应用启动后自动注册到事件管理器
*/
@Slf4j
@Component
public class PaymentStatusChangeListener implements OrderEventListener {
@Resource
private OrderEventManager orderEventManager;
@PostConstruct
public void init() {
// 应用启动后自动注册到事件管理器
orderEventManager.registerListener(this);
log.info("支付状态变更监听器注册完成");
}
@Override
public void onPaymentStatusChanged(PaymentStatusChangeEvent event) {
log.info("处理支付状态变更事件: orderId={}, orderNo={}, {} -> {}, reason={}",
event.getOrderId(), event.getOrderNo(),
event.getOldPaymentStatus(), event.getNewPaymentStatus(),
event.getChangeReason());
// 根据支付状态执行相应的业务逻辑
switch (event.getNewPaymentStatus()) {
case PAID:
handlePaymentSuccess(event);
break;
case REFUNDED:
handlePaymentRefunded(event);
break;
case UNPAID:
handlePaymentPending(event);
break;
default:
log.warn("未处理的支付状态: {}", event.getNewPaymentStatus());
}
}
@Override
public void onRefundStatusChanged(RefundStatusChangeEvent event) {
log.info("处理退款状态变更事件: orderId={}, refundId={}, {} -> {}",
event.getOrderId(), event.getRefundId(),
event.getOldRefundStatus(), event.getNewRefundStatus());
// 可以在这里添加退款相关的业务逻辑
// 比如发送通知、更新库存等
}
@Override
public void onOrderStatusChanged(OrderStatusChangeEvent event) {
log.info("处理订单状态变更事件: orderId={}, {} -> {}",
event.getOrderId(), event.getOldOrderStatus(), event.getNewOrderStatus());
// 可以在这里添加订单状态相关的业务逻辑
// 比如发送用户通知、触发后续流程等
}
/**
* 处理支付成功事件
*/
private void handlePaymentSuccess(PaymentStatusChangeEvent event) {
log.info("处理支付成功: orderId={}", event.getOrderId());
// 这里可以添加支付成功后的业务逻辑:
// 1. 发送支付成功通知
// 2. 更新商品库存
// 3. 生成相关凭证
// 4. 发送短信/邮件通知
// 5. 启动履约流程
// 示例:记录支付成功日志
log.info("订单支付成功处理完成: orderId={}, changeTime={}",
event.getOrderId(), event.getChangeTime());
}
/**
* 处理支付退款事件
*/
private void handlePaymentRefunded(PaymentStatusChangeEvent event) {
log.info("处理支付退款: orderId={}", event.getOrderId());
// 这里可以添加支付退款后的业务逻辑:
// 1. 恢复商品库存
// 2. 取消相关服务
// 3. 发送退款通知
// 4. 更新会员积分
// 示例:记录退款处理日志
log.info("订单退款处理完成: orderId={}, changeTime={}",
event.getOrderId(), event.getChangeTime());
}
/**
* 处理支付待处理事件
*/
private void handlePaymentPending(PaymentStatusChangeEvent event) {
log.info("处理支付待处理: orderId={}", event.getOrderId());
// 这里可以添加支付待处理的业务逻辑:
// 1. 设置支付超时提醒
// 2. 预留库存
// 3. 发送支付提醒
// 示例:记录待支付处理日志
log.info("订单待支付处理完成: orderId={}, changeTime={}",
event.getOrderId(), event.getChangeTime());
}
}

View File

@@ -0,0 +1,12 @@
package com.ycwl.basic.order.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycwl.basic.order.entity.OrderDiscountV2;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单优惠记录表Mapper接口
*/
@Mapper
public interface OrderDiscountMapper extends BaseMapper<OrderDiscountV2> {
}

View File

@@ -0,0 +1,12 @@
package com.ycwl.basic.order.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycwl.basic.order.entity.OrderItemV2;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单商品明细表Mapper接口
*/
@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItemV2> {
}

View File

@@ -0,0 +1,12 @@
package com.ycwl.basic.order.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycwl.basic.order.entity.OrderV2;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单表Mapper接口
*/
@Mapper
public interface OrderMapper extends BaseMapper<OrderV2> {
}

View File

@@ -0,0 +1,12 @@
package com.ycwl.basic.order.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycwl.basic.order.entity.OrderRefundV2;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单退款记录表Mapper接口
*/
@Mapper
public interface OrderRefundMapper extends BaseMapper<OrderRefundV2> {
}

View File

@@ -0,0 +1,119 @@
package com.ycwl.basic.order.service;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.dto.MobileOrderRequest;
import com.ycwl.basic.order.dto.*;
import com.ycwl.basic.order.entity.OrderV2;
import com.ycwl.basic.order.enums.RefundStatus;
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
/**
* 订单服务接口
*/
public interface IOrderService {
/**
* 创建订单
*
* @param request 移动端下单请求
* @param userId 用户ID
* @param scenicId 景区ID
* @param priceResult 价格计算结果
* @return 订单ID
*/
Long createOrder(MobileOrderRequest request, Long userId, Long scenicId, PriceCalculationResult priceResult);
/**
* 根据订单号查询订单
*
* @param orderNo 订单号
* @return 订单信息
*/
OrderV2 getByOrderNo(String orderNo);
/**
* 根据订单ID查询订单
*
* @param orderId 订单ID
* @return 订单信息
*/
OrderV2 getById(Long orderId);
/**
* 更新订单状态
*
* @param orderId 订单ID
* @param orderStatus 订单状态
* @param paymentStatus 支付状态
* @return 更新结果
*/
boolean updateOrderStatus(Long orderId, String orderStatus, String paymentStatus);
/**
* 生成订单号
*
* @return 订单号
*/
String generateOrderNo();
// ====== 新增方法 ======
/**
* 分页查询订单列表
*
* @param request 分页查询请求
* @return 分页结果
*/
PageInfo<OrderV2ListResponse> pageOrders(OrderV2PageRequest request);
/**
* 查询订单详情(包含商品明细、优惠记录、退款记录)
*
* @param orderId 订单ID
* @return 订单详情
*/
OrderV2DetailResponse getOrderDetail(Long orderId);
/**
* 更新订单备注
*
* @param orderId 订单ID
* @param remarks 备注内容
* @return 更新结果
*/
boolean updateOrderRemarks(Long orderId, String remarks);
/**
* 更新支付状态
*
* @param orderId 订单ID
* @param paymentStatus 支付状态
* @return 更新结果
*/
boolean updatePaymentStatus(Long orderId, String paymentStatus);
/**
* 更新退款状态
*
* @param orderId 订单ID
* @param refundStatus 退款状态
* @return 更新结果
*/
boolean updateRefundStatus(Long orderId, RefundStatus refundStatus);
/**
* 创建退款记录
*
* @param request 退款申请请求
* @return 退款记录ID
*/
Long createRefundRecord(RefundRequest request);
/**
* 根据用户ID分页查询订单列表(移动端使用)
*
* @param request 分页查询请求(已设置用户ID)
* @return 分页结果
*/
PageInfo<OrderV2ListResponse> pageOrdersByUser(OrderV2PageRequest request);
}

View File

@@ -0,0 +1,530 @@
package com.ycwl.basic.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.dto.MobileOrderRequest;
import com.ycwl.basic.order.dto.*;
import com.ycwl.basic.order.entity.OrderDiscountV2;
import com.ycwl.basic.order.entity.OrderItemV2;
import com.ycwl.basic.order.entity.OrderRefundV2;
import com.ycwl.basic.order.entity.OrderV2;
import com.ycwl.basic.order.enums.*;
import com.ycwl.basic.order.mapper.OrderDiscountMapper;
import com.ycwl.basic.order.mapper.OrderItemMapper;
import com.ycwl.basic.order.mapper.OrderMapper;
import com.ycwl.basic.order.mapper.OrderRefundMapper;
import com.ycwl.basic.order.service.IOrderService;
import com.ycwl.basic.order.event.*;
import com.ycwl.basic.pricing.dto.DiscountDetail;
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
import com.ycwl.basic.pricing.dto.ProductItem;
import com.ycwl.basic.pricing.dto.VoucherInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 订单服务实现类
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements IOrderService {
private final OrderMapper orderMapper;
private final OrderItemMapper orderItemMapper;
private final OrderDiscountMapper orderDiscountMapper;
private final OrderRefundMapper orderRefundMapper;
private final OrderEventManager orderEventManager;
@Override
@Transactional
public Long createOrder(MobileOrderRequest request, Long userId, Long scenicId, PriceCalculationResult priceResult) {
Date now = new Date();
// 1. 生成订单号
String orderNo = generateOrderNo();
// 2. 创建订单基础信息
OrderV2 order = new OrderV2();
order.setOrderNo(orderNo);
order.setMemberId(userId);
order.setOpenId(request.getFaceId().toString()); // 临时使用faceId,实际应传入openId
order.setFaceId(request.getFaceId());
order.setScenicId(scenicId);
order.setOriginalAmount(priceResult.getOriginalAmount());
order.setDiscountAmount(priceResult.getDiscountAmount());
order.setFinalAmount(priceResult.getFinalAmount());
order.setOrderStatus(OrderStatus.PENDING_PAYMENT);
order.setPaymentStatus(PaymentStatus.UNPAID);
order.setRefundStatus(RefundStatus.NO_REFUND);
order.setTotalRefundAmount(BigDecimal.ZERO);
order.setRemarks(request.getRemarks());
order.setCreateTime(now);
order.setUpdateTime(now);
order.setCreateBy(userId);
order.setUpdateBy(userId);
order.setDeleted(0);
// 保存订单
orderMapper.insert(order);
Long orderId = order.getId();
log.info("订单基础信息创建成功: orderId={}, orderNo={}", orderId, orderNo);
// 3. 保存订单商品明细
for (ProductItem product : request.getProducts()) {
OrderItemV2 orderItem = new OrderItemV2();
orderItem.setOrderId(orderId);
orderItem.setProductType(product.getProductType().name());
orderItem.setProductId(product.getProductId());
orderItem.setProductName(getProductName(product)); // 根据商品类型和ID获取商品名称
orderItem.setQuantity(product.getQuantity());
orderItem.setUnitPrice(product.getUnitPrice());
orderItem.setOriginalAmount(product.getOriginalPrice());
orderItem.setFinalAmount(product.getSubtotal());
orderItem.setCreateTime(now);
orderItem.setUpdateTime(now);
orderItemMapper.insert(orderItem);
}
log.info("订单商品明细保存成功: orderId={}, itemCount={}", orderId, request.getProducts().size());
// 4. 记录使用的优惠券信息
if (priceResult.getUsedCoupon() != null) {
OrderDiscountV2 couponDiscount = new OrderDiscountV2();
couponDiscount.setOrderId(orderId);
couponDiscount.setDiscountType(DiscountType.COUPON);
couponDiscount.setDiscountName(priceResult.getUsedCoupon().getCouponName());
couponDiscount.setDiscountAmount(priceResult.getUsedCoupon().getActualDiscountAmount());
couponDiscount.setSortOrder(3); // 优惠券显示顺序
couponDiscount.setCouponId(priceResult.getUsedCoupon().getCouponId());
couponDiscount.setCreateTime(now);
orderDiscountMapper.insert(couponDiscount);
log.info("优惠券记录保存成功: orderId={}, couponId={}, discountAmount={}",
orderId, priceResult.getUsedCoupon().getCouponId(), priceResult.getUsedCoupon().getActualDiscountAmount());
}
// 5. 记录使用的券码信息
if (priceResult.getUsedVoucher() != null) {
VoucherInfo voucherInfo = priceResult.getUsedVoucher();
OrderDiscountV2 voucherDiscount = new OrderDiscountV2();
voucherDiscount.setOrderId(orderId);
voucherDiscount.setDiscountType(DiscountType.VOUCHER);
voucherDiscount.setDiscountName(voucherInfo.getBatchName());
voucherDiscount.setDiscountAmount(voucherInfo.getActualDiscountAmount());
voucherDiscount.setSortOrder(1); // 券码显示顺序最高
voucherDiscount.setVoucherCode(voucherInfo.getVoucherCode());
voucherDiscount.setCreateTime(now);
orderDiscountMapper.insert(voucherDiscount);
log.info("券码记录保存成功: orderId={}, voucherCode={}, discountAmount={}",
orderId, voucherInfo.getVoucherCode(), voucherInfo.getActualDiscountAmount());
}
// 6. 记录其他优惠信息(如限时立减等)
if (priceResult.getDiscountDetails() != null) {
for (DiscountDetail discount : priceResult.getDiscountDetails()) {
// 跳过已经记录的优惠券和券码
if ("VOUCHER".equals(discount.getDiscountType()) || "COUPON".equals(discount.getDiscountType())) {
continue;
}
OrderDiscountV2 otherDiscount = new OrderDiscountV2();
otherDiscount.setOrderId(orderId);
otherDiscount.setDiscountType(DiscountType.fromCode(discount.getDiscountType()));
otherDiscount.setDiscountName(discount.getDiscountName());
otherDiscount.setDiscountAmount(discount.getDiscountAmount());
otherDiscount.setSortOrder(discount.getSortOrder());
otherDiscount.setCreateTime(now);
orderDiscountMapper.insert(otherDiscount);
log.info("其他优惠记录保存成功: orderId={}, type={}, discountAmount={}",
orderId, discount.getDiscountType(), discount.getDiscountAmount());
}
}
log.info("订单创建完成: orderId={}, orderNo={}, finalAmount={}",
orderId, orderNo, priceResult.getFinalAmount());
return orderId;
}
@Override
public OrderV2 getByOrderNo(String orderNo) {
QueryWrapper<OrderV2> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_no", orderNo);
queryWrapper.eq("deleted", 0);
return orderMapper.selectOne(queryWrapper);
}
@Override
public OrderV2 getById(Long orderId) {
return orderMapper.selectById(orderId);
}
@Override
public boolean updateOrderStatus(Long orderId, String orderStatus, String paymentStatus) {
OrderV2 order = new OrderV2();
order.setId(orderId);
order.setOrderStatus(OrderStatus.fromCode(orderStatus));
order.setPaymentStatus(PaymentStatus.fromCode(paymentStatus));
order.setUpdateTime(new Date());
if ("PAID".equals(paymentStatus)) {
order.setPayTime(new Date());
}
if ("COMPLETED".equals(orderStatus)) {
order.setCompleteTime(new Date());
}
return orderMapper.updateById(order) > 0;
}
@Override
public String generateOrderNo() {
// 生成订单号:ORDER + 时间戳 + 3位随机数
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String timestamp = sdf.format(new Date());
int random = (int) (Math.random() * 900) + 100;
return "ORDER" + timestamp + random;
}
/**
* 根据商品类型和ID获取商品名称
* TODO: 实际应该从商品配置表或其他数据源获取
*/
private String getProductName(ProductItem product) {
return product.getProductType().getDescription() + "-" + product.getProductId();
}
// ====== 新增方法实现 ======
@Override
public PageInfo<OrderV2ListResponse> pageOrders(OrderV2PageRequest request) {
PageHelper.startPage(request.getPageNum(), request.getPageSize());
QueryWrapper<OrderV2> queryWrapper = buildOrderQueryWrapper(request);
List<OrderV2> orders = orderMapper.selectList(queryWrapper);
// 转换为响应DTO
List<OrderV2ListResponse> responseList = orders.stream()
.map(this::convertToListResponse)
.collect(Collectors.toList());
return new PageInfo<>(responseList);
}
@Override
public OrderV2DetailResponse getOrderDetail(Long orderId) {
// 查询订单基础信息
OrderV2 order = orderMapper.selectById(orderId);
if (order == null || order.getDeleted() == 1) {
return null;
}
// 查询订单商品明细
QueryWrapper<OrderItemV2> itemQuery = new QueryWrapper<>();
itemQuery.eq("order_id", orderId);
List<OrderItemV2> orderItems = orderItemMapper.selectList(itemQuery);
// 查询订单优惠记录
QueryWrapper<OrderDiscountV2> discountQuery = new QueryWrapper<>();
discountQuery.eq("order_id", orderId).orderByAsc("sort_order");
List<OrderDiscountV2> orderDiscounts = orderDiscountMapper.selectList(discountQuery);
// 查询订单退款记录
QueryWrapper<OrderRefundV2> refundQuery = new QueryWrapper<>();
refundQuery.eq("order_id", orderId).orderByDesc("create_time");
List<OrderRefundV2> orderRefunds = orderRefundMapper.selectList(refundQuery);
// 转换为响应DTO
return convertToDetailResponse(order, orderItems, orderDiscounts, orderRefunds);
}
@Override
@Transactional
public boolean updateOrderRemarks(Long orderId, String remarks) {
OrderV2 order = new OrderV2();
order.setId(orderId);
order.setRemarks(remarks);
order.setUpdateTime(new Date());
return orderMapper.updateById(order) > 0;
}
@Override
@Transactional
public boolean updatePaymentStatus(Long orderId, String paymentStatus) {
// 先查询当前订单状态
OrderV2 currentOrder = orderMapper.selectById(orderId);
if (currentOrder == null) {
log.warn("订单不存在: orderId={}", orderId);
return false;
}
PaymentStatus oldPaymentStatus = currentOrder.getPaymentStatus();
PaymentStatus newPaymentStatus = PaymentStatus.fromCode(paymentStatus);
// 更新订单支付状态
OrderV2 order = new OrderV2();
order.setId(orderId);
order.setPaymentStatus(newPaymentStatus);
order.setUpdateTime(new Date());
if ("PAID".equals(paymentStatus)) {
order.setPayTime(new Date());
// 支付完成后,订单状态也需要更新
order.setOrderStatus(OrderStatus.PAID);
}
boolean success = orderMapper.updateById(order) > 0;
// 发布支付状态变更事件
if (success && !oldPaymentStatus.equals(newPaymentStatus)) {
PaymentStatusChangeEvent event = new PaymentStatusChangeEvent(
orderId, currentOrder.getOrderNo(),
oldPaymentStatus, newPaymentStatus,
"支付状态更新", null
);
orderEventManager.publishPaymentStatusChangeEvent(event);
}
return success;
}
@Override
@Transactional
public boolean updateRefundStatus(Long orderId, RefundStatus refundStatus) {
// 先查询当前订单状态
OrderV2 currentOrder = orderMapper.selectById(orderId);
if (currentOrder == null) {
log.warn("订单不存在: orderId={}", orderId);
return false;
}
RefundStatus oldRefundStatus = currentOrder.getRefundStatus();
// 更新订单退款状态
OrderV2 order = new OrderV2();
order.setId(orderId);
order.setRefundStatus(refundStatus);
order.setUpdateTime(new Date());
// 根据退款状态更新订单状态
if (RefundStatus.FULL_REFUND.equals(refundStatus)) {
order.setOrderStatus(OrderStatus.REFUNDED);
} else if (RefundStatus.REFUND_PROCESSING.equals(refundStatus)) {
order.setOrderStatus(OrderStatus.REFUNDING);
}
boolean success = orderMapper.updateById(order) > 0;
// 发布退款状态变更事件
if (success && !oldRefundStatus.equals(refundStatus)) {
RefundStatusChangeEvent event = new RefundStatusChangeEvent(
orderId, currentOrder.getOrderNo(), null, null,
oldRefundStatus, refundStatus, null,
"退款状态更新", null
);
orderEventManager.publishRefundStatusChangeEvent(event);
}
return success;
}
@Override
@Transactional
public Long createRefundRecord(RefundRequest request) {
Date now = new Date();
// 生成退款单号
String refundNo = generateRefundNo();
// 创建退款记录
OrderRefundV2 refund = new OrderRefundV2();
refund.setOrderId(request.getOrderId());
refund.setRefundNo(refundNo);
refund.setRefundType(RefundType.fromCode(request.getRefundType()));
refund.setRefundAmount(request.getRefundAmount());
refund.setRefundFee(request.getRefundFee());
refund.setRefundStatus(com.ycwl.basic.order.enums.RefundStatus.REFUND_PROCESSING);
refund.setRefundReason(request.getRefundReason());
refund.setRefundDescription(request.getRefundDescription());
refund.setOperatorRemarks(request.getOperatorRemarks());
refund.setRefundChannel(request.getRefundChannel());
refund.setApplyTime(now);
refund.setCreateTime(now);
refund.setUpdateTime(now);
orderRefundMapper.insert(refund);
// 更新订单退款状态
updateRefundStatus(request.getOrderId(), com.ycwl.basic.order.enums.RefundStatus.REFUND_PROCESSING);
log.info("退款记录创建成功: refundId={}, refundNo={}, orderId={}, refundAmount={}",
refund.getId(), refundNo, request.getOrderId(), request.getRefundAmount());
return refund.getId();
}
@Override
public PageInfo<OrderV2ListResponse> pageOrdersByUser(OrderV2PageRequest request) {
// 移动端查询,确保只查询当前用户的订单
return pageOrders(request);
}
// ====== 私有辅助方法 ======
/**
* 构建订单查询条件
*/
private QueryWrapper<OrderV2> buildOrderQueryWrapper(OrderV2PageRequest request) {
QueryWrapper<OrderV2> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("deleted", 0);
if (request.getOrderNo() != null && !request.getOrderNo().trim().isEmpty()) {
queryWrapper.like("order_no", request.getOrderNo().trim());
}
if (request.getMemberId() != null) {
queryWrapper.eq("member_id", request.getMemberId());
}
if (request.getScenicId() != null) {
queryWrapper.eq("scenic_id", request.getScenicId());
}
if (request.getOrderStatus() != null && !request.getOrderStatus().trim().isEmpty()) {
queryWrapper.eq("order_status", request.getOrderStatus().trim());
}
if (request.getPaymentStatus() != null && !request.getPaymentStatus().trim().isEmpty()) {
queryWrapper.eq("payment_status", request.getPaymentStatus().trim());
}
if (request.getRefundStatus() != null && !request.getRefundStatus().trim().isEmpty()) {
queryWrapper.eq("refund_status", request.getRefundStatus().trim());
}
if (request.getCreateTimeStart() != null) {
queryWrapper.ge("create_time", request.getCreateTimeStart());
}
if (request.getCreateTimeEnd() != null) {
queryWrapper.le("create_time", request.getCreateTimeEnd());
}
if (request.getPayTimeStart() != null) {
queryWrapper.ge("pay_time", request.getPayTimeStart());
}
if (request.getPayTimeEnd() != null) {
queryWrapper.le("pay_time", request.getPayTimeEnd());
}
queryWrapper.orderByDesc("create_time");
return queryWrapper;
}
/**
* 转换为列表响应DTO
*/
private OrderV2ListResponse convertToListResponse(OrderV2 order) {
OrderV2ListResponse response = new OrderV2ListResponse();
response.setId(order.getId());
response.setOrderNo(order.getOrderNo());
response.setMemberId(order.getMemberId());
response.setOpenId(order.getOpenId());
response.setFaceId(order.getFaceId());
response.setScenicId(order.getScenicId());
response.setOriginalAmount(order.getOriginalAmount());
response.setDiscountAmount(order.getDiscountAmount());
response.setFinalAmount(order.getFinalAmount());
response.setOrderStatus(order.getOrderStatus());
response.setOrderStatusDesc(order.getOrderStatus().getDescription());
response.setPaymentStatus(order.getPaymentStatus());
response.setPaymentStatusDesc(order.getPaymentStatus().getDescription());
response.setRefundStatus(order.getRefundStatus());
response.setRefundStatusDesc(order.getRefundStatus().getDescription());
response.setTotalRefundAmount(order.getTotalRefundAmount());
response.setRemarks(order.getRemarks());
response.setPayTime(order.getPayTime());
response.setCompleteTime(order.getCompleteTime());
response.setCreateTime(order.getCreateTime());
response.setUpdateTime(order.getUpdateTime());
// 查询商品数量信息
QueryWrapper<OrderItemV2> itemQuery = new QueryWrapper<>();
itemQuery.eq("order_id", order.getId());
List<OrderItemV2> items = orderItemMapper.selectList(itemQuery);
response.setItemCount(items.size());
response.setTotalQuantity(items.stream().mapToInt(OrderItemV2::getQuantity).sum());
return response;
}
/**
* 转换为详情响应DTO
*/
private OrderV2DetailResponse convertToDetailResponse(OrderV2 order,
List<OrderItemV2> orderItems,
List<OrderDiscountV2> orderDiscounts,
List<OrderRefundV2> orderRefunds) {
OrderV2DetailResponse response = new OrderV2DetailResponse();
response.setId(order.getId());
response.setOrderNo(order.getOrderNo());
response.setMemberId(order.getMemberId());
response.setOpenId(order.getOpenId());
response.setFaceId(order.getFaceId());
response.setScenicId(order.getScenicId());
response.setOriginalAmount(order.getOriginalAmount());
response.setDiscountAmount(order.getDiscountAmount());
response.setFinalAmount(order.getFinalAmount());
response.setOrderStatus(order.getOrderStatus());
response.setOrderStatusDesc(order.getOrderStatus().getDescription());
response.setPaymentStatus(order.getPaymentStatus());
response.setPaymentStatusDesc(order.getPaymentStatus().getDescription());
response.setRefundStatus(order.getRefundStatus());
response.setRefundStatusDesc(order.getRefundStatus().getDescription());
response.setTotalRefundAmount(order.getTotalRefundAmount());
response.setRemarks(order.getRemarks());
response.setPayTime(order.getPayTime());
response.setCompleteTime(order.getCompleteTime());
response.setCreateTime(order.getCreateTime());
response.setUpdateTime(order.getUpdateTime());
response.setCreateBy(order.getCreateBy());
response.setUpdateBy(order.getUpdateBy());
response.setOrderItems(orderItems);
response.setOrderDiscounts(orderDiscounts);
response.setOrderRefunds(orderRefunds);
return response;
}
/**
* 生成退款单号
*/
private String generateRefundNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String timestamp = sdf.format(new Date());
int random = (int) (Math.random() * 900) + 100;
return "REFUND" + timestamp + random;
}
}