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.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
|
||||
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.vo.*;
|
||||
import com.starry.admin.modules.order.service.IOrderLifecycleService;
|
||||
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.service.IPlayCommodityInfoService;
|
||||
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
||||
@@ -53,9 +54,7 @@ public class PlayOrderInfoController {
|
||||
private IPlayCommodityInfoService playCommodityInfoService;
|
||||
|
||||
@Resource
|
||||
private IPlayOrderRefundInfoService playOrderRefundInfoService;
|
||||
@Resource
|
||||
private IPlayCustomUserInfoService customUserInfoService;
|
||||
private IOrderLifecycleService orderLifecycleService;
|
||||
@Resource
|
||||
private IPlayClerkUserInfoService clerkUserInfoService;
|
||||
|
||||
@@ -89,38 +88,14 @@ public class PlayOrderInfoController {
|
||||
// @PreAuthorize("@customSs.hasPermission('order:order:update')")
|
||||
@PostMapping("/orderRefund")
|
||||
public R orderRefund(@ApiParam(value = "退款信息", required = true) @Validated @RequestBody PlayOrderRefundAddVo vo) {
|
||||
PlayOrderInfoEntity orderInfo = orderInfoService.selectOrderInfoById(vo.getOrderId());
|
||||
if (orderInfo.getFinalAmount().compareTo(vo.getRefundAmount()) < 0) {
|
||||
throw new CustomException("退款金额不能大于支付金额");
|
||||
}
|
||||
if ("3".equals(orderInfo.getOrderStatus())) {
|
||||
throw new CustomException("【已完成】的订单无法操作退款");
|
||||
}
|
||||
if ("4".equals(orderInfo.getOrderStatus())) {
|
||||
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");
|
||||
OrderRefundContext context = new OrderRefundContext();
|
||||
context.setOrderId(vo.getOrderId());
|
||||
context.setRefundAmount(vo.getRefundAmount());
|
||||
context.setRefundReason(vo.getRefundReason());
|
||||
context.setOperatorType(OperatorType.ADMIN.getCode());
|
||||
context.setOperatorId(SecurityUtils.getUserId());
|
||||
context.withTriggerSource(OrderTriggerSource.ADMIN_API);
|
||||
orderLifecycleService.refundOrder(context);
|
||||
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.service.IPlayClerkUserInfoService;
|
||||
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.service.IOrderLifecycleService;
|
||||
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
||||
import com.starry.common.redis.RedisCache;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
@@ -18,7 +19,6 @@ import java.util.List;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -33,6 +33,8 @@ public class OrderJob {
|
||||
@Resource
|
||||
private IPlayClerkUserInfoService clerkUserInfoService;
|
||||
@Resource
|
||||
private IOrderLifecycleService orderLifecycleService;
|
||||
@Resource
|
||||
public RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
|
||||
@@ -60,11 +62,10 @@ public class OrderJob {
|
||||
|
||||
// 判断与开始时间相比较,如果大于服务时长,则修改订单状态为已完成
|
||||
if (ca.getOrderStartTime().plusMinutes(serviceDuration).isBefore(LocalDateTime.now())) {
|
||||
PlayOrderInfoEntity entity2 = new PlayOrderInfoEntity(ca.getId(), "3");
|
||||
entity2.setOrderEndTime(LocalDateTime.now());
|
||||
this.orderInfoMapper.updateById(entity2);
|
||||
// 发送消息
|
||||
wxCustomMpService.sendOrderFinishMessage(ca);
|
||||
orderLifecycleService.completeOrder(
|
||||
ca.getId(),
|
||||
OrderCompletionContext.scheduler("auto finish by duration")
|
||||
.withForceNotify(true));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -211,15 +211,142 @@ public class OrderConstant {
|
||||
public static final String EXCLUDE_HISTORY_NO = "0";
|
||||
public static final String EXCLUDE_HISTORY_YES = "1";
|
||||
|
||||
// Legacy constants for backward compatibility - consider deprecating
|
||||
@Deprecated
|
||||
public final static String ORDER_STATUS_0 = "0";
|
||||
@Deprecated
|
||||
public final static String ORDER_STATUS_1 = "1";
|
||||
@Deprecated
|
||||
public final static String ORDER_STATUS_2 = "2";
|
||||
@Deprecated
|
||||
public final static String ORDER_STATUS_3 = "3";
|
||||
@Deprecated
|
||||
public final static String ORDER_STATUS_4 = "4";
|
||||
@Getter
|
||||
public enum OrderRefundFlag {
|
||||
NOT_REFUNDED("0"),
|
||||
REFUNDED("1");
|
||||
|
||||
private final String code;
|
||||
|
||||
OrderRefundFlag(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
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.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;
|
||||
|
||||
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 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
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.order.mapper.PlayOrderInfoMapper;
|
||||
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.entity.PlayOrderComplaintInfoEntity;
|
||||
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.PlayOrderRefundInfoEntity;
|
||||
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.IPlayOrderEvaluateInfoService;
|
||||
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||
@@ -93,7 +99,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
private IPlayClerkLevelInfoService playClerkLevelInfoService;
|
||||
|
||||
@Resource
|
||||
private IEarningsService earningsService;
|
||||
private IOrderLifecycleService orderLifecycleService;
|
||||
|
||||
@Override
|
||||
public List<PlayOrderInfoEntity> getTotalOrderInfo(String tenantId) {
|
||||
@@ -117,7 +123,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
entity.setPlaceType(placeType);
|
||||
entity.setRewardType(rewardType);
|
||||
entity.setFirstOrder(firstOrder);
|
||||
entity.setRefundType("0");
|
||||
entity.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||
entity.setCommodityId(commodityId);
|
||||
entity.setCommodityType(commodityType);
|
||||
entity.setCommodityPrice(commodityPrice);
|
||||
@@ -162,9 +168,11 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
// 修改优惠券状态
|
||||
playCouponDetailsService.updateCouponUseStateByIds(couponIds, "2");
|
||||
if ("2".equals(placeType) && StrUtil.isNotBlank(acceptBy)) {
|
||||
PlayOrderInfoEntity latest = this.selectOrderInfoById(orderId);
|
||||
earningsService.createFromOrder(latest);
|
||||
wxCustomMpService.sendOrderFinishMessageAsync(latest);
|
||||
orderLifecycleService.completeOrder(
|
||||
orderId,
|
||||
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())) {
|
||||
PlayOrderInfoEntity latest = this.selectOrderInfoById(entity.getId());
|
||||
earningsService.createFromOrder(latest);
|
||||
wxCustomMpService.sendOrderFinishMessageAsync(latest);
|
||||
orderLifecycleService.completeOrder(
|
||||
entity.getId(),
|
||||
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.setRefundType("0");
|
||||
entity.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||
entity.setBackendEntry("0");
|
||||
entity.setPayMethod("0");
|
||||
entity.setOrderSettlementState("0");
|
||||
@@ -500,7 +510,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
|
||||
@Override
|
||||
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.setId(IdUtils.getUuid());
|
||||
entity.setOrderMoney(orderMoney);
|
||||
@@ -538,7 +548,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
public List<PlayOrderInfoEntity> listByEndTime(String clerkId, LocalDateTime endTime) {
|
||||
MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, clerkId);
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, "3");
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.COMPLETED.getCode());
|
||||
lambdaQueryWrapper.lt(PlayOrderInfoEntity::getOrderEndTime, endTime);
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderSettlementState, "0");
|
||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
@@ -604,7 +614,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
PlayClerkUserInfoEntity entity = playClerkUserInfoService.getById(clerkId);
|
||||
LambdaQueryWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
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::getSex, entity.getSex());
|
||||
// lambdaQueryWrapper.eq(PlayOrderInfoEntity::getExcludeHistory, "0")
|
||||
@@ -617,7 +627,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
public IPage<PlayRewardInfoReturnVo> selectRewardByPage(PlayRewardOrderQueryVo vo) {
|
||||
MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, "2");
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getRefundType, "0");
|
||||
lambdaQueryWrapper.eq(PlayOrderInfoEntity::getRefundType, OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||
lambdaQueryWrapper.selectAll(PlayOrderInfoEntity.class);
|
||||
lambdaQueryWrapper.orderByDesc(PlayOrderInfoEntity::getPurchaserTime);
|
||||
// 查询陪聊表
|
||||
@@ -636,7 +646,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -677,7 +687,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
PlayOrderDetailsReturnVo vo = this.baseMapper.selectJoinOne(PlayOrderDetailsReturnVo.class, lambdaQueryWrapper);
|
||||
|
||||
// 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.setCustomAvatar("");
|
||||
vo.setCustomId("");
|
||||
@@ -738,7 +748,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
PlayClerkOrderDetailsReturnVo returnVo = this.baseMapper.selectJoinOne(PlayClerkOrderDetailsReturnVo.class,
|
||||
lambdaQueryWrapper);
|
||||
// 如果订单状态为退款,查询订单退款原因
|
||||
if (returnVo.getOrderStatus().equals(OrderConstant.ORDER_STATUS_4)) {
|
||||
if (returnVo.getOrderStatus().equals(OrderStatus.CANCELLED.getCode())) {
|
||||
PlayOrderRefundInfoEntity orderRefundInfoEntity = playOrderRefundInfoService
|
||||
.selectPlayOrderRefundInfoByOrderId(returnVo.getId());
|
||||
returnVo.setRefundByType(orderRefundInfoEntity.getRefundByType());
|
||||
@@ -781,7 +791,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
PlayCustomOrderDetailsReturnVo returnVo = this.baseMapper.selectJoinOne(PlayCustomOrderDetailsReturnVo.class,
|
||||
lambdaQueryWrapper);
|
||||
// 如果订单状态为退款,查询订单退款原因
|
||||
if (returnVo.getOrderStatus().equals(OrderConstant.ORDER_STATUS_4)) {
|
||||
if (returnVo.getOrderStatus().equals(OrderStatus.CANCELLED.getCode())) {
|
||||
PlayOrderRefundInfoEntity orderRefundInfoEntity = playOrderRefundInfoService
|
||||
.selectPlayOrderRefundInfoByOrderId(returnVo.getId());
|
||||
returnVo.setRefundByType(orderRefundInfoEntity.getRefundByType());
|
||||
@@ -872,7 +882,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
// throw new CustomException("只有未接单的店员才可接单");
|
||||
}
|
||||
}
|
||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity(orderId, OrderConstant.ORDER_STATUS_1);
|
||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity(orderId, OrderStatus.ACCEPTED.getCode());
|
||||
entity.setAcceptBy(acceptBy);
|
||||
entity.setAcceptTime(LocalDateTime.now());
|
||||
ClerkEstimatedRevenueVo estimatedRevenueVo = this.getClerkEstimatedRevenue(acceptBy, orderInfo.getCouponIds(),
|
||||
@@ -944,11 +954,11 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
@Override
|
||||
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("禁止操作");
|
||||
}
|
||||
// 完成订单只能顾客或者管理员操作
|
||||
if (orderState.equals(ORDER_STATUS_3) && "1".equals(operatorByType)) {
|
||||
if (OrderStatus.COMPLETED.getCode().equals(orderState) && "1".equals(operatorByType)) {
|
||||
throw new CustomException("禁止操作");
|
||||
}
|
||||
PlayOrderInfoEntity entity = this.selectOrderInfoById(orderId);
|
||||
@@ -960,38 +970,30 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
if ("1".equals(operatorByType) && !entity.getAcceptBy().equals(operatorBy)) {
|
||||
throw new CustomException("只能操作本人订单");
|
||||
}
|
||||
switch (orderState) {
|
||||
case ORDER_STATUS_2: {
|
||||
// 开始订单前,订单状态必须为接单状态
|
||||
if (!entity.getOrderStatus().equals(OrderConstant.ORDER_STATUS_1)) {
|
||||
if (OrderStatus.IN_PROGRESS.getCode().equals(orderState)) {
|
||||
if (!OrderStatus.ACCEPTED.getCode().equals(entity.getOrderStatus())) {
|
||||
log.error("订单状态异常,不能开始接单,orderId={},orderStace={}", orderId, orderState);
|
||||
throw new CustomException("订单状态异常,不能开始订单");
|
||||
}
|
||||
PlayOrderInfoEntity entity2 = new PlayOrderInfoEntity(orderId, ORDER_STATUS_2);
|
||||
PlayOrderInfoEntity entity2 = new PlayOrderInfoEntity(orderId, OrderStatus.IN_PROGRESS.getCode());
|
||||
entity2.setOrderStartTime(LocalDateTime.now());
|
||||
this.baseMapper.updateById(entity2);
|
||||
break;
|
||||
}
|
||||
case ORDER_STATUS_3: {
|
||||
// 完成订单前,订单状态必须为开始状态
|
||||
if (!entity.getOrderStatus().equals(ORDER_STATUS_2)) {
|
||||
} else if (OrderStatus.COMPLETED.getCode().equals(orderState)) {
|
||||
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: {
|
||||
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("只能操作本人订单");
|
||||
}
|
||||
// 取消订单(必须订单未接单或者为开始状态)
|
||||
if (!orderInfo.getOrderStatus().equals(OrderConstant.ORDER_STATUS_0)
|
||||
&& !orderInfo.getOrderStatus().equals(OrderConstant.ORDER_STATUS_1)) {
|
||||
if (!orderInfo.getOrderStatus().equals(OrderStatus.PENDING.getCode())
|
||||
&& !orderInfo.getOrderStatus().equals(OrderStatus.ACCEPTED.getCode())) {
|
||||
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());
|
||||
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);
|
||||
// 取消订单后,记录退款信息
|
||||
playOrderRefundInfoService.add(orderId, orderInfo.getPurchaserBy(), orderInfo.getAcceptBy(),
|
||||
orderInfo.getPayMethod(), "0", orderInfo.getFinalAmount(), refundReason, operatorByType, operatorBy,
|
||||
"0", "0");
|
||||
playOrderRefundInfoService.add(orderInfo.getId(), orderInfo.getPurchaserBy(), orderInfo.getAcceptBy(),
|
||||
orderInfo.getPayMethod(), OrderRefundRecordType.FULL.getCode(), orderInfo.getFinalAmount(), refundReason, operatorByType, operatorBy,
|
||||
OrderRefundState.PROCESSING.getCode(), ReviewRequirement.NOT_REQUIRED.getCode());
|
||||
wxCustomMpService.sendOrderCancelMessageAsync(orderInfo, refundReason);
|
||||
}
|
||||
|
||||
@@ -1042,8 +1044,8 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
throw new CustomException("禁止操作");
|
||||
}
|
||||
PlayOrderInfoEntity orderInfo = this.selectOrderInfoById(orderId);
|
||||
if (!OrderConstant.ORDER_STATUS_1.equals(orderInfo.getOrderStatus())
|
||||
&& !OrderConstant.ORDER_STATUS_2.equals(orderInfo.getOrderStatus())) {
|
||||
if (!OrderStatus.ACCEPTED.getCode().equals(orderInfo.getOrderStatus())
|
||||
&& !OrderStatus.IN_PROGRESS.getCode().equals(orderInfo.getOrderStatus())) {
|
||||
throw new CustomException("订单状态异常,无法取消");
|
||||
}
|
||||
BigDecimal actualRefundAmount = refundAmount != null ? refundAmount : orderInfo.getFinalAmount();
|
||||
@@ -1055,12 +1057,12 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
}
|
||||
|
||||
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.setRefundReason(refundReason);
|
||||
updateEntity.setRefundType("1");
|
||||
updateEntity.setRefundType(OrderRefundFlag.REFUNDED.getCode());
|
||||
updateEntity.setOrderCancelTime(now);
|
||||
if (OrderConstant.ORDER_STATUS_2.equals(orderInfo.getOrderStatus())) {
|
||||
if (OrderStatus.IN_PROGRESS.getCode().equals(orderInfo.getOrderStatus())) {
|
||||
updateEntity.setOrderEndTime(now);
|
||||
}
|
||||
this.baseMapper.updateById(updateEntity);
|
||||
@@ -1071,11 +1073,15 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
}
|
||||
|
||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(),
|
||||
customUserInfo.getAccountBalance().add(actualRefundAmount), "3", "订单取消退款",
|
||||
customUserInfo.getAccountBalance().add(actualRefundAmount), BalanceOperationType.REFUND.getCode(), "订单取消退款",
|
||||
actualRefundAmount, BigDecimal.ZERO, orderId);
|
||||
|
||||
OrderRefundRecordType forceCancelRefundType = actualRefundAmount.compareTo(orderInfo.getFinalAmount()) == 0
|
||||
? OrderRefundRecordType.FULL
|
||||
: OrderRefundRecordType.PARTIAL;
|
||||
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);
|
||||
wxCustomMpService.sendOrderCancelMessageAsync(latest, refundReason);
|
||||
@@ -1121,4 +1127,25 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
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.order.mapper.PlayOrderInfoMapper;
|
||||
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.OrderCreationRequest;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
@@ -450,7 +451,7 @@ class PlayOrderInfoServiceTest {
|
||||
String orderId = "order_force_cancel";
|
||||
PlayOrderInfoEntity inProgressOrder = new PlayOrderInfoEntity();
|
||||
inProgressOrder.setId(orderId);
|
||||
inProgressOrder.setOrderStatus(OrderConstant.ORDER_STATUS_2);
|
||||
inProgressOrder.setOrderStatus(OrderStatus.IN_PROGRESS.getCode());
|
||||
inProgressOrder.setAcceptBy("clerk-1");
|
||||
inProgressOrder.setPurchaserBy("customer-1");
|
||||
inProgressOrder.setFinalAmount(BigDecimal.valueOf(100));
|
||||
@@ -458,7 +459,7 @@ class PlayOrderInfoServiceTest {
|
||||
|
||||
PlayOrderInfoEntity cancelledOrder = new PlayOrderInfoEntity();
|
||||
cancelledOrder.setId(orderId);
|
||||
cancelledOrder.setOrderStatus(OrderConstant.ORDER_STATUS_4);
|
||||
cancelledOrder.setOrderStatus(OrderStatus.CANCELLED.getCode());
|
||||
|
||||
PlayCustomUserInfoEntity customUserInfo = new PlayCustomUserInfoEntity();
|
||||
customUserInfo.setId("customer-1");
|
||||
@@ -472,18 +473,22 @@ class PlayOrderInfoServiceTest {
|
||||
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(),
|
||||
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,
|
||||
BigDecimal.valueOf(80), "管理员取消测试", Collections.emptyList()));
|
||||
|
||||
verify(orderInfoMapper, times(1)).updateById(any(PlayOrderInfoEntity.class));
|
||||
verify(customUserInfoService, times(1)).updateAccountBalanceById(eq("customer-1"), any(BigDecimal.class),
|
||||
any(BigDecimal.class), anyString(), anyString(), eq(BigDecimal.valueOf(80)), eq(BigDecimal.ZERO),
|
||||
eq(orderId));
|
||||
verify(playOrderRefundInfoService, times(1)).add(eq(orderId), eq("customer-1"), eq("clerk-1"), anyString(),
|
||||
eq("0"), eq(BigDecimal.valueOf(80)), eq("管理员取消测试"), eq("2"), eq("admin-1"), eq("0"), eq("0"));
|
||||
verify(wxCustomMpService, times(1)).sendOrderCancelMessage(any(PlayOrderInfoEntity.class),
|
||||
any(BigDecimal.class), eq(OrderConstant.BalanceOperationType.REFUND.getCode()), eq("订单取消退款"),
|
||||
eq(BigDecimal.valueOf(80)), eq(BigDecimal.ZERO), eq(orderId));
|
||||
verify(playOrderRefundInfoService, times(1)).add(eq(orderId), eq("customer-1"), eq("clerk-1"),
|
||||
eq(inProgressOrder.getPayMethod()),
|
||||
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("管理员取消测试"));
|
||||
}
|
||||
|
||||
@@ -493,7 +498,7 @@ class PlayOrderInfoServiceTest {
|
||||
String orderId = "order_invalid_force_cancel";
|
||||
PlayOrderInfoEntity pendingOrder = new PlayOrderInfoEntity();
|
||||
pendingOrder.setId(orderId);
|
||||
pendingOrder.setOrderStatus(OrderConstant.ORDER_STATUS_0);
|
||||
pendingOrder.setOrderStatus(OrderStatus.PENDING.getCode());
|
||||
pendingOrder.setAcceptBy("clerk-1");
|
||||
pendingOrder.setPurchaserBy("customer-1");
|
||||
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