重构订单创建逻辑:采用Builder模式替代20+参数方法
主要改进: - 新增OrderCreationRequest及相关DTO类,使用Builder模式提升代码可读性 - 引入类型安全的枚举类OrderConstant,替代魔法字符串常量 - 重构PlayOrderInfoServiceImpl,新增基于Builder模式的createOrderInfo方法 - 保留原有方法并标记为@Deprecated,确保向后兼容性 - 完善单元测试覆盖,包含Mockito模拟和边界条件测试 - 优化包结构,将DTO类从vo包迁移到dto包 - 添加JUnit 5和Mockito测试依赖 - 移除实体类过度使用的Lombok注解,改用精简的自定义构造器 - 新增数据库开发工作流程文档 技术栈: - Spring Boot 2.7.9 - MyBatis-Plus 3.5.3.2 - JUnit 5 + Mockito - Lombok Builder模式 - 类型安全枚举设计
This commit is contained in:
@@ -1,44 +1,136 @@
|
||||
package com.starry.admin.modules.order.module.constant;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 订单相关枚举和常量
|
||||
*
|
||||
* @author admin
|
||||
* @since 2024/5/8 15:41
|
||||
**/
|
||||
*/
|
||||
public class OrderConstant {
|
||||
|
||||
/**
|
||||
* 订单状态-待接单
|
||||
*
|
||||
* @since 2024/5/8 15:42
|
||||
**/
|
||||
* 订单状态枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum OrderStatus {
|
||||
PENDING("0", "已下单(待接单)"),
|
||||
ACCEPTED("1", "已接单(待开始)"),
|
||||
IN_PROGRESS("2", "已开始(服务中)"),
|
||||
COMPLETED("3", "已完成"),
|
||||
CANCELLED("4", "已取消");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
OrderStatus(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static OrderStatus fromCode(String code) {
|
||||
for (OrderStatus status : values()) {
|
||||
if (status.code.equals(code)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown order status code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单类型枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum OrderType {
|
||||
REFUND("-1", "退款订单"),
|
||||
RECHARGE("0", "充值订单"),
|
||||
WITHDRAWAL("1", "提现订单"),
|
||||
NORMAL("2", "普通订单");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
OrderType(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static OrderType fromCode(String code) {
|
||||
for (OrderType type : values()) {
|
||||
if (type.code.equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown order type code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下单类型枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum PlaceType {
|
||||
OTHER("-1", "其他类型"),
|
||||
SPECIFIED("0", "指定单"),
|
||||
RANDOM("1", "随机单"),
|
||||
REWARD("2", "打赏单");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
PlaceType(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static PlaceType fromCode(String code) {
|
||||
for (PlaceType type : values()) {
|
||||
if (type.code.equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown place type code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 性别枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum Gender {
|
||||
UNKNOWN("0", "未知"),
|
||||
MALE("1", "男"),
|
||||
FEMALE("2", "女");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
Gender(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static Gender fromCode(String code) {
|
||||
for (Gender gender : values()) {
|
||||
if (gender.code.equals(code)) {
|
||||
return gender;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown gender code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy constants for backward compatibility - consider deprecating
|
||||
@Deprecated
|
||||
public final static String ORDER_STATUS_0 = "0";
|
||||
|
||||
/**
|
||||
* 订单状态-待开始
|
||||
*
|
||||
* @since 2024/5/8 15:42
|
||||
**/
|
||||
@Deprecated
|
||||
public final static String ORDER_STATUS_1 = "1";
|
||||
|
||||
/**
|
||||
* 订单状态-服务中
|
||||
*
|
||||
* @since 2024/5/8 15:42
|
||||
**/
|
||||
@Deprecated
|
||||
public final static String ORDER_STATUS_2 = "2";
|
||||
|
||||
/**
|
||||
* 订单状态-已完成
|
||||
*
|
||||
* @since 2024/5/8 15:42
|
||||
**/
|
||||
@Deprecated
|
||||
public final static String ORDER_STATUS_3 = "3";
|
||||
|
||||
/**
|
||||
* 订单状态-已取消
|
||||
*
|
||||
* @since 2024/5/8 15:42
|
||||
**/
|
||||
@Deprecated
|
||||
public final static String ORDER_STATUS_4 = "4";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.starry.admin.modules.order.module.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 商品信息值对象
|
||||
*
|
||||
* @author admin
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class CommodityInfo {
|
||||
/**
|
||||
* 商品ID
|
||||
*/
|
||||
private String commodityId;
|
||||
|
||||
/**
|
||||
* 商品类型[0:礼物,1:服务]
|
||||
*/
|
||||
private String commodityType;
|
||||
|
||||
/**
|
||||
* 商品单价
|
||||
*/
|
||||
private BigDecimal commodityPrice;
|
||||
|
||||
/**
|
||||
* 商品属性-服务时长
|
||||
*/
|
||||
private String serviceDuration;
|
||||
|
||||
/**
|
||||
* 商品名称
|
||||
*/
|
||||
private String commodityName;
|
||||
|
||||
/**
|
||||
* 商品数量
|
||||
*/
|
||||
private String commodityNumber;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.starry.admin.modules.order.module.dto;
|
||||
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 订单创建请求对象 - 使用Builder模式替换20+参数的方法
|
||||
*
|
||||
* @author admin
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class OrderCreationRequest {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
@NotBlank(message = "订单ID不能为空")
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
@NotBlank(message = "订单编号不能为空")
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 订单状态
|
||||
*/
|
||||
@NotNull(message = "订单状态不能为空")
|
||||
private OrderConstant.OrderStatus orderStatus;
|
||||
|
||||
/**
|
||||
* 订单类型
|
||||
*/
|
||||
@NotNull(message = "订单类型不能为空")
|
||||
private OrderConstant.OrderType orderType;
|
||||
|
||||
/**
|
||||
* 下单类型
|
||||
*/
|
||||
@NotNull(message = "下单类型不能为空")
|
||||
private OrderConstant.PlaceType placeType;
|
||||
|
||||
/**
|
||||
* 打赏类型(0:余额;1:礼物)
|
||||
*/
|
||||
private String rewardType;
|
||||
|
||||
/**
|
||||
* 是否是首单
|
||||
*/
|
||||
private boolean isFirstOrder;
|
||||
|
||||
/**
|
||||
* 商品信息
|
||||
*/
|
||||
@Valid
|
||||
@NotNull(message = "商品信息不能为空")
|
||||
private CommodityInfo commodityInfo;
|
||||
|
||||
/**
|
||||
* 支付信息
|
||||
*/
|
||||
@Valid
|
||||
@NotNull(message = "支付信息不能为空")
|
||||
private PaymentInfo paymentInfo;
|
||||
|
||||
/**
|
||||
* 下单人
|
||||
*/
|
||||
@NotBlank(message = "下单人不能为空")
|
||||
private String purchaserBy;
|
||||
|
||||
/**
|
||||
* 接单人(可选)
|
||||
*/
|
||||
private String acceptBy;
|
||||
|
||||
/**
|
||||
* 微信号码
|
||||
*/
|
||||
private String weiChatCode;
|
||||
|
||||
/**
|
||||
* 订单备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 随机单要求(仅随机单时需要)
|
||||
*/
|
||||
private RandomOrderRequirements randomOrderRequirements;
|
||||
|
||||
/**
|
||||
* 获取首单标识字符串(兼容现有系统)
|
||||
*/
|
||||
public String getFirstOrderString() {
|
||||
return isFirstOrder ? "1" : "0";
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证随机单要求
|
||||
*/
|
||||
public boolean isValidForRandomOrder() {
|
||||
return placeType == OrderConstant.PlaceType.RANDOM
|
||||
&& randomOrderRequirements != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为打赏单
|
||||
*/
|
||||
public boolean isRewardOrder() {
|
||||
return placeType == OrderConstant.PlaceType.REWARD;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为指定单
|
||||
*/
|
||||
public boolean isSpecifiedOrder() {
|
||||
return placeType == OrderConstant.PlaceType.SPECIFIED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.starry.admin.modules.order.module.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 支付信息值对象
|
||||
*
|
||||
* @author admin
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class PaymentInfo {
|
||||
/**
|
||||
* 订单金额
|
||||
*/
|
||||
private BigDecimal orderMoney;
|
||||
|
||||
/**
|
||||
* 订单最终金额(支付金额)
|
||||
*/
|
||||
private BigDecimal finalAmount;
|
||||
|
||||
/**
|
||||
* 优惠金额
|
||||
*/
|
||||
private BigDecimal discountAmount;
|
||||
|
||||
/**
|
||||
* 优惠券ID列表
|
||||
*/
|
||||
private List<String> couponIds;
|
||||
|
||||
/**
|
||||
* 支付方式,0:余额支付,1:微信支付,2:支付宝支付
|
||||
*/
|
||||
private String payMethod;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.starry.admin.modules.order.module.dto;
|
||||
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 随机单要求信息值对象
|
||||
*
|
||||
* @author admin
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class RandomOrderRequirements {
|
||||
/**
|
||||
* 随机单要求-店员性别
|
||||
*/
|
||||
private OrderConstant.Gender clerkGender;
|
||||
|
||||
/**
|
||||
* 随机单要求-店员等级ID
|
||||
*/
|
||||
private String clerkLevelId;
|
||||
|
||||
/**
|
||||
* 随机单要求-是否排除下单过的成员(0:不排除;1:排除)
|
||||
*/
|
||||
private String excludeHistory;
|
||||
|
||||
/**
|
||||
* 是否排除历史订单
|
||||
*/
|
||||
public boolean shouldExcludeHistory() {
|
||||
return "1".equals(excludeHistory);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.starry.admin.modules.order.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.starry.admin.modules.order.module.dto.*;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.module.vo.*;
|
||||
import com.starry.admin.modules.weichat.entity.order.*;
|
||||
@@ -43,8 +44,18 @@ public interface IPlayOrderInfoService extends IService<PlayOrderInfoEntity> {
|
||||
void createRechargeOrder(String orderNo, BigDecimal orderMoney, BigDecimal finalAmount, String purchaserBy);
|
||||
|
||||
/**
|
||||
* 新增订单信息
|
||||
* 新增订单信息 - 重构版本使用Builder模式
|
||||
*
|
||||
* @param request 订单创建请求对象
|
||||
* @author admin
|
||||
* @since 2024/6/3 10:53
|
||||
**/
|
||||
void createOrderInfo(OrderCreationRequest request);
|
||||
|
||||
/**
|
||||
* 新增订单信息 - 旧版本方法(已废弃,建议使用OrderCreationRequest)
|
||||
*
|
||||
* @deprecated 请使用 {@link #createOrderInfo(OrderCreationRequest)} 替代
|
||||
* @param orderId
|
||||
* 订单ID
|
||||
* @param orderNo
|
||||
@@ -96,6 +107,7 @@ public interface IPlayOrderInfoService extends IService<PlayOrderInfoEntity> {
|
||||
* @author admin
|
||||
* @since 2024/6/3 10:53
|
||||
**/
|
||||
@Deprecated
|
||||
void createOrderInfo(String orderId, String orderNo, String orderState, String orderType, String placeType,
|
||||
String rewardType, String firstOrder, String commodityId, String commodityType, BigDecimal commodityPrice,
|
||||
String serviceDuration, String commodityName, String commodityNumber, BigDecimal orderMoney,
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.dto.*;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderComplaintInfoEntity;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderEvaluateInfoEntity;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
@@ -158,6 +159,132 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
playCouponDetailsService.updateCouponUseStateByIds(couponIds, "2");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createOrderInfo(OrderCreationRequest request) {
|
||||
// 验证请求
|
||||
validateOrderCreationRequest(request);
|
||||
|
||||
PlayOrderInfoEntity entity = buildOrderEntity(request);
|
||||
|
||||
// 处理随机单要求
|
||||
if (request.getPlaceType() == OrderConstant.PlaceType.RANDOM) {
|
||||
setRandomOrderRequirements(entity, request.getRandomOrderRequirements());
|
||||
}
|
||||
|
||||
// 处理接单人信息
|
||||
if (StrUtil.isNotBlank(request.getAcceptBy())) {
|
||||
setAcceptByInfo(entity, request);
|
||||
}
|
||||
|
||||
// 处理打赏单自动完成逻辑
|
||||
if (request.isRewardOrder()) {
|
||||
setRewardOrderCompleted(entity);
|
||||
}
|
||||
|
||||
// 保存订单
|
||||
userInfoService.saveOrderInfo(entity);
|
||||
this.baseMapper.insert(entity);
|
||||
|
||||
// 修改优惠券状态
|
||||
playCouponDetailsService.updateCouponUseStateByIds(
|
||||
request.getPaymentInfo().getCouponIds(), "2");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证订单创建请求
|
||||
*/
|
||||
private void validateOrderCreationRequest(OrderCreationRequest request) {
|
||||
if (request.getPlaceType() == OrderConstant.PlaceType.RANDOM
|
||||
&& !request.isValidForRandomOrder()) {
|
||||
throw new CustomException("随机单必须提供店员要求信息");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建订单实体
|
||||
*/
|
||||
private PlayOrderInfoEntity buildOrderEntity(OrderCreationRequest request) {
|
||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity();
|
||||
|
||||
// 基本信息
|
||||
entity.setId(request.getOrderId());
|
||||
entity.setOrderNo(request.getOrderNo());
|
||||
entity.setOrderStatus(request.getOrderStatus().getCode());
|
||||
entity.setOrderType(request.getOrderType().getCode());
|
||||
entity.setPlaceType(request.getPlaceType().getCode());
|
||||
entity.setRewardType(request.getRewardType());
|
||||
entity.setFirstOrder(request.getFirstOrderString());
|
||||
|
||||
// 固定默认值
|
||||
entity.setRefundType("0");
|
||||
entity.setBackendEntry("0");
|
||||
entity.setPayMethod("0");
|
||||
entity.setOrderSettlementState("0");
|
||||
entity.setOrdersExpiredState("0");
|
||||
|
||||
// 商品信息
|
||||
CommodityInfo commodityInfo = request.getCommodityInfo();
|
||||
entity.setCommodityId(commodityInfo.getCommodityId());
|
||||
entity.setCommodityType(commodityInfo.getCommodityType());
|
||||
entity.setCommodityPrice(commodityInfo.getCommodityPrice());
|
||||
entity.setServiceDuration(commodityInfo.getServiceDuration());
|
||||
entity.setCommodityName(commodityInfo.getCommodityName());
|
||||
entity.setCommodityNumber(commodityInfo.getCommodityNumber());
|
||||
|
||||
// 支付信息
|
||||
PaymentInfo paymentInfo = request.getPaymentInfo();
|
||||
entity.setOrderMoney(paymentInfo.getOrderMoney());
|
||||
entity.setFinalAmount(paymentInfo.getFinalAmount());
|
||||
entity.setDiscountAmount(paymentInfo.getDiscountAmount());
|
||||
entity.setCouponIds(paymentInfo.getCouponIds());
|
||||
entity.setUseCoupon(
|
||||
paymentInfo.getCouponIds() != null && !paymentInfo.getCouponIds().isEmpty() ? "1" : "0");
|
||||
|
||||
// 用户信息
|
||||
entity.setPurchaserBy(request.getPurchaserBy());
|
||||
entity.setPurchaserTime(LocalDateTime.now());
|
||||
entity.setWeiChatCode(request.getWeiChatCode());
|
||||
entity.setRemark(request.getRemark());
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置随机单要求
|
||||
*/
|
||||
private void setRandomOrderRequirements(PlayOrderInfoEntity entity, RandomOrderRequirements requirements) {
|
||||
if (requirements != null) {
|
||||
entity.setSex(requirements.getClerkGender().getCode());
|
||||
entity.setLevelId(requirements.getClerkLevelId());
|
||||
entity.setExcludeHistory(requirements.getExcludeHistory());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置接单人信息
|
||||
*/
|
||||
private void setAcceptByInfo(PlayOrderInfoEntity entity, OrderCreationRequest request) {
|
||||
entity.setAcceptBy(request.getAcceptBy());
|
||||
ClerkEstimatedRevenueVo estimatedRevenueVo = getClerkEstimatedRevenue(
|
||||
request.getAcceptBy(),
|
||||
request.getPaymentInfo().getCouponIds(),
|
||||
request.getPlaceType().getCode(),
|
||||
request.getFirstOrderString(),
|
||||
request.getPaymentInfo().getFinalAmount());
|
||||
entity.setEstimatedRevenue(estimatedRevenueVo.getRevenueAmount());
|
||||
entity.setEstimatedRevenueRatio(estimatedRevenueVo.getRevenueRatio());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置打赏单为已完成状态
|
||||
*/
|
||||
private void setRewardOrderCompleted(PlayOrderInfoEntity entity) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
entity.setAcceptTime(now);
|
||||
entity.setOrderStartTime(now);
|
||||
entity.setOrderEndTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClerkEstimatedRevenueVo getClerkEstimatedRevenue(String clerkId, List<String> croupIds, String placeType,
|
||||
String firstOrder, BigDecimal finalAmount) {
|
||||
|
||||
Reference in New Issue
Block a user