fix: correct order lifecycle refunds and add coverage
Some checks failed
Build and Push Backend / docker (push) Failing after 5s
Some checks failed
Build and Push Backend / docker (push) Failing after 5s
This commit is contained in:
@@ -4,12 +4,13 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|||||||
import com.starry.admin.common.exception.CustomException;
|
import com.starry.admin.common.exception.CustomException;
|
||||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||||
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
|
||||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
|
||||||
|
import com.starry.admin.modules.order.module.dto.OrderRefundContext;
|
||||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||||
import com.starry.admin.modules.order.module.vo.*;
|
import com.starry.admin.modules.order.module.vo.*;
|
||||||
|
import com.starry.admin.modules.order.service.IOrderLifecycleService;
|
||||||
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||||
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
|
|
||||||
import com.starry.admin.modules.shop.module.vo.PlayCommodityInfoVo;
|
import com.starry.admin.modules.shop.module.vo.PlayCommodityInfoVo;
|
||||||
import com.starry.admin.modules.shop.service.IPlayCommodityInfoService;
|
import com.starry.admin.modules.shop.service.IPlayCommodityInfoService;
|
||||||
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
||||||
@@ -53,9 +54,7 @@ public class PlayOrderInfoController {
|
|||||||
private IPlayCommodityInfoService playCommodityInfoService;
|
private IPlayCommodityInfoService playCommodityInfoService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private IPlayOrderRefundInfoService playOrderRefundInfoService;
|
private IOrderLifecycleService orderLifecycleService;
|
||||||
@Resource
|
|
||||||
private IPlayCustomUserInfoService customUserInfoService;
|
|
||||||
@Resource
|
@Resource
|
||||||
private IPlayClerkUserInfoService clerkUserInfoService;
|
private IPlayClerkUserInfoService clerkUserInfoService;
|
||||||
|
|
||||||
@@ -89,38 +88,14 @@ public class PlayOrderInfoController {
|
|||||||
// @PreAuthorize("@customSs.hasPermission('order:order:update')")
|
// @PreAuthorize("@customSs.hasPermission('order:order:update')")
|
||||||
@PostMapping("/orderRefund")
|
@PostMapping("/orderRefund")
|
||||||
public R orderRefund(@ApiParam(value = "退款信息", required = true) @Validated @RequestBody PlayOrderRefundAddVo vo) {
|
public R orderRefund(@ApiParam(value = "退款信息", required = true) @Validated @RequestBody PlayOrderRefundAddVo vo) {
|
||||||
PlayOrderInfoEntity orderInfo = orderInfoService.selectOrderInfoById(vo.getOrderId());
|
OrderRefundContext context = new OrderRefundContext();
|
||||||
if (orderInfo.getFinalAmount().compareTo(vo.getRefundAmount()) < 0) {
|
context.setOrderId(vo.getOrderId());
|
||||||
throw new CustomException("退款金额不能大于支付金额");
|
context.setRefundAmount(vo.getRefundAmount());
|
||||||
}
|
context.setRefundReason(vo.getRefundReason());
|
||||||
if ("3".equals(orderInfo.getOrderStatus())) {
|
context.setOperatorType(OperatorType.ADMIN.getCode());
|
||||||
throw new CustomException("【已完成】的订单无法操作退款");
|
context.setOperatorId(SecurityUtils.getUserId());
|
||||||
}
|
context.withTriggerSource(OrderTriggerSource.ADMIN_API);
|
||||||
if ("4".equals(orderInfo.getOrderStatus())) {
|
orderLifecycleService.refundOrder(context);
|
||||||
throw new CustomException("【已取消】的订单无法操作退款");
|
|
||||||
}
|
|
||||||
if ("1".equals(orderInfo.getRefundType())) {
|
|
||||||
throw new CustomException("每个订单只能退款一次~");
|
|
||||||
}
|
|
||||||
PlayOrderInfoEntity updateOrderInfo = new PlayOrderInfoEntity();
|
|
||||||
updateOrderInfo.setId(orderInfo.getId());
|
|
||||||
updateOrderInfo.setRefundType("1");
|
|
||||||
// 订单退款,订单状态变为已取消
|
|
||||||
updateOrderInfo.setOrderStatus("4");
|
|
||||||
updateOrderInfo.setRefundAmount(vo.getRefundAmount());
|
|
||||||
// 修改订单状态
|
|
||||||
orderInfoService.update(updateOrderInfo);
|
|
||||||
// 记录退款信息
|
|
||||||
String refundType = orderInfo.getFinalAmount().compareTo(vo.getRefundAmount()) == 0 ? "0" : "1";
|
|
||||||
|
|
||||||
PlayCustomUserInfoEntity customUserInfo = customUserInfoService.getById(orderInfo.getPurchaserBy());
|
|
||||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(),
|
|
||||||
customUserInfo.getAccountBalance().add(orderInfo.getOrderMoney()), "3", "订单退款",
|
|
||||||
orderInfo.getOrderMoney(), BigDecimal.ZERO, vo.getOrderId());
|
|
||||||
|
|
||||||
playOrderRefundInfoService.add(orderInfo.getId(), orderInfo.getPurchaserBy(), orderInfo.getAcceptBy(),
|
|
||||||
orderInfo.getPayMethod(), refundType, vo.getRefundAmount(), vo.getRefundReason(), "2",
|
|
||||||
SecurityUtils.getUserId(), "0", "0");
|
|
||||||
return R.ok("退款成功");
|
return R.ok("退款成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ import com.starry.admin.common.exception.CustomException;
|
|||||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||||
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||||
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
||||||
|
import com.starry.admin.modules.order.module.dto.OrderCompletionContext;
|
||||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||||
|
import com.starry.admin.modules.order.service.IOrderLifecycleService;
|
||||||
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
||||||
import com.starry.common.redis.RedisCache;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -18,7 +19,6 @@ import java.util.List;
|
|||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -33,6 +33,8 @@ public class OrderJob {
|
|||||||
@Resource
|
@Resource
|
||||||
private IPlayClerkUserInfoService clerkUserInfoService;
|
private IPlayClerkUserInfoService clerkUserInfoService;
|
||||||
@Resource
|
@Resource
|
||||||
|
private IOrderLifecycleService orderLifecycleService;
|
||||||
|
@Resource
|
||||||
public RedisTemplate<String, Object> redisTemplate;
|
public RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
|
||||||
@@ -60,11 +62,10 @@ public class OrderJob {
|
|||||||
|
|
||||||
// 判断与开始时间相比较,如果大于服务时长,则修改订单状态为已完成
|
// 判断与开始时间相比较,如果大于服务时长,则修改订单状态为已完成
|
||||||
if (ca.getOrderStartTime().plusMinutes(serviceDuration).isBefore(LocalDateTime.now())) {
|
if (ca.getOrderStartTime().plusMinutes(serviceDuration).isBefore(LocalDateTime.now())) {
|
||||||
PlayOrderInfoEntity entity2 = new PlayOrderInfoEntity(ca.getId(), "3");
|
orderLifecycleService.completeOrder(
|
||||||
entity2.setOrderEndTime(LocalDateTime.now());
|
ca.getId(),
|
||||||
this.orderInfoMapper.updateById(entity2);
|
OrderCompletionContext.scheduler("auto finish by duration")
|
||||||
// 发送消息
|
.withForceNotify(true));
|
||||||
wxCustomMpService.sendOrderFinishMessage(ca);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -211,15 +211,142 @@ public class OrderConstant {
|
|||||||
public static final String EXCLUDE_HISTORY_NO = "0";
|
public static final String EXCLUDE_HISTORY_NO = "0";
|
||||||
public static final String EXCLUDE_HISTORY_YES = "1";
|
public static final String EXCLUDE_HISTORY_YES = "1";
|
||||||
|
|
||||||
// Legacy constants for backward compatibility - consider deprecating
|
@Getter
|
||||||
@Deprecated
|
public enum OrderRefundFlag {
|
||||||
public final static String ORDER_STATUS_0 = "0";
|
NOT_REFUNDED("0"),
|
||||||
@Deprecated
|
REFUNDED("1");
|
||||||
public final static String ORDER_STATUS_1 = "1";
|
|
||||||
@Deprecated
|
private final String code;
|
||||||
public final static String ORDER_STATUS_2 = "2";
|
|
||||||
@Deprecated
|
OrderRefundFlag(String code) {
|
||||||
public final static String ORDER_STATUS_3 = "3";
|
this.code = code;
|
||||||
@Deprecated
|
}
|
||||||
public final static String ORDER_STATUS_4 = "4";
|
|
||||||
|
public static OrderRefundFlag fromCode(String code) {
|
||||||
|
for (OrderRefundFlag flag : values()) {
|
||||||
|
if (flag.code.equals(code)) {
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unknown order refund flag code: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum OrderRefundRecordType {
|
||||||
|
FULL("0"),
|
||||||
|
PARTIAL("1");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
OrderRefundRecordType(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OrderRefundRecordType fromCode(String code) {
|
||||||
|
for (OrderRefundRecordType type : values()) {
|
||||||
|
if (type.code.equals(code)) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unknown refund record type code: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum OrderRefundState {
|
||||||
|
PROCESSING("0"),
|
||||||
|
SUCCESS("1"),
|
||||||
|
CLOSED("2"),
|
||||||
|
ABNORMAL("-1");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
OrderRefundState(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OrderRefundState fromCode(String code) {
|
||||||
|
for (OrderRefundState state : values()) {
|
||||||
|
if (state.code.equals(code)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unknown refund state code: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum ReviewRequirement {
|
||||||
|
NOT_REQUIRED("0"),
|
||||||
|
REQUIRED("1");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
ReviewRequirement(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReviewRequirement fromCode(String code) {
|
||||||
|
for (ReviewRequirement requirement : values()) {
|
||||||
|
if (requirement.code.equals(code)) {
|
||||||
|
return requirement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unknown review requirement code: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum BalanceOperationType {
|
||||||
|
RECHARGE("0"),
|
||||||
|
CONSUME("1"),
|
||||||
|
SERVICE("2"),
|
||||||
|
REFUND("3");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
BalanceOperationType(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BalanceOperationType fromCode(String code) {
|
||||||
|
for (BalanceOperationType type : values()) {
|
||||||
|
if (type.code.equals(code)) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unknown balance operation type code: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum OrderTriggerSource {
|
||||||
|
UNKNOWN("unknown"),
|
||||||
|
MANUAL("manual"),
|
||||||
|
WX_CUSTOMER("wx_customer"),
|
||||||
|
WX_CLERK("wx_clerk"),
|
||||||
|
ADMIN_CONSOLE("admin_console"),
|
||||||
|
ADMIN_API("admin_api"),
|
||||||
|
REWARD_ORDER("reward_order"),
|
||||||
|
SCHEDULER("scheduler"),
|
||||||
|
SYSTEM("system");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
OrderTriggerSource(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OrderTriggerSource fromCode(String code) {
|
||||||
|
for (OrderTriggerSource source : values()) {
|
||||||
|
if (source.code.equals(code)) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unknown order trigger source code: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.starry.admin.modules.order.module.dto;
|
||||||
|
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
|
||||||
|
import java.util.Objects;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单完成上下文信息,用于记录触发来源与操作人。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class OrderCompletionContext {
|
||||||
|
/**
|
||||||
|
* 操作人类型(0:顾客;1:店员;2:管理员),可为空用于系统任务。
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private String operatorType;
|
||||||
|
|
||||||
|
/** 操作人ID,可为空用于系统任务。 */
|
||||||
|
@Nullable
|
||||||
|
private String operatorId;
|
||||||
|
|
||||||
|
/** 触发来源描述,例如 wx_customer、scheduler、admin_panel。 */
|
||||||
|
private OrderTriggerSource triggerSource = OrderTriggerSource.UNKNOWN;
|
||||||
|
|
||||||
|
/** 额外备注信息,可为空。 */
|
||||||
|
@Nullable
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
/** 是否强制发送完成通知。 */
|
||||||
|
private boolean forceNotify;
|
||||||
|
|
||||||
|
public static OrderCompletionContext of(@Nullable String operatorType, @Nullable String operatorId, OrderTriggerSource triggerSource) {
|
||||||
|
Objects.requireNonNull(triggerSource, "triggerSource cannot be null");
|
||||||
|
OrderCompletionContext context = new OrderCompletionContext();
|
||||||
|
context.setOperatorType(operatorType);
|
||||||
|
context.setOperatorId(operatorId);
|
||||||
|
context.setTriggerSource(triggerSource);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OrderCompletionContext of(@Nullable String operatorType, @Nullable String operatorId, OrderTriggerSource triggerSource, @Nullable String comment) {
|
||||||
|
OrderCompletionContext context = of(operatorType, operatorId, triggerSource);
|
||||||
|
return context.withComment(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OrderCompletionContext scheduler(@Nullable String comment) {
|
||||||
|
return of(null, null, OrderTriggerSource.SCHEDULER, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OrderCompletionContext system(OrderTriggerSource triggerSource, @Nullable String comment) {
|
||||||
|
return of(null, null, triggerSource, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderCompletionContext withForceNotify(boolean forceNotify) {
|
||||||
|
this.forceNotify = forceNotify;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderCompletionContext withComment(@Nullable String comment) {
|
||||||
|
this.comment = comment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderCompletionContext withTriggerSource(OrderTriggerSource triggerSource) {
|
||||||
|
this.triggerSource = Objects.requireNonNull(triggerSource, "triggerSource");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.starry.admin.modules.order.module.dto;
|
||||||
|
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class OrderRefundContext {
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String orderId;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private BigDecimal refundAmount;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String refundReason;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String operatorType;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String operatorId;
|
||||||
|
|
||||||
|
private OrderTriggerSource triggerSource = OrderTriggerSource.UNKNOWN;
|
||||||
|
|
||||||
|
private boolean fullRefund;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
public OrderRefundContext withTriggerSource(OrderTriggerSource triggerSource) {
|
||||||
|
this.triggerSource = triggerSource;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderRefundContext withComment(@Nullable String comment) {
|
||||||
|
this.comment = comment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -313,4 +313,20 @@ public class PlayOrderInfoEntity extends BaseEntity<PlayOrderInfoEntity> {
|
|||||||
this.orderType = orderType;
|
this.orderType = orderType;
|
||||||
this.placeType = placeType;
|
this.placeType = placeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请使用 OrderCompletionService 统一处理订单完成逻辑,直接设置状态仅限初始化或遗留代码。
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setOrderStatus(String orderStatus) {
|
||||||
|
this.orderStatus = orderStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请使用 OrderCompletionService 统一处理订单结束时间写入。
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setOrderEndTime(LocalDateTime orderEndTime) {
|
||||||
|
this.orderEndTime = orderEndTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.starry.admin.modules.order.service;
|
||||||
|
|
||||||
|
import com.starry.admin.modules.order.module.dto.OrderCompletionContext;
|
||||||
|
import com.starry.admin.modules.order.module.dto.OrderRefundContext;
|
||||||
|
|
||||||
|
public interface IOrderLifecycleService {
|
||||||
|
|
||||||
|
void completeOrder(String orderId, OrderCompletionContext context);
|
||||||
|
|
||||||
|
void refundOrder(OrderRefundContext context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
package com.starry.admin.modules.order.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.starry.admin.common.exception.CustomException;
|
||||||
|
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.BalanceOperationType;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundFlag;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundRecordType;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundState;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
|
||||||
|
import com.starry.admin.modules.order.module.dto.OrderCompletionContext;
|
||||||
|
import com.starry.admin.modules.order.module.dto.OrderRefundContext;
|
||||||
|
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||||
|
import com.starry.admin.modules.order.service.IOrderLifecycleService;
|
||||||
|
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
|
||||||
|
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
||||||
|
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
|
||||||
|
import com.starry.admin.modules.withdraw.service.IEarningsService;
|
||||||
|
import com.starry.admin.utils.SecurityUtils;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PlayOrderInfoMapper orderInfoMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IEarningsService earningsService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private WxCustomMpService wxCustomMpService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IPlayOrderRefundInfoService orderRefundInfoService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IPlayCustomUserInfoService customUserInfoService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void completeOrder(String orderId, OrderCompletionContext context) {
|
||||||
|
if (StrUtil.isBlank(orderId)) {
|
||||||
|
throw new CustomException("订单ID不能为空");
|
||||||
|
}
|
||||||
|
PlayOrderInfoEntity order = orderInfoMapper.selectById(orderId);
|
||||||
|
if (order == null) {
|
||||||
|
throw new CustomException("订单不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderTriggerSource source = context != null && context.getTriggerSource() != null
|
||||||
|
? context.getTriggerSource()
|
||||||
|
: OrderTriggerSource.UNKNOWN;
|
||||||
|
boolean alreadyCompleted = OrderStatus.COMPLETED.getCode().equals(order.getOrderStatus());
|
||||||
|
if (!alreadyCompleted && !OrderStatus.IN_PROGRESS.getCode().equals(order.getOrderStatus())) {
|
||||||
|
log.warn("Skip completing order {}, unexpected status {}, source={}", orderId, order.getOrderStatus(), source.getCode());
|
||||||
|
throw new CustomException("订单状态异常,无法完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
LocalDateTime endTime = order.getOrderEndTime() != null ? order.getOrderEndTime() : now;
|
||||||
|
|
||||||
|
boolean statusUpdated = false;
|
||||||
|
if (!alreadyCompleted) {
|
||||||
|
PlayOrderInfoEntity update = new PlayOrderInfoEntity(orderId, OrderStatus.COMPLETED.getCode());
|
||||||
|
update.setOrderEndTime(endTime);
|
||||||
|
orderInfoMapper.updateById(update);
|
||||||
|
statusUpdated = true;
|
||||||
|
} else if (order.getOrderEndTime() == null) {
|
||||||
|
PlayOrderInfoEntity update = new PlayOrderInfoEntity(orderId, OrderStatus.COMPLETED.getCode());
|
||||||
|
update.setOrderEndTime(endTime);
|
||||||
|
orderInfoMapper.updateById(update);
|
||||||
|
statusUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayOrderInfoEntity latest = orderInfoMapper.selectById(orderId);
|
||||||
|
if (latest == null) {
|
||||||
|
throw new CustomException("订单不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean earningsCreated = ensureEarnings(latest, source);
|
||||||
|
boolean forceNotify = context != null && context.isForceNotify();
|
||||||
|
boolean shouldNotify = statusUpdated || (forceNotify && earningsCreated);
|
||||||
|
if (shouldNotify) {
|
||||||
|
wxCustomMpService.sendOrderFinishMessageAsync(latest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void refundOrder(OrderRefundContext context) {
|
||||||
|
if (context == null || StrUtil.isBlank(context.getOrderId())) {
|
||||||
|
throw new CustomException("订单ID不能为空");
|
||||||
|
}
|
||||||
|
PlayOrderInfoEntity order = orderInfoMapper.selectById(context.getOrderId());
|
||||||
|
if (order == null) {
|
||||||
|
throw new CustomException("订单不存在");
|
||||||
|
}
|
||||||
|
BigDecimal refundAmount = context.getRefundAmount();
|
||||||
|
if (refundAmount == null) {
|
||||||
|
throw new CustomException("退款金额不能为空");
|
||||||
|
}
|
||||||
|
if (refundAmount.compareTo(BigDecimal.ZERO) < 0) {
|
||||||
|
throw new CustomException("退款金额不能小于0");
|
||||||
|
}
|
||||||
|
BigDecimal finalAmount = order.getFinalAmount() == null ? BigDecimal.ZERO : order.getFinalAmount();
|
||||||
|
if (finalAmount.compareTo(refundAmount) < 0) {
|
||||||
|
throw new CustomException("退款金额不能大于支付金额");
|
||||||
|
}
|
||||||
|
if (OrderStatus.COMPLETED.getCode().equals(order.getOrderStatus())) {
|
||||||
|
throw new CustomException("【已完成】的订单无法操作退款");
|
||||||
|
}
|
||||||
|
if (OrderStatus.CANCELLED.getCode().equals(order.getOrderStatus())) {
|
||||||
|
throw new CustomException("【已取消】的订单无法操作退款");
|
||||||
|
}
|
||||||
|
if (OrderRefundFlag.REFUNDED.getCode().equals(order.getRefundType())) {
|
||||||
|
throw new CustomException("每个订单只能退款一次~");
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayOrderInfoEntity update = new PlayOrderInfoEntity();
|
||||||
|
update.setId(order.getId());
|
||||||
|
update.setRefundType(OrderRefundFlag.REFUNDED.getCode());
|
||||||
|
update.setOrderStatus(OrderStatus.CANCELLED.getCode());
|
||||||
|
update.setRefundAmount(refundAmount);
|
||||||
|
orderInfoMapper.updateById(update);
|
||||||
|
|
||||||
|
PlayCustomUserInfoEntity customUser = customUserInfoService.getById(order.getPurchaserBy());
|
||||||
|
if (customUser == null) {
|
||||||
|
throw new CustomException("顾客信息不存在");
|
||||||
|
}
|
||||||
|
BigDecimal currentBalance = customUser.getAccountBalance() == null ? BigDecimal.ZERO : customUser.getAccountBalance();
|
||||||
|
BigDecimal refundSourceAmount = refundAmount;
|
||||||
|
customUserInfoService.updateAccountBalanceById(
|
||||||
|
customUser.getId(),
|
||||||
|
currentBalance,
|
||||||
|
currentBalance.add(refundSourceAmount),
|
||||||
|
BalanceOperationType.REFUND.getCode(),
|
||||||
|
"订单退款",
|
||||||
|
refundSourceAmount,
|
||||||
|
BigDecimal.ZERO,
|
||||||
|
order.getId());
|
||||||
|
|
||||||
|
OrderRefundRecordType refundRecordType = finalAmount.compareTo(refundAmount) == 0
|
||||||
|
? OrderRefundRecordType.FULL
|
||||||
|
: OrderRefundRecordType.PARTIAL;
|
||||||
|
String refundByType = StrUtil.isNotBlank(context.getOperatorType())
|
||||||
|
? context.getOperatorType()
|
||||||
|
: OperatorType.ADMIN.getCode();
|
||||||
|
String refundById = StrUtil.isNotBlank(context.getOperatorId()) ? context.getOperatorId() : SecurityUtils.getUserId();
|
||||||
|
|
||||||
|
orderRefundInfoService.add(
|
||||||
|
order.getId(),
|
||||||
|
order.getPurchaserBy(),
|
||||||
|
order.getAcceptBy(),
|
||||||
|
order.getPayMethod(),
|
||||||
|
refundRecordType.getCode(),
|
||||||
|
refundAmount,
|
||||||
|
context.getRefundReason(),
|
||||||
|
refundByType,
|
||||||
|
refundById,
|
||||||
|
OrderRefundState.PROCESSING.getCode(),
|
||||||
|
ReviewRequirement.NOT_REQUIRED.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean ensureEarnings(PlayOrderInfoEntity order, OrderTriggerSource source) {
|
||||||
|
Long existing = earningsService.lambdaQuery()
|
||||||
|
.eq(EarningsLineEntity::getTenantId, order.getTenantId())
|
||||||
|
.eq(EarningsLineEntity::getOrderId, order.getId())
|
||||||
|
.count();
|
||||||
|
if (existing != null && existing > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
earningsService.createFromOrder(order);
|
||||||
|
Long after = earningsService.lambdaQuery()
|
||||||
|
.eq(EarningsLineEntity::getTenantId, order.getTenantId())
|
||||||
|
.eq(EarningsLineEntity::getOrderId, order.getId())
|
||||||
|
.count();
|
||||||
|
if (after == null || after == 0) {
|
||||||
|
log.warn("Failed to create earnings line for order {}, source={}", order.getId(), source.getCode());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
package com.starry.admin.modules.order.service.impl;
|
package com.starry.admin.modules.order.service.impl;
|
||||||
|
|
||||||
import static com.starry.admin.modules.order.module.constant.OrderConstant.ORDER_STATUS_2;
|
|
||||||
import static com.starry.admin.modules.order.module.constant.OrderConstant.ORDER_STATUS_3;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
@@ -20,12 +17,21 @@ import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
|||||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||||
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.BalanceOperationType;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundFlag;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundRecordType;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundState;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
|
||||||
import com.starry.admin.modules.order.module.dto.*;
|
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.PlayOrderComplaintInfoEntity;
|
||||||
import com.starry.admin.modules.order.module.entity.PlayOrderEvaluateInfoEntity;
|
import com.starry.admin.modules.order.module.entity.PlayOrderEvaluateInfoEntity;
|
||||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||||
import com.starry.admin.modules.order.module.entity.PlayOrderRefundInfoEntity;
|
import com.starry.admin.modules.order.module.entity.PlayOrderRefundInfoEntity;
|
||||||
import com.starry.admin.modules.order.module.vo.*;
|
import com.starry.admin.modules.order.module.vo.*;
|
||||||
|
import com.starry.admin.modules.order.service.IOrderLifecycleService;
|
||||||
import com.starry.admin.modules.order.service.IPlayOrderComplaintInfoService;
|
import com.starry.admin.modules.order.service.IPlayOrderComplaintInfoService;
|
||||||
import com.starry.admin.modules.order.service.IPlayOrderEvaluateInfoService;
|
import com.starry.admin.modules.order.service.IPlayOrderEvaluateInfoService;
|
||||||
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||||
@@ -93,7 +99,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
private IPlayClerkLevelInfoService playClerkLevelInfoService;
|
private IPlayClerkLevelInfoService playClerkLevelInfoService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private IEarningsService earningsService;
|
private IOrderLifecycleService orderLifecycleService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PlayOrderInfoEntity> getTotalOrderInfo(String tenantId) {
|
public List<PlayOrderInfoEntity> getTotalOrderInfo(String tenantId) {
|
||||||
@@ -117,7 +123,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
entity.setPlaceType(placeType);
|
entity.setPlaceType(placeType);
|
||||||
entity.setRewardType(rewardType);
|
entity.setRewardType(rewardType);
|
||||||
entity.setFirstOrder(firstOrder);
|
entity.setFirstOrder(firstOrder);
|
||||||
entity.setRefundType("0");
|
entity.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||||
entity.setCommodityId(commodityId);
|
entity.setCommodityId(commodityId);
|
||||||
entity.setCommodityType(commodityType);
|
entity.setCommodityType(commodityType);
|
||||||
entity.setCommodityPrice(commodityPrice);
|
entity.setCommodityPrice(commodityPrice);
|
||||||
@@ -162,9 +168,11 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
// 修改优惠券状态
|
// 修改优惠券状态
|
||||||
playCouponDetailsService.updateCouponUseStateByIds(couponIds, "2");
|
playCouponDetailsService.updateCouponUseStateByIds(couponIds, "2");
|
||||||
if ("2".equals(placeType) && StrUtil.isNotBlank(acceptBy)) {
|
if ("2".equals(placeType) && StrUtil.isNotBlank(acceptBy)) {
|
||||||
PlayOrderInfoEntity latest = this.selectOrderInfoById(orderId);
|
orderLifecycleService.completeOrder(
|
||||||
earningsService.createFromOrder(latest);
|
orderId,
|
||||||
wxCustomMpService.sendOrderFinishMessageAsync(latest);
|
OrderCompletionContext.of(null, null, OrderTriggerSource.REWARD_ORDER)
|
||||||
|
.withForceNotify(true)
|
||||||
|
.withComment("auto-complete reward order"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,9 +212,11 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
|
|
||||||
// 打赏单立即入账
|
// 打赏单立即入账
|
||||||
if (request.isRewardOrder() && StrUtil.isNotBlank(request.getAcceptBy())) {
|
if (request.isRewardOrder() && StrUtil.isNotBlank(request.getAcceptBy())) {
|
||||||
PlayOrderInfoEntity latest = this.selectOrderInfoById(entity.getId());
|
orderLifecycleService.completeOrder(
|
||||||
earningsService.createFromOrder(latest);
|
entity.getId(),
|
||||||
wxCustomMpService.sendOrderFinishMessageAsync(latest);
|
OrderCompletionContext.of(null, null, OrderTriggerSource.REWARD_ORDER)
|
||||||
|
.withForceNotify(true)
|
||||||
|
.withComment("auto-complete reward order"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +246,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
entity.setFirstOrder(request.getFirstOrderString());
|
entity.setFirstOrder(request.getFirstOrderString());
|
||||||
|
|
||||||
// 固定默认值
|
// 固定默认值
|
||||||
entity.setRefundType("0");
|
entity.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||||
entity.setBackendEntry("0");
|
entity.setBackendEntry("0");
|
||||||
entity.setPayMethod("0");
|
entity.setPayMethod("0");
|
||||||
entity.setOrderSettlementState("0");
|
entity.setOrderSettlementState("0");
|
||||||
@@ -500,7 +510,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createRechargeOrder(String orderId, BigDecimal orderMoney, BigDecimal finalAmount, String purchaserBy) {
|
public void createRechargeOrder(String orderId, BigDecimal orderMoney, BigDecimal finalAmount, String purchaserBy) {
|
||||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity(IdUtils.getUuid(), ORDER_STATUS_3, "0", "-1");
|
PlayOrderInfoEntity entity = new PlayOrderInfoEntity(IdUtils.getUuid(), OrderStatus.COMPLETED.getCode(), "0", "-1");
|
||||||
entity.setOrderNo(orderId);
|
entity.setOrderNo(orderId);
|
||||||
entity.setId(IdUtils.getUuid());
|
entity.setId(IdUtils.getUuid());
|
||||||
entity.setOrderMoney(orderMoney);
|
entity.setOrderMoney(orderMoney);
|
||||||
@@ -538,7 +548,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
public List<PlayOrderInfoEntity> listByEndTime(String clerkId, LocalDateTime endTime) {
|
public List<PlayOrderInfoEntity> listByEndTime(String clerkId, LocalDateTime endTime) {
|
||||||
MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
|
MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
|
||||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, clerkId);
|
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, clerkId);
|
||||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, "3");
|
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.COMPLETED.getCode());
|
||||||
lambdaQueryWrapper.lt(PlayOrderInfoEntity::getOrderEndTime, endTime);
|
lambdaQueryWrapper.lt(PlayOrderInfoEntity::getOrderEndTime, endTime);
|
||||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderSettlementState, "0");
|
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderSettlementState, "0");
|
||||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||||
@@ -604,7 +614,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
PlayClerkUserInfoEntity entity = playClerkUserInfoService.getById(clerkId);
|
PlayClerkUserInfoEntity entity = playClerkUserInfoService.getById(clerkId);
|
||||||
LambdaQueryWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, "1");
|
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, "1");
|
||||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, "0");
|
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.PENDING.getCode());
|
||||||
// lambdaQueryWrapper.eq(PlayOrderInfoEntity::getLevelId, entity.getLevelId());
|
// lambdaQueryWrapper.eq(PlayOrderInfoEntity::getLevelId, entity.getLevelId());
|
||||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getSex, entity.getSex());
|
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getSex, entity.getSex());
|
||||||
// lambdaQueryWrapper.eq(PlayOrderInfoEntity::getExcludeHistory, "0")
|
// lambdaQueryWrapper.eq(PlayOrderInfoEntity::getExcludeHistory, "0")
|
||||||
@@ -617,7 +627,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
public IPage<PlayRewardInfoReturnVo> selectRewardByPage(PlayRewardOrderQueryVo vo) {
|
public IPage<PlayRewardInfoReturnVo> selectRewardByPage(PlayRewardOrderQueryVo vo) {
|
||||||
MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
|
MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
|
||||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, "2");
|
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, "2");
|
||||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getRefundType, "0");
|
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getRefundType, OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||||
lambdaQueryWrapper.selectAll(PlayOrderInfoEntity.class);
|
lambdaQueryWrapper.selectAll(PlayOrderInfoEntity.class);
|
||||||
lambdaQueryWrapper.orderByDesc(PlayOrderInfoEntity::getPurchaserTime);
|
lambdaQueryWrapper.orderByDesc(PlayOrderInfoEntity::getPurchaserTime);
|
||||||
// 查询陪聊表
|
// 查询陪聊表
|
||||||
@@ -636,7 +646,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
@Override
|
@Override
|
||||||
public Boolean checkFirstOrderFlag(String customId, String clerkId) {
|
public Boolean checkFirstOrderFlag(String customId, String clerkId) {
|
||||||
// 检查是否是首单
|
// 检查是否是首单
|
||||||
LambdaQueryWrapper<PlayOrderInfoEntity> wrapper = Wrappers.lambdaQuery(PlayOrderInfoEntity.class).eq(PlayOrderInfoEntity::getPurchaserBy, customId).eq(PlayOrderInfoEntity::getAcceptBy, clerkId).eq(PlayOrderInfoEntity::getOrderStatus, "3");
|
LambdaQueryWrapper<PlayOrderInfoEntity> wrapper = Wrappers.lambdaQuery(PlayOrderInfoEntity.class).eq(PlayOrderInfoEntity::getPurchaserBy, customId).eq(PlayOrderInfoEntity::getAcceptBy, clerkId).eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.COMPLETED.getCode());
|
||||||
return this.baseMapper.selectCount(wrapper) > 0;
|
return this.baseMapper.selectCount(wrapper) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,7 +687,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
PlayOrderDetailsReturnVo vo = this.baseMapper.selectJoinOne(PlayOrderDetailsReturnVo.class, lambdaQueryWrapper);
|
PlayOrderDetailsReturnVo vo = this.baseMapper.selectJoinOne(PlayOrderDetailsReturnVo.class, lambdaQueryWrapper);
|
||||||
|
|
||||||
// Privacy protection: Hide customer info for pending random orders
|
// Privacy protection: Hide customer info for pending random orders
|
||||||
if (vo != null && OrderConstant.PlaceType.RANDOM.getCode().equals(vo.getPlaceType()) && OrderConstant.OrderStatus.PENDING.getCode().equals(vo.getOrderStatus())) {
|
if (vo != null && OrderConstant.PlaceType.RANDOM.getCode().equals(vo.getPlaceType()) && OrderStatus.PENDING.getCode().equals(vo.getOrderStatus())) {
|
||||||
vo.setCustomNickname("匿名用户");
|
vo.setCustomNickname("匿名用户");
|
||||||
vo.setCustomAvatar("");
|
vo.setCustomAvatar("");
|
||||||
vo.setCustomId("");
|
vo.setCustomId("");
|
||||||
@@ -738,7 +748,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
PlayClerkOrderDetailsReturnVo returnVo = this.baseMapper.selectJoinOne(PlayClerkOrderDetailsReturnVo.class,
|
PlayClerkOrderDetailsReturnVo returnVo = this.baseMapper.selectJoinOne(PlayClerkOrderDetailsReturnVo.class,
|
||||||
lambdaQueryWrapper);
|
lambdaQueryWrapper);
|
||||||
// 如果订单状态为退款,查询订单退款原因
|
// 如果订单状态为退款,查询订单退款原因
|
||||||
if (returnVo.getOrderStatus().equals(OrderConstant.ORDER_STATUS_4)) {
|
if (returnVo.getOrderStatus().equals(OrderStatus.CANCELLED.getCode())) {
|
||||||
PlayOrderRefundInfoEntity orderRefundInfoEntity = playOrderRefundInfoService
|
PlayOrderRefundInfoEntity orderRefundInfoEntity = playOrderRefundInfoService
|
||||||
.selectPlayOrderRefundInfoByOrderId(returnVo.getId());
|
.selectPlayOrderRefundInfoByOrderId(returnVo.getId());
|
||||||
returnVo.setRefundByType(orderRefundInfoEntity.getRefundByType());
|
returnVo.setRefundByType(orderRefundInfoEntity.getRefundByType());
|
||||||
@@ -781,7 +791,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
PlayCustomOrderDetailsReturnVo returnVo = this.baseMapper.selectJoinOne(PlayCustomOrderDetailsReturnVo.class,
|
PlayCustomOrderDetailsReturnVo returnVo = this.baseMapper.selectJoinOne(PlayCustomOrderDetailsReturnVo.class,
|
||||||
lambdaQueryWrapper);
|
lambdaQueryWrapper);
|
||||||
// 如果订单状态为退款,查询订单退款原因
|
// 如果订单状态为退款,查询订单退款原因
|
||||||
if (returnVo.getOrderStatus().equals(OrderConstant.ORDER_STATUS_4)) {
|
if (returnVo.getOrderStatus().equals(OrderStatus.CANCELLED.getCode())) {
|
||||||
PlayOrderRefundInfoEntity orderRefundInfoEntity = playOrderRefundInfoService
|
PlayOrderRefundInfoEntity orderRefundInfoEntity = playOrderRefundInfoService
|
||||||
.selectPlayOrderRefundInfoByOrderId(returnVo.getId());
|
.selectPlayOrderRefundInfoByOrderId(returnVo.getId());
|
||||||
returnVo.setRefundByType(orderRefundInfoEntity.getRefundByType());
|
returnVo.setRefundByType(orderRefundInfoEntity.getRefundByType());
|
||||||
@@ -872,7 +882,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
// throw new CustomException("只有未接单的店员才可接单");
|
// throw new CustomException("只有未接单的店员才可接单");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity(orderId, OrderConstant.ORDER_STATUS_1);
|
PlayOrderInfoEntity entity = new PlayOrderInfoEntity(orderId, OrderStatus.ACCEPTED.getCode());
|
||||||
entity.setAcceptBy(acceptBy);
|
entity.setAcceptBy(acceptBy);
|
||||||
entity.setAcceptTime(LocalDateTime.now());
|
entity.setAcceptTime(LocalDateTime.now());
|
||||||
ClerkEstimatedRevenueVo estimatedRevenueVo = this.getClerkEstimatedRevenue(acceptBy, orderInfo.getCouponIds(),
|
ClerkEstimatedRevenueVo estimatedRevenueVo = this.getClerkEstimatedRevenue(acceptBy, orderInfo.getCouponIds(),
|
||||||
@@ -944,11 +954,11 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
@Override
|
@Override
|
||||||
public void updateStateTo23(String operatorByType, String operatorBy, String orderState, String orderId) {
|
public void updateStateTo23(String operatorByType, String operatorBy, String orderState, String orderId) {
|
||||||
// 开始订单只能店员或者管理员操作
|
// 开始订单只能店员或者管理员操作
|
||||||
if (orderState.equals(ORDER_STATUS_2) && "0".equals(operatorByType)) {
|
if (OrderStatus.IN_PROGRESS.getCode().equals(orderState) && "0".equals(operatorByType)) {
|
||||||
throw new CustomException("禁止操作");
|
throw new CustomException("禁止操作");
|
||||||
}
|
}
|
||||||
// 完成订单只能顾客或者管理员操作
|
// 完成订单只能顾客或者管理员操作
|
||||||
if (orderState.equals(ORDER_STATUS_3) && "1".equals(operatorByType)) {
|
if (OrderStatus.COMPLETED.getCode().equals(orderState) && "1".equals(operatorByType)) {
|
||||||
throw new CustomException("禁止操作");
|
throw new CustomException("禁止操作");
|
||||||
}
|
}
|
||||||
PlayOrderInfoEntity entity = this.selectOrderInfoById(orderId);
|
PlayOrderInfoEntity entity = this.selectOrderInfoById(orderId);
|
||||||
@@ -960,36 +970,28 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
if ("1".equals(operatorByType) && !entity.getAcceptBy().equals(operatorBy)) {
|
if ("1".equals(operatorByType) && !entity.getAcceptBy().equals(operatorBy)) {
|
||||||
throw new CustomException("只能操作本人订单");
|
throw new CustomException("只能操作本人订单");
|
||||||
}
|
}
|
||||||
switch (orderState) {
|
if (OrderStatus.IN_PROGRESS.getCode().equals(orderState)) {
|
||||||
case ORDER_STATUS_2: {
|
if (!OrderStatus.ACCEPTED.getCode().equals(entity.getOrderStatus())) {
|
||||||
// 开始订单前,订单状态必须为接单状态
|
log.error("订单状态异常,不能开始接单,orderId={},orderStace={}", orderId, orderState);
|
||||||
if (!entity.getOrderStatus().equals(OrderConstant.ORDER_STATUS_1)) {
|
throw new CustomException("订单状态异常,不能开始订单");
|
||||||
log.error("订单状态异常,不能开始接单,orderId={},orderStace={}", orderId, orderState);
|
|
||||||
throw new CustomException("订单状态异常,不能开始订单");
|
|
||||||
}
|
|
||||||
PlayOrderInfoEntity entity2 = new PlayOrderInfoEntity(orderId, ORDER_STATUS_2);
|
|
||||||
entity2.setOrderStartTime(LocalDateTime.now());
|
|
||||||
this.baseMapper.updateById(entity2);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case ORDER_STATUS_3: {
|
PlayOrderInfoEntity entity2 = new PlayOrderInfoEntity(orderId, OrderStatus.IN_PROGRESS.getCode());
|
||||||
// 完成订单前,订单状态必须为开始状态
|
entity2.setOrderStartTime(LocalDateTime.now());
|
||||||
if (!entity.getOrderStatus().equals(ORDER_STATUS_2)) {
|
this.baseMapper.updateById(entity2);
|
||||||
log.error("订单状态异常,不能完成订单,orderId={},orderStace={}", orderId, orderState);
|
} else if (OrderStatus.COMPLETED.getCode().equals(orderState)) {
|
||||||
throw new CustomException("订单状态异常,不能开始订单");
|
if (!OrderStatus.IN_PROGRESS.getCode().equals(entity.getOrderStatus())) {
|
||||||
}
|
log.error("订单状态异常,不能完成订单,orderId={},orderStace={}", orderId, orderState);
|
||||||
// 完成订单
|
throw new CustomException("订单状态异常,不能开始订单");
|
||||||
PlayOrderInfoEntity entity2 = new PlayOrderInfoEntity(orderId, ORDER_STATUS_3);
|
|
||||||
entity2.setOrderEndTime(LocalDateTime.now());
|
|
||||||
this.baseMapper.updateById(entity2);
|
|
||||||
PlayOrderInfoEntity latest = this.selectOrderInfoById(orderId);
|
|
||||||
wxCustomMpService.sendOrderFinishMessageAsync(latest);
|
|
||||||
earningsService.createFromOrder(latest);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
log.error("修改订单状态异常,orderId={},orderStace={}", orderId, orderState);
|
|
||||||
}
|
}
|
||||||
|
orderLifecycleService.completeOrder(
|
||||||
|
orderId,
|
||||||
|
OrderCompletionContext.of(
|
||||||
|
operatorByType,
|
||||||
|
operatorBy,
|
||||||
|
resolveCompletionSource(operatorByType),
|
||||||
|
"manual"));
|
||||||
|
} else {
|
||||||
|
log.error("修改订单状态异常,orderId={},orderStace={}", orderId, orderState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1013,21 +1015,21 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
throw new CustomException("只能操作本人订单");
|
throw new CustomException("只能操作本人订单");
|
||||||
}
|
}
|
||||||
// 取消订单(必须订单未接单或者为开始状态)
|
// 取消订单(必须订单未接单或者为开始状态)
|
||||||
if (!orderInfo.getOrderStatus().equals(OrderConstant.ORDER_STATUS_0)
|
if (!orderInfo.getOrderStatus().equals(OrderStatus.PENDING.getCode())
|
||||||
&& !orderInfo.getOrderStatus().equals(OrderConstant.ORDER_STATUS_1)) {
|
&& !orderInfo.getOrderStatus().equals(OrderStatus.ACCEPTED.getCode())) {
|
||||||
throw new CustomException("订单状态异常,无法取消");
|
throw new CustomException("订单状态异常,无法取消");
|
||||||
}
|
}
|
||||||
// 修改订单状态
|
// 修改订单状态
|
||||||
this.baseMapper.updateById(new PlayOrderInfoEntity(orderId, OrderConstant.ORDER_STATUS_4));
|
this.baseMapper.updateById(new PlayOrderInfoEntity(orderId, OrderStatus.CANCELLED.getCode()));
|
||||||
// 用户增加余额
|
// 用户增加余额
|
||||||
PlayCustomUserInfoEntity customUserInfo = customUserInfoService.getById(orderInfo.getPurchaserBy());
|
PlayCustomUserInfoEntity customUserInfo = customUserInfoService.getById(orderInfo.getPurchaserBy());
|
||||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(),
|
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(),
|
||||||
customUserInfo.getAccountBalance().add(orderInfo.getOrderMoney()), "3", "订单退款",
|
customUserInfo.getAccountBalance().add(orderInfo.getOrderMoney()), BalanceOperationType.REFUND.getCode(), "订单退款",
|
||||||
orderInfo.getOrderMoney(), BigDecimal.ZERO, orderId);
|
orderInfo.getOrderMoney(), BigDecimal.ZERO, orderId);
|
||||||
// 取消订单后,记录退款信息
|
// 取消订单后,记录退款信息
|
||||||
playOrderRefundInfoService.add(orderId, orderInfo.getPurchaserBy(), orderInfo.getAcceptBy(),
|
playOrderRefundInfoService.add(orderInfo.getId(), orderInfo.getPurchaserBy(), orderInfo.getAcceptBy(),
|
||||||
orderInfo.getPayMethod(), "0", orderInfo.getFinalAmount(), refundReason, operatorByType, operatorBy,
|
orderInfo.getPayMethod(), OrderRefundRecordType.FULL.getCode(), orderInfo.getFinalAmount(), refundReason, operatorByType, operatorBy,
|
||||||
"0", "0");
|
OrderRefundState.PROCESSING.getCode(), ReviewRequirement.NOT_REQUIRED.getCode());
|
||||||
wxCustomMpService.sendOrderCancelMessageAsync(orderInfo, refundReason);
|
wxCustomMpService.sendOrderCancelMessageAsync(orderInfo, refundReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1042,8 +1044,8 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
throw new CustomException("禁止操作");
|
throw new CustomException("禁止操作");
|
||||||
}
|
}
|
||||||
PlayOrderInfoEntity orderInfo = this.selectOrderInfoById(orderId);
|
PlayOrderInfoEntity orderInfo = this.selectOrderInfoById(orderId);
|
||||||
if (!OrderConstant.ORDER_STATUS_1.equals(orderInfo.getOrderStatus())
|
if (!OrderStatus.ACCEPTED.getCode().equals(orderInfo.getOrderStatus())
|
||||||
&& !OrderConstant.ORDER_STATUS_2.equals(orderInfo.getOrderStatus())) {
|
&& !OrderStatus.IN_PROGRESS.getCode().equals(orderInfo.getOrderStatus())) {
|
||||||
throw new CustomException("订单状态异常,无法取消");
|
throw new CustomException("订单状态异常,无法取消");
|
||||||
}
|
}
|
||||||
BigDecimal actualRefundAmount = refundAmount != null ? refundAmount : orderInfo.getFinalAmount();
|
BigDecimal actualRefundAmount = refundAmount != null ? refundAmount : orderInfo.getFinalAmount();
|
||||||
@@ -1055,12 +1057,12 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
}
|
}
|
||||||
|
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
PlayOrderInfoEntity updateEntity = new PlayOrderInfoEntity(orderId, OrderConstant.ORDER_STATUS_4);
|
PlayOrderInfoEntity updateEntity = new PlayOrderInfoEntity(orderId, OrderStatus.CANCELLED.getCode());
|
||||||
updateEntity.setRefundAmount(actualRefundAmount);
|
updateEntity.setRefundAmount(actualRefundAmount);
|
||||||
updateEntity.setRefundReason(refundReason);
|
updateEntity.setRefundReason(refundReason);
|
||||||
updateEntity.setRefundType("1");
|
updateEntity.setRefundType(OrderRefundFlag.REFUNDED.getCode());
|
||||||
updateEntity.setOrderCancelTime(now);
|
updateEntity.setOrderCancelTime(now);
|
||||||
if (OrderConstant.ORDER_STATUS_2.equals(orderInfo.getOrderStatus())) {
|
if (OrderStatus.IN_PROGRESS.getCode().equals(orderInfo.getOrderStatus())) {
|
||||||
updateEntity.setOrderEndTime(now);
|
updateEntity.setOrderEndTime(now);
|
||||||
}
|
}
|
||||||
this.baseMapper.updateById(updateEntity);
|
this.baseMapper.updateById(updateEntity);
|
||||||
@@ -1071,11 +1073,15 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
}
|
}
|
||||||
|
|
||||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(),
|
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(),
|
||||||
customUserInfo.getAccountBalance().add(actualRefundAmount), "3", "订单取消退款",
|
customUserInfo.getAccountBalance().add(actualRefundAmount), BalanceOperationType.REFUND.getCode(), "订单取消退款",
|
||||||
actualRefundAmount, BigDecimal.ZERO, orderId);
|
actualRefundAmount, BigDecimal.ZERO, orderId);
|
||||||
|
|
||||||
|
OrderRefundRecordType forceCancelRefundType = actualRefundAmount.compareTo(orderInfo.getFinalAmount()) == 0
|
||||||
|
? OrderRefundRecordType.FULL
|
||||||
|
: OrderRefundRecordType.PARTIAL;
|
||||||
playOrderRefundInfoService.add(orderId, orderInfo.getPurchaserBy(), orderInfo.getAcceptBy(),
|
playOrderRefundInfoService.add(orderId, orderInfo.getPurchaserBy(), orderInfo.getAcceptBy(),
|
||||||
orderInfo.getPayMethod(), "0", actualRefundAmount, refundReason, operatorByType, operatorBy, "0", "0");
|
orderInfo.getPayMethod(), forceCancelRefundType.getCode(), actualRefundAmount, refundReason, operatorByType, operatorBy,
|
||||||
|
OrderRefundState.PROCESSING.getCode(), ReviewRequirement.NOT_REQUIRED.getCode());
|
||||||
|
|
||||||
PlayOrderInfoEntity latest = this.selectOrderInfoById(orderId);
|
PlayOrderInfoEntity latest = this.selectOrderInfoById(orderId);
|
||||||
wxCustomMpService.sendOrderCancelMessageAsync(latest, refundReason);
|
wxCustomMpService.sendOrderCancelMessageAsync(latest, refundReason);
|
||||||
@@ -1121,4 +1127,25 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
|||||||
return orderInfoMapper.deleteById(id);
|
return orderInfoMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private OrderTriggerSource resolveCompletionSource(String operatorType) {
|
||||||
|
if (operatorType == null) {
|
||||||
|
return OrderTriggerSource.UNKNOWN;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
OperatorType type = OperatorType.fromCode(operatorType);
|
||||||
|
switch (type) {
|
||||||
|
case CUSTOMER:
|
||||||
|
return OrderTriggerSource.WX_CUSTOMER;
|
||||||
|
case CLERK:
|
||||||
|
return OrderTriggerSource.WX_CLERK;
|
||||||
|
case ADMIN:
|
||||||
|
return OrderTriggerSource.ADMIN_CONSOLE;
|
||||||
|
default:
|
||||||
|
return OrderTriggerSource.UNKNOWN;
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
log.warn("Unknown operator type {}, fallback to unknown", operatorType, ex);
|
||||||
|
return OrderTriggerSource.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
|||||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||||
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
|
||||||
import com.starry.admin.modules.order.module.dto.CommodityInfo;
|
import com.starry.admin.modules.order.module.dto.CommodityInfo;
|
||||||
import com.starry.admin.modules.order.module.dto.OrderCreationRequest;
|
import com.starry.admin.modules.order.module.dto.OrderCreationRequest;
|
||||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||||
@@ -450,7 +451,7 @@ class PlayOrderInfoServiceTest {
|
|||||||
String orderId = "order_force_cancel";
|
String orderId = "order_force_cancel";
|
||||||
PlayOrderInfoEntity inProgressOrder = new PlayOrderInfoEntity();
|
PlayOrderInfoEntity inProgressOrder = new PlayOrderInfoEntity();
|
||||||
inProgressOrder.setId(orderId);
|
inProgressOrder.setId(orderId);
|
||||||
inProgressOrder.setOrderStatus(OrderConstant.ORDER_STATUS_2);
|
inProgressOrder.setOrderStatus(OrderStatus.IN_PROGRESS.getCode());
|
||||||
inProgressOrder.setAcceptBy("clerk-1");
|
inProgressOrder.setAcceptBy("clerk-1");
|
||||||
inProgressOrder.setPurchaserBy("customer-1");
|
inProgressOrder.setPurchaserBy("customer-1");
|
||||||
inProgressOrder.setFinalAmount(BigDecimal.valueOf(100));
|
inProgressOrder.setFinalAmount(BigDecimal.valueOf(100));
|
||||||
@@ -458,7 +459,7 @@ class PlayOrderInfoServiceTest {
|
|||||||
|
|
||||||
PlayOrderInfoEntity cancelledOrder = new PlayOrderInfoEntity();
|
PlayOrderInfoEntity cancelledOrder = new PlayOrderInfoEntity();
|
||||||
cancelledOrder.setId(orderId);
|
cancelledOrder.setId(orderId);
|
||||||
cancelledOrder.setOrderStatus(OrderConstant.ORDER_STATUS_4);
|
cancelledOrder.setOrderStatus(OrderStatus.CANCELLED.getCode());
|
||||||
|
|
||||||
PlayCustomUserInfoEntity customUserInfo = new PlayCustomUserInfoEntity();
|
PlayCustomUserInfoEntity customUserInfo = new PlayCustomUserInfoEntity();
|
||||||
customUserInfo.setId("customer-1");
|
customUserInfo.setId("customer-1");
|
||||||
@@ -472,18 +473,22 @@ class PlayOrderInfoServiceTest {
|
|||||||
any(BigDecimal.class), anyString(), anyString(), any(BigDecimal.class), any(BigDecimal.class), eq(orderId));
|
any(BigDecimal.class), anyString(), anyString(), any(BigDecimal.class), any(BigDecimal.class), eq(orderId));
|
||||||
doNothing().when(playOrderRefundInfoService).add(eq(orderId), eq("customer-1"), eq("clerk-1"), anyString(),
|
doNothing().when(playOrderRefundInfoService).add(eq(orderId), eq("customer-1"), eq("clerk-1"), anyString(),
|
||||||
anyString(), any(BigDecimal.class), anyString(), anyString(), anyString(), anyString(), anyString());
|
anyString(), any(BigDecimal.class), anyString(), anyString(), anyString(), anyString(), anyString());
|
||||||
doNothing().when(wxCustomMpService).sendOrderCancelMessage(any(PlayOrderInfoEntity.class), anyString());
|
doNothing().when(wxCustomMpService).sendOrderCancelMessageAsync(any(PlayOrderInfoEntity.class), anyString());
|
||||||
|
|
||||||
assertDoesNotThrow(() -> orderService.forceCancelOngoingOrder("2", "admin-1", orderId,
|
assertDoesNotThrow(() -> orderService.forceCancelOngoingOrder("2", "admin-1", orderId,
|
||||||
BigDecimal.valueOf(80), "管理员取消测试", Collections.emptyList()));
|
BigDecimal.valueOf(80), "管理员取消测试", Collections.emptyList()));
|
||||||
|
|
||||||
verify(orderInfoMapper, times(1)).updateById(any(PlayOrderInfoEntity.class));
|
verify(orderInfoMapper, times(1)).updateById(any(PlayOrderInfoEntity.class));
|
||||||
verify(customUserInfoService, times(1)).updateAccountBalanceById(eq("customer-1"), any(BigDecimal.class),
|
verify(customUserInfoService, times(1)).updateAccountBalanceById(eq("customer-1"), any(BigDecimal.class),
|
||||||
any(BigDecimal.class), anyString(), anyString(), eq(BigDecimal.valueOf(80)), eq(BigDecimal.ZERO),
|
any(BigDecimal.class), eq(OrderConstant.BalanceOperationType.REFUND.getCode()), eq("订单取消退款"),
|
||||||
eq(orderId));
|
eq(BigDecimal.valueOf(80)), eq(BigDecimal.ZERO), eq(orderId));
|
||||||
verify(playOrderRefundInfoService, times(1)).add(eq(orderId), eq("customer-1"), eq("clerk-1"), anyString(),
|
verify(playOrderRefundInfoService, times(1)).add(eq(orderId), eq("customer-1"), eq("clerk-1"),
|
||||||
eq("0"), eq(BigDecimal.valueOf(80)), eq("管理员取消测试"), eq("2"), eq("admin-1"), eq("0"), eq("0"));
|
eq(inProgressOrder.getPayMethod()),
|
||||||
verify(wxCustomMpService, times(1)).sendOrderCancelMessage(any(PlayOrderInfoEntity.class),
|
eq(OrderConstant.OrderRefundRecordType.PARTIAL.getCode()),
|
||||||
|
eq(BigDecimal.valueOf(80)), eq("管理员取消测试"), eq("2"), eq("admin-1"),
|
||||||
|
eq(OrderConstant.OrderRefundState.PROCESSING.getCode()),
|
||||||
|
eq(OrderConstant.ReviewRequirement.NOT_REQUIRED.getCode()));
|
||||||
|
verify(wxCustomMpService, times(1)).sendOrderCancelMessageAsync(any(PlayOrderInfoEntity.class),
|
||||||
eq("管理员取消测试"));
|
eq("管理员取消测试"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,7 +498,7 @@ class PlayOrderInfoServiceTest {
|
|||||||
String orderId = "order_invalid_force_cancel";
|
String orderId = "order_invalid_force_cancel";
|
||||||
PlayOrderInfoEntity pendingOrder = new PlayOrderInfoEntity();
|
PlayOrderInfoEntity pendingOrder = new PlayOrderInfoEntity();
|
||||||
pendingOrder.setId(orderId);
|
pendingOrder.setId(orderId);
|
||||||
pendingOrder.setOrderStatus(OrderConstant.ORDER_STATUS_0);
|
pendingOrder.setOrderStatus(OrderStatus.PENDING.getCode());
|
||||||
pendingOrder.setAcceptBy("clerk-1");
|
pendingOrder.setAcceptBy("clerk-1");
|
||||||
pendingOrder.setPurchaserBy("customer-1");
|
pendingOrder.setPurchaserBy("customer-1");
|
||||||
pendingOrder.setFinalAmount(BigDecimal.valueOf(50));
|
pendingOrder.setFinalAmount(BigDecimal.valueOf(50));
|
||||||
|
|||||||
@@ -0,0 +1,216 @@
|
|||||||
|
package com.starry.admin.modules.order.service.impl;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
||||||
|
import com.starry.admin.common.exception.CustomException;
|
||||||
|
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.BalanceOperationType;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundFlag;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundRecordType;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundState;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
|
||||||
|
import com.starry.admin.modules.order.module.dto.OrderCompletionContext;
|
||||||
|
import com.starry.admin.modules.order.module.dto.OrderRefundContext;
|
||||||
|
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||||
|
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
|
||||||
|
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
||||||
|
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
|
||||||
|
import com.starry.admin.modules.withdraw.service.IEarningsService;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class OrderLifecycleServiceImplTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private OrderLifecycleServiceImpl lifecycleService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PlayOrderInfoMapper orderInfoMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IEarningsService earningsService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private WxCustomMpService wxCustomMpService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IPlayOrderRefundInfoService orderRefundInfoService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IPlayCustomUserInfoService customUserInfoService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void completeOrder_inProgress_createsEarningsAndNotifies() {
|
||||||
|
String orderId = UUID.randomUUID().toString();
|
||||||
|
PlayOrderInfoEntity inProgress = buildOrder(orderId, OrderStatus.IN_PROGRESS.getCode());
|
||||||
|
inProgress.setOrderEndTime(null);
|
||||||
|
|
||||||
|
PlayOrderInfoEntity completed = buildOrder(orderId, OrderStatus.COMPLETED.getCode());
|
||||||
|
completed.setOrderEndTime(LocalDateTime.now());
|
||||||
|
|
||||||
|
when(orderInfoMapper.selectById(orderId)).thenReturn(inProgress, completed);
|
||||||
|
when(orderInfoMapper.updateById(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||||
|
mockEarningsCounts(0L, 1L);
|
||||||
|
|
||||||
|
OrderCompletionContext context = OrderCompletionContext.of(
|
||||||
|
OperatorType.CLERK.getCode(),
|
||||||
|
inProgress.getAcceptBy(),
|
||||||
|
OrderTriggerSource.WX_CLERK);
|
||||||
|
|
||||||
|
lifecycleService.completeOrder(orderId, context);
|
||||||
|
|
||||||
|
verify(orderInfoMapper).updateById(argThat(entity ->
|
||||||
|
orderId.equals(entity.getId())
|
||||||
|
&& OrderStatus.COMPLETED.getCode().equals(entity.getOrderStatus())
|
||||||
|
&& entity.getOrderEndTime() != null));
|
||||||
|
verify(earningsService).createFromOrder(completed);
|
||||||
|
verify(wxCustomMpService).sendOrderFinishMessageAsync(completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void completeOrder_alreadyCompleted_skipsEarningsCreation() {
|
||||||
|
String orderId = UUID.randomUUID().toString();
|
||||||
|
PlayOrderInfoEntity alreadyCompleted = buildOrder(orderId, OrderStatus.COMPLETED.getCode());
|
||||||
|
alreadyCompleted.setOrderEndTime(null);
|
||||||
|
|
||||||
|
PlayOrderInfoEntity completedWithEnd = buildOrder(orderId, OrderStatus.COMPLETED.getCode());
|
||||||
|
completedWithEnd.setOrderEndTime(LocalDateTime.now());
|
||||||
|
|
||||||
|
when(orderInfoMapper.selectById(orderId)).thenReturn(alreadyCompleted, completedWithEnd);
|
||||||
|
when(orderInfoMapper.updateById(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||||
|
mockEarningsCounts(1L);
|
||||||
|
|
||||||
|
lifecycleService.completeOrder(orderId, OrderCompletionContext.of(
|
||||||
|
OperatorType.ADMIN.getCode(),
|
||||||
|
"admin-1",
|
||||||
|
OrderTriggerSource.ADMIN_CONSOLE));
|
||||||
|
|
||||||
|
verify(earningsService, never()).createFromOrder(any());
|
||||||
|
verify(wxCustomMpService).sendOrderFinishMessageAsync(completedWithEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void refundOrder_partialAmount_updatesLedgerAndRecords() {
|
||||||
|
String orderId = UUID.randomUUID().toString();
|
||||||
|
BigDecimal finalAmount = BigDecimal.valueOf(100);
|
||||||
|
BigDecimal refundAmount = BigDecimal.valueOf(40);
|
||||||
|
|
||||||
|
PlayOrderInfoEntity order = buildOrder(orderId, OrderStatus.ACCEPTED.getCode());
|
||||||
|
order.setFinalAmount(finalAmount);
|
||||||
|
order.setOrderMoney(finalAmount);
|
||||||
|
order.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||||
|
order.setPayMethod("1");
|
||||||
|
|
||||||
|
when(orderInfoMapper.selectById(orderId)).thenReturn(order);
|
||||||
|
when(orderInfoMapper.updateById(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||||
|
|
||||||
|
PlayCustomUserInfoEntity customer = new PlayCustomUserInfoEntity();
|
||||||
|
customer.setId(order.getPurchaserBy());
|
||||||
|
customer.setAccountBalance(BigDecimal.valueOf(10));
|
||||||
|
when(customUserInfoService.getById(order.getPurchaserBy())).thenReturn(customer);
|
||||||
|
|
||||||
|
OrderRefundContext context = new OrderRefundContext();
|
||||||
|
context.setOrderId(orderId);
|
||||||
|
context.setRefundAmount(refundAmount);
|
||||||
|
context.setRefundReason("部分退款测试");
|
||||||
|
context.setOperatorType(OperatorType.ADMIN.getCode());
|
||||||
|
context.setOperatorId("admin-1");
|
||||||
|
context.withTriggerSource(OrderTriggerSource.ADMIN_API);
|
||||||
|
|
||||||
|
lifecycleService.refundOrder(context);
|
||||||
|
|
||||||
|
ArgumentCaptor<PlayOrderInfoEntity> updateCaptor = ArgumentCaptor.forClass(PlayOrderInfoEntity.class);
|
||||||
|
verify(orderInfoMapper).updateById(updateCaptor.capture());
|
||||||
|
PlayOrderInfoEntity updated = updateCaptor.getValue();
|
||||||
|
assertEquals(OrderStatus.CANCELLED.getCode(), updated.getOrderStatus());
|
||||||
|
assertEquals(OrderRefundFlag.REFUNDED.getCode(), updated.getRefundType());
|
||||||
|
assertEquals(refundAmount, updated.getRefundAmount());
|
||||||
|
|
||||||
|
verify(customUserInfoService).updateAccountBalanceById(
|
||||||
|
eq(order.getPurchaserBy()),
|
||||||
|
eq(customer.getAccountBalance()),
|
||||||
|
eq(customer.getAccountBalance().add(refundAmount)),
|
||||||
|
eq(BalanceOperationType.REFUND.getCode()),
|
||||||
|
eq("订单退款"),
|
||||||
|
eq(refundAmount),
|
||||||
|
eq(BigDecimal.ZERO),
|
||||||
|
eq(orderId));
|
||||||
|
|
||||||
|
verify(orderRefundInfoService).add(
|
||||||
|
eq(orderId),
|
||||||
|
eq(order.getPurchaserBy()),
|
||||||
|
eq(order.getAcceptBy()),
|
||||||
|
eq(order.getPayMethod()),
|
||||||
|
eq(OrderRefundRecordType.PARTIAL.getCode()),
|
||||||
|
eq(refundAmount),
|
||||||
|
eq("部分退款测试"),
|
||||||
|
eq(OperatorType.ADMIN.getCode()),
|
||||||
|
eq("admin-1"),
|
||||||
|
eq(OrderRefundState.PROCESSING.getCode()),
|
||||||
|
eq(ReviewRequirement.NOT_REQUIRED.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void refundOrder_throwsWhenAlreadyRefunded() {
|
||||||
|
String orderId = UUID.randomUUID().toString();
|
||||||
|
PlayOrderInfoEntity order = buildOrder(orderId, OrderStatus.ACCEPTED.getCode());
|
||||||
|
order.setFinalAmount(BigDecimal.TEN);
|
||||||
|
order.setOrderMoney(BigDecimal.TEN);
|
||||||
|
order.setRefundType(OrderRefundFlag.REFUNDED.getCode());
|
||||||
|
|
||||||
|
when(orderInfoMapper.selectById(orderId)).thenReturn(order);
|
||||||
|
|
||||||
|
OrderRefundContext context = new OrderRefundContext();
|
||||||
|
context.setOrderId(orderId);
|
||||||
|
context.setRefundAmount(BigDecimal.ONE);
|
||||||
|
context.withTriggerSource(OrderTriggerSource.ADMIN_API);
|
||||||
|
|
||||||
|
assertThrows(CustomException.class, () -> lifecycleService.refundOrder(context));
|
||||||
|
verify(orderInfoMapper, never()).updateById(any());
|
||||||
|
verify(orderRefundInfoService, never()).add(anyString(), anyString(), anyString(), anyString(), anyString(), any(), anyString(), anyString(), anyString(), anyString(), anyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayOrderInfoEntity buildOrder(String orderId, String status) {
|
||||||
|
PlayOrderInfoEntity entity = new PlayOrderInfoEntity();
|
||||||
|
entity.setId(orderId);
|
||||||
|
entity.setOrderStatus(status);
|
||||||
|
entity.setAcceptBy("clerk-1");
|
||||||
|
entity.setPurchaserBy("customer-1");
|
||||||
|
entity.setTenantId("tenant-1");
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockEarningsCounts(long... counts) {
|
||||||
|
LambdaQueryChainWrapper<EarningsLineEntity> chain = Mockito.mock(LambdaQueryChainWrapper.class);
|
||||||
|
when(chain.eq(any(), any())).thenReturn(chain);
|
||||||
|
if (counts.length == 0) {
|
||||||
|
when(chain.count()).thenReturn(0L);
|
||||||
|
} else {
|
||||||
|
org.mockito.stubbing.OngoingStubbing<Long> stubbing = when(chain.count()).thenReturn(counts[0]);
|
||||||
|
for (int i = 1; i < counts.length; i++) {
|
||||||
|
stubbing = stubbing.thenReturn(counts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
when(earningsService.lambdaQuery()).thenReturn(chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user