This commit is contained in:
@@ -134,7 +134,16 @@ public class PlayClerkUserInfoServiceImpl extends ServiceImpl<PlayClerkUserInfoM
|
|||||||
lambdaWrapper.leftJoin(PlayClerkLevelInfoEntity.class, PlayClerkLevelInfoEntity::getId,
|
lambdaWrapper.leftJoin(PlayClerkLevelInfoEntity.class, PlayClerkLevelInfoEntity::getId,
|
||||||
PlayClerkUserInfoEntity::getLevelId);
|
PlayClerkUserInfoEntity::getLevelId);
|
||||||
lambdaWrapper.eq(PlayClerkUserInfoEntity::getId, clerkId);
|
lambdaWrapper.eq(PlayClerkUserInfoEntity::getId, clerkId);
|
||||||
return this.baseMapper.selectJoinOne(PlayClerkLevelInfoEntity.class, lambdaWrapper);
|
PlayClerkLevelInfoEntity levelInfo = this.baseMapper.selectJoinOne(PlayClerkLevelInfoEntity.class, lambdaWrapper);
|
||||||
|
if (levelInfo != null) {
|
||||||
|
return levelInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayClerkUserInfoEntity clerk = this.baseMapper.selectById(clerkId);
|
||||||
|
if (clerk == null || StringUtils.isBlank(clerk.getLevelId())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return playClerkLevelInfoService.getById(clerk.getLevelId());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ 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.OrderTriggerSource;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrdersExpiredState;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrdersExpiredState;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.PayMethod;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.PayMethod;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.PaymentSource;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.PlaceType;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.PlaceType;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.YesNoFlag;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.YesNoFlag;
|
||||||
@@ -41,6 +42,7 @@ import com.starry.admin.modules.order.module.vo.ClerkEstimatedRevenueVo;
|
|||||||
import com.starry.admin.modules.order.service.IOrderLifecycleService;
|
import com.starry.admin.modules.order.service.IOrderLifecycleService;
|
||||||
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
|
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
|
||||||
import com.starry.admin.modules.order.service.support.ClerkRevenueCalculator;
|
import com.starry.admin.modules.order.service.support.ClerkRevenueCalculator;
|
||||||
|
import com.starry.admin.modules.personnel.service.IPlayBalanceDetailsInfoService;
|
||||||
import com.starry.admin.modules.shop.module.constant.CouponUseState;
|
import com.starry.admin.modules.shop.module.constant.CouponUseState;
|
||||||
import com.starry.admin.modules.shop.module.entity.PlayCouponDetailsEntity;
|
import com.starry.admin.modules.shop.module.entity.PlayCouponDetailsEntity;
|
||||||
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
|
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
|
||||||
@@ -105,6 +107,9 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
|||||||
@Resource
|
@Resource
|
||||||
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IPlayBalanceDetailsInfoService playBalanceDetailsInfoService;
|
||||||
|
|
||||||
private Map<StrategyKey, OrderPlacementStrategy> placementStrategies;
|
private Map<StrategyKey, OrderPlacementStrategy> placementStrategies;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@@ -515,6 +520,10 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
|||||||
throw new CustomException("每个订单只能退款一次~");
|
throw new CustomException("每个订单只能退款一次~");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isBalancePaidOrder(order) && !playBalanceDetailsInfoService.existsCustomerConsumeRecord(order.getPurchaserBy(), order.getId())) {
|
||||||
|
throw new CustomException("订单未发生余额扣款,无法退款");
|
||||||
|
}
|
||||||
|
|
||||||
UpdateWrapper<PlayOrderInfoEntity> refundUpdate = new UpdateWrapper<>();
|
UpdateWrapper<PlayOrderInfoEntity> refundUpdate = new UpdateWrapper<>();
|
||||||
refundUpdate.eq("id", order.getId())
|
refundUpdate.eq("id", order.getId())
|
||||||
.eq("refund_type", OrderRefundFlag.NOT_REFUNDED.getCode())
|
.eq("refund_type", OrderRefundFlag.NOT_REFUNDED.getCode())
|
||||||
@@ -581,6 +590,19 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
|||||||
refundOperationType);
|
refundOperationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isBalancePaidOrder(PlayOrderInfoEntity order) {
|
||||||
|
String sourceCode = order.getPaymentSource();
|
||||||
|
if (StrUtil.isBlank(sourceCode)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return PaymentSource.fromCode(sourceCode) == PaymentSource.BALANCE;
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
log.warn("Unknown payment source {}, defaulting to balance for refund guard", sourceCode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void validateOrderCreationRequest(OrderCreationContext context) {
|
private void validateOrderCreationRequest(OrderCreationContext context) {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
throw new CustomException("订单创建请求不能为空");
|
throw new CustomException("订单创建请求不能为空");
|
||||||
|
|||||||
@@ -37,6 +37,13 @@ public class ClerkRevenueCalculator {
|
|||||||
BigDecimal baseAmount = orderAmount == null ? BigDecimal.ZERO : orderAmount;
|
BigDecimal baseAmount = orderAmount == null ? BigDecimal.ZERO : orderAmount;
|
||||||
ClerkEstimatedRevenueVo estimatedRevenueVo = new ClerkEstimatedRevenueVo();
|
ClerkEstimatedRevenueVo estimatedRevenueVo = new ClerkEstimatedRevenueVo();
|
||||||
|
|
||||||
|
if (levelInfo == null) {
|
||||||
|
log.warn("店员{}缺少等级提成配置,预计收益按0处理", clerkId);
|
||||||
|
estimatedRevenueVo.setRevenueRatio(0);
|
||||||
|
estimatedRevenueVo.setRevenueAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||||
|
return estimatedRevenueVo;
|
||||||
|
}
|
||||||
|
|
||||||
boolean fallbackToOther = false;
|
boolean fallbackToOther = false;
|
||||||
OrderConstant.PlaceType placeTypeEnum;
|
OrderConstant.PlaceType placeTypeEnum;
|
||||||
try {
|
try {
|
||||||
@@ -49,13 +56,13 @@ public class ClerkRevenueCalculator {
|
|||||||
|
|
||||||
switch (placeTypeEnum) {
|
switch (placeTypeEnum) {
|
||||||
case SPECIFIED: // 指定单
|
case SPECIFIED: // 指定单
|
||||||
fillRegularOrderRevenue(firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
fillRegularOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||||
break;
|
break;
|
||||||
case RANDOM: // 随机单
|
case RANDOM: // 随机单
|
||||||
fillRandomOrderRevenue(firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
fillRandomOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||||
break;
|
break;
|
||||||
case REWARD: // 打赏单
|
case REWARD: // 打赏单
|
||||||
fillRewardOrderRevenue(firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
fillRewardOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||||
break;
|
break;
|
||||||
case OTHER:
|
case OTHER:
|
||||||
default:
|
default:
|
||||||
@@ -71,42 +78,56 @@ public class ClerkRevenueCalculator {
|
|||||||
return estimatedRevenueVo;
|
return estimatedRevenueVo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillRegularOrderRevenue(String firstOrder, BigDecimal orderAmount,
|
private void fillRegularOrderRevenue(String clerkId, String firstOrder, BigDecimal orderAmount,
|
||||||
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
||||||
if ("1".equals(firstOrder)) {
|
if ("1".equals(firstOrder)) {
|
||||||
vo.setRevenueRatio(levelInfo.getFirstRegularRatio());
|
int ratio = safeRatio(levelInfo.getFirstRegularRatio(), "firstRegularRatio", clerkId);
|
||||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getFirstRegularRatio()));
|
vo.setRevenueRatio(ratio);
|
||||||
|
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||||
} else {
|
} else {
|
||||||
vo.setRevenueRatio(levelInfo.getNotFirstRegularRatio());
|
int ratio = safeRatio(levelInfo.getNotFirstRegularRatio(), "notFirstRegularRatio", clerkId);
|
||||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getNotFirstRegularRatio()));
|
vo.setRevenueRatio(ratio);
|
||||||
|
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillRandomOrderRevenue(String firstOrder, BigDecimal orderAmount,
|
private void fillRandomOrderRevenue(String clerkId, String firstOrder, BigDecimal orderAmount,
|
||||||
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
||||||
if ("1".equals(firstOrder)) {
|
if ("1".equals(firstOrder)) {
|
||||||
vo.setRevenueRatio(levelInfo.getFirstRandomRadio());
|
int ratio = safeRatio(levelInfo.getFirstRandomRadio(), "firstRandomRatio", clerkId);
|
||||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getFirstRandomRadio()));
|
vo.setRevenueRatio(ratio);
|
||||||
|
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||||
} else {
|
} else {
|
||||||
vo.setRevenueRatio(levelInfo.getNotFirstRandomRadio());
|
int ratio = safeRatio(levelInfo.getNotFirstRandomRadio(), "notFirstRandomRatio", clerkId);
|
||||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getNotFirstRandomRadio()));
|
vo.setRevenueRatio(ratio);
|
||||||
|
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillRewardOrderRevenue(String firstOrder, BigDecimal orderAmount,
|
private void fillRewardOrderRevenue(String clerkId, String firstOrder, BigDecimal orderAmount,
|
||||||
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
||||||
if ("1".equals(firstOrder)) {
|
if ("1".equals(firstOrder)) {
|
||||||
vo.setRevenueRatio(levelInfo.getFirstRewardRatio());
|
int ratio = safeRatio(levelInfo.getFirstRewardRatio(), "firstRewardRatio", clerkId);
|
||||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getFirstRewardRatio()));
|
vo.setRevenueRatio(ratio);
|
||||||
|
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||||
} else {
|
} else {
|
||||||
vo.setRevenueRatio(levelInfo.getNotFirstRewardRatio());
|
int ratio = safeRatio(levelInfo.getNotFirstRewardRatio(), "notFirstRewardRatio", clerkId);
|
||||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getNotFirstRewardRatio()));
|
vo.setRevenueRatio(ratio);
|
||||||
|
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal scaleAmount(BigDecimal baseAmount, Integer ratio) {
|
private int safeRatio(Integer ratio, String ratioField, String clerkId) {
|
||||||
|
if (ratio == null) {
|
||||||
|
log.warn("店员{}的等级配置字段{}缺失,已按0%处理", clerkId, ratioField);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal scaleAmount(BigDecimal baseAmount, int ratio) {
|
||||||
return baseAmount
|
return baseAmount
|
||||||
.multiply(new BigDecimal(ratio).divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
.multiply(BigDecimal.valueOf(ratio).divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
||||||
.setScale(2, RoundingMode.HALF_UP);
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.starry.admin.modules.personnel.module.enums;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户类型枚举(0:陪聊;1:顾客)
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum BalanceDetailsUserType {
|
||||||
|
CLERK("0"),
|
||||||
|
CUSTOMER("1");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
BalanceDetailsUserType(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,6 +69,17 @@ public interface IPlayBalanceDetailsInfoService extends IService<PlayBalanceDeta
|
|||||||
BigDecimal balanceAfterOperation, String operationType, String operationAction, BigDecimal balanceMoney,
|
BigDecimal balanceAfterOperation, String operationType, String operationAction, BigDecimal balanceMoney,
|
||||||
BigDecimal giftAmount, String orderId);
|
BigDecimal giftAmount, String orderId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断顾客是否对指定订单发生过余额扣款。
|
||||||
|
*
|
||||||
|
* @param userId
|
||||||
|
* 顾客ID
|
||||||
|
* @param orderId
|
||||||
|
* 订单ID
|
||||||
|
* @return 存在消费流水返回true
|
||||||
|
*/
|
||||||
|
boolean existsCustomerConsumeRecord(String userId, String orderId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增余额明细
|
* 新增余额明细
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -6,11 +6,14 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||||
|
import com.starry.admin.common.exception.CustomException;
|
||||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.BalanceOperationType;
|
||||||
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.IPlayOrderInfoService;
|
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||||
import com.starry.admin.modules.personnel.mapper.PlayBalanceDetailsInfoMapper;
|
import com.starry.admin.modules.personnel.mapper.PlayBalanceDetailsInfoMapper;
|
||||||
import com.starry.admin.modules.personnel.module.entity.PlayBalanceDetailsInfoEntity;
|
import com.starry.admin.modules.personnel.module.entity.PlayBalanceDetailsInfoEntity;
|
||||||
|
import com.starry.admin.modules.personnel.module.enums.BalanceDetailsUserType;
|
||||||
import com.starry.admin.modules.personnel.module.vo.PlayBalanceDetailsQueryVo;
|
import com.starry.admin.modules.personnel.module.vo.PlayBalanceDetailsQueryVo;
|
||||||
import com.starry.admin.modules.personnel.module.vo.PlayBalanceDetailsReturnVo;
|
import com.starry.admin.modules.personnel.module.vo.PlayBalanceDetailsReturnVo;
|
||||||
import com.starry.admin.modules.personnel.service.IPlayBalanceDetailsInfoService;
|
import com.starry.admin.modules.personnel.service.IPlayBalanceDetailsInfoService;
|
||||||
@@ -114,7 +117,12 @@ public class PlayBalanceDetailsInfoServiceImpl
|
|||||||
public void insertBalanceDetailsInfo(String userType, String userId, BigDecimal balanceBeforeOperation,
|
public void insertBalanceDetailsInfo(String userType, String userId, BigDecimal balanceBeforeOperation,
|
||||||
BigDecimal balanceAfterOperation, String operationType, String operationAction, BigDecimal balanceMoney,
|
BigDecimal balanceAfterOperation, String operationType, String operationAction, BigDecimal balanceMoney,
|
||||||
BigDecimal giftAmount, String orderId) {
|
BigDecimal giftAmount, String orderId) {
|
||||||
PlayOrderInfoEntity orderInfo = playOrderInfoService.selectOrderInfoById(orderId);
|
PlayOrderInfoEntity orderInfo = null;
|
||||||
|
try {
|
||||||
|
orderInfo = playOrderInfoService.selectOrderInfoById(orderId);
|
||||||
|
} catch (CustomException ex) {
|
||||||
|
orderInfo = null;
|
||||||
|
}
|
||||||
PlayBalanceDetailsInfoEntity entity = new PlayBalanceDetailsInfoEntity();
|
PlayBalanceDetailsInfoEntity entity = new PlayBalanceDetailsInfoEntity();
|
||||||
entity.setId(IdUtils.getUuid());
|
entity.setId(IdUtils.getUuid());
|
||||||
entity.setUserType(userType);
|
entity.setUserType(userType);
|
||||||
@@ -180,4 +188,15 @@ public class PlayBalanceDetailsInfoServiceImpl
|
|||||||
public int deletePlayBalanceDetailsInfoById(String id) {
|
public int deletePlayBalanceDetailsInfoById(String id) {
|
||||||
return playBalanceDetailsInfoMapper.deleteById(id);
|
return playBalanceDetailsInfoMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean existsCustomerConsumeRecord(String userId, String orderId) {
|
||||||
|
return lambdaQuery()
|
||||||
|
.eq(PlayBalanceDetailsInfoEntity::getUserType, BalanceDetailsUserType.CUSTOMER.getCode())
|
||||||
|
.eq(PlayBalanceDetailsInfoEntity::getUserId, userId)
|
||||||
|
.eq(PlayBalanceDetailsInfoEntity::getOrderId, orderId)
|
||||||
|
.eq(PlayBalanceDetailsInfoEntity::getOperationType, BalanceOperationType.CONSUME.getCode())
|
||||||
|
.last("limit 1")
|
||||||
|
.one() != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
import org.springframework.test.context.TestPropertySource;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
@AutoConfigureMockMvc
|
@AutoConfigureMockMvc
|
||||||
@ActiveProfiles("apitest")
|
@ActiveProfiles("apitest")
|
||||||
|
@TestPropertySource(properties = "spring.task.scheduling.enabled=false")
|
||||||
public abstract class AbstractApiTest {
|
public abstract class AbstractApiTest {
|
||||||
|
|
||||||
protected static final String TENANT_HEADER = "X-Tenant";
|
protected static final String TENANT_HEADER = "X-Tenant";
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.starry.admin.api;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import com.starry.admin.common.apitest.ApiTestDataSeeder;
|
||||||
|
import com.starry.admin.modules.blindbox.module.entity.BlindBoxConfigEntity;
|
||||||
|
import com.starry.admin.modules.blindbox.service.BlindBoxConfigService;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||||
|
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||||
|
import com.starry.admin.utils.SecurityUtils;
|
||||||
|
import com.starry.common.constant.Constants;
|
||||||
|
import com.starry.common.context.CustomSecurityContextHolder;
|
||||||
|
import com.starry.common.utils.IdUtils;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
|
class WxBlindBoxOrderApiTest extends WxCustomOrderApiTestSupport {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BlindBoxConfigService blindBoxConfigService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void blindBoxPurchaseFailsWhenBalanceInsufficient() throws Exception {
|
||||||
|
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||||
|
String configId = "blind-" + IdUtils.getUuid();
|
||||||
|
try {
|
||||||
|
BlindBoxConfigEntity config = new BlindBoxConfigEntity();
|
||||||
|
config.setId(configId);
|
||||||
|
config.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||||
|
config.setName("API盲盒-" + IdUtils.getUuid().substring(0, 6));
|
||||||
|
config.setPrice(new BigDecimal("66.00"));
|
||||||
|
config.setStatus(1);
|
||||||
|
blindBoxConfigService.save(config);
|
||||||
|
|
||||||
|
setCustomerBalance(BigDecimal.ZERO);
|
||||||
|
String customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
||||||
|
customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);
|
||||||
|
|
||||||
|
ensureTenantContext();
|
||||||
|
long beforeCount = playOrderInfoService.lambdaQuery()
|
||||||
|
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
|
||||||
|
.eq(PlayOrderInfoEntity::getOrderType, OrderConstant.OrderType.BLIND_BOX_PURCHASE.getCode())
|
||||||
|
.count();
|
||||||
|
|
||||||
|
String payload = "{" +
|
||||||
|
"\"blindBoxId\":\"" + configId + "\"," +
|
||||||
|
"\"clerkId\":\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"," +
|
||||||
|
"\"weiChatCode\":\"apitest-customer-wx\"" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
mockMvc.perform(post("/wx/blind-box/order/purchase")
|
||||||
|
.header(USER_HEADER, DEFAULT_USER)
|
||||||
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||||
|
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(payload))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.code").value(998));
|
||||||
|
|
||||||
|
ensureTenantContext();
|
||||||
|
long afterCount = playOrderInfoService.lambdaQuery()
|
||||||
|
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
|
||||||
|
.eq(PlayOrderInfoEntity::getOrderType, OrderConstant.OrderType.BLIND_BOX_PURCHASE.getCode())
|
||||||
|
.count();
|
||||||
|
Assertions.assertThat(afterCount).isEqualTo(beforeCount);
|
||||||
|
} finally {
|
||||||
|
blindBoxConfigService.removeById(configId);
|
||||||
|
CustomSecurityContextHolder.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,43 @@ class WxCustomGiftOrderApiTest extends WxCustomOrderApiTestSupport {
|
|||||||
|
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void giftOrderFailsWhenBalanceInsufficient() throws Exception {
|
||||||
|
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||||
|
String remark = "API gift insufficient " + IdUtils.getUuid();
|
||||||
|
try {
|
||||||
|
setCustomerBalance(BigDecimal.ZERO);
|
||||||
|
String customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
||||||
|
customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);
|
||||||
|
|
||||||
|
String payload = "{" +
|
||||||
|
"\"clerkId\":\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"," +
|
||||||
|
"\"giftId\":\"" + ApiTestDataSeeder.DEFAULT_GIFT_ID + "\"," +
|
||||||
|
"\"giftQuantity\":1," +
|
||||||
|
"\"weiChatCode\":\"apitest-customer-wx\"," +
|
||||||
|
"\"remark\":\"" + remark + "\"" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
mockMvc.perform(post("/wx/custom/order/gift")
|
||||||
|
.header(USER_HEADER, DEFAULT_USER)
|
||||||
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||||
|
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(payload))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.code").value(998));
|
||||||
|
|
||||||
|
ensureTenantContext();
|
||||||
|
long count = playOrderInfoService.lambdaQuery()
|
||||||
|
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
|
||||||
|
.eq(PlayOrderInfoEntity::getRemark, remark)
|
||||||
|
.count();
|
||||||
|
Assertions.assertThat(count).isZero();
|
||||||
|
} finally {
|
||||||
|
CustomSecurityContextHolder.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
// 测试用例:用户余额充足且携带有效登录态时,请求 /wx/custom/order/gift 下单指定礼物,
|
// 测试用例:用户余额充足且携带有效登录态时,请求 /wx/custom/order/gift 下单指定礼物,
|
||||||
// 期望生成已完成的礼物奖励订单、产生对应收益记录,同时校验用户/陪玩师礼物计数与账户余额随订单金额同步更新。
|
// 期望生成已完成的礼物奖励订单、产生对应收益记录,同时校验用户/陪玩师礼物计数与账户余额随订单金额同步更新。
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.starry.admin.common.apitest.ApiTestDataSeeder;
|
import com.starry.admin.common.apitest.ApiTestDataSeeder;
|
||||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
|
||||||
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||||
import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService;
|
import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService;
|
||||||
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.custom.module.entity.PlayCustomUserInfoEntity;
|
||||||
@@ -12,12 +13,14 @@ import com.starry.admin.modules.order.module.constant.OrderConstant;
|
|||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.PlaceType;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.PlaceType;
|
||||||
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||||
import com.starry.admin.modules.shop.module.constant.CouponUseState;
|
import com.starry.admin.modules.shop.module.constant.CouponUseState;
|
||||||
|
import com.starry.admin.modules.shop.module.entity.PlayCommodityAndLevelInfoEntity;
|
||||||
import com.starry.admin.modules.shop.module.entity.PlayCouponDetailsEntity;
|
import com.starry.admin.modules.shop.module.entity.PlayCouponDetailsEntity;
|
||||||
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
|
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
|
||||||
import com.starry.admin.modules.shop.module.enums.CouponClaimConditionType;
|
import com.starry.admin.modules.shop.module.enums.CouponClaimConditionType;
|
||||||
import com.starry.admin.modules.shop.module.enums.CouponDiscountType;
|
import com.starry.admin.modules.shop.module.enums.CouponDiscountType;
|
||||||
import com.starry.admin.modules.shop.module.enums.CouponOnlineState;
|
import com.starry.admin.modules.shop.module.enums.CouponOnlineState;
|
||||||
import com.starry.admin.modules.shop.module.enums.CouponValidityPeriodType;
|
import com.starry.admin.modules.shop.module.enums.CouponValidityPeriodType;
|
||||||
|
import com.starry.admin.modules.shop.service.IPlayCommodityAndLevelInfoService;
|
||||||
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
|
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
|
||||||
import com.starry.admin.modules.shop.service.IPlayCouponInfoService;
|
import com.starry.admin.modules.shop.service.IPlayCouponInfoService;
|
||||||
import com.starry.admin.modules.weichat.service.WxTokenService;
|
import com.starry.admin.modules.weichat.service.WxTokenService;
|
||||||
@@ -66,7 +69,11 @@ abstract class WxCustomOrderApiTestSupport extends AbstractApiTest {
|
|||||||
@Autowired
|
@Autowired
|
||||||
protected IPlayCouponDetailsService couponDetailsService;
|
protected IPlayCouponDetailsService couponDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected IPlayCommodityAndLevelInfoService commodityAndLevelInfoService;
|
||||||
|
|
||||||
protected void resetCustomerBalance() {
|
protected void resetCustomerBalance() {
|
||||||
|
ensureDefaultClerkLevelBinding();
|
||||||
BigDecimal balance = new BigDecimal("200.00");
|
BigDecimal balance = new BigDecimal("200.00");
|
||||||
customUserInfoService.updateAccountBalanceById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, balance);
|
customUserInfoService.updateAccountBalanceById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, balance);
|
||||||
customUserInfoService.lambdaUpdate()
|
customUserInfoService.lambdaUpdate()
|
||||||
@@ -80,6 +87,11 @@ abstract class WxCustomOrderApiTestSupport extends AbstractApiTest {
|
|||||||
.update();
|
.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setCustomerBalance(BigDecimal balance) {
|
||||||
|
ensureDefaultClerkLevelBinding();
|
||||||
|
customUserInfoService.updateAccountBalanceById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, balance);
|
||||||
|
}
|
||||||
|
|
||||||
protected void ensureTenantContext() {
|
protected void ensureTenantContext() {
|
||||||
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||||
}
|
}
|
||||||
@@ -199,4 +211,43 @@ abstract class WxCustomOrderApiTestSupport extends AbstractApiTest {
|
|||||||
throw new AssertionError("优惠券未标记为已使用");
|
throw new AssertionError("优惠券未标记为已使用");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureDefaultClerkLevelBinding() {
|
||||||
|
ensureTenantContext();
|
||||||
|
PlayClerkUserInfoEntity clerk = clerkUserInfoService.selectById(ApiTestDataSeeder.DEFAULT_CLERK_ID);
|
||||||
|
if (!StringUtils.hasText(clerk.getLevelId())
|
||||||
|
|| !ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID.equals(clerk.getLevelId())) {
|
||||||
|
boolean updated = clerkUserInfoService.lambdaUpdate()
|
||||||
|
.set(PlayClerkUserInfoEntity::getLevelId, ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID)
|
||||||
|
.eq(PlayClerkUserInfoEntity::getId, ApiTestDataSeeder.DEFAULT_CLERK_ID)
|
||||||
|
.eq(PlayClerkUserInfoEntity::getTenantId, ApiTestDataSeeder.DEFAULT_TENANT_ID)
|
||||||
|
.update();
|
||||||
|
if (!updated) {
|
||||||
|
throw new IllegalStateException("ApiTest fixtures failed to bind default level to test clerk");
|
||||||
|
}
|
||||||
|
clerk = clerkUserInfoService.selectById(ApiTestDataSeeder.DEFAULT_CLERK_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
long pricingCount = commodityAndLevelInfoService.lambdaQuery()
|
||||||
|
.eq(PlayCommodityAndLevelInfoEntity::getCommodityId, ApiTestDataSeeder.DEFAULT_COMMODITY_ID)
|
||||||
|
.eq(PlayCommodityAndLevelInfoEntity::getLevelId, ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID)
|
||||||
|
.count();
|
||||||
|
if (pricingCount == 0) {
|
||||||
|
PlayCommodityAndLevelInfoEntity mapping = new PlayCommodityAndLevelInfoEntity();
|
||||||
|
mapping.setId(com.starry.common.utils.IdUtils.getUuid());
|
||||||
|
mapping.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||||
|
mapping.setCommodityId(ApiTestDataSeeder.DEFAULT_COMMODITY_ID);
|
||||||
|
mapping.setLevelId(ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID);
|
||||||
|
mapping.setPrice(ApiTestDataSeeder.DEFAULT_COMMODITY_PRICE);
|
||||||
|
mapping.setSort(1L);
|
||||||
|
commodityAndLevelInfoService.save(mapping);
|
||||||
|
pricingCount = commodityAndLevelInfoService.lambdaQuery()
|
||||||
|
.eq(PlayCommodityAndLevelInfoEntity::getCommodityId, ApiTestDataSeeder.DEFAULT_COMMODITY_ID)
|
||||||
|
.eq(PlayCommodityAndLevelInfoEntity::getLevelId, ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID)
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
if (pricingCount == 0) {
|
||||||
|
throw new IllegalStateException("ApiTest fixtures missing commodity pricing for default clerk level");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,52 @@ class WxCustomRandomOrderApiTest extends WxCustomOrderApiTestSupport {
|
|||||||
@org.springframework.beans.factory.annotation.Autowired
|
@org.springframework.beans.factory.annotation.Autowired
|
||||||
private com.starry.admin.modules.order.service.IPlayOrderInfoService orderInfoService;
|
private com.starry.admin.modules.order.service.IPlayOrderInfoService orderInfoService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void randomOrderFailsWhenBalanceInsufficient() throws Exception {
|
||||||
|
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||||
|
String remark = "API random insufficient " + IdUtils.getUuid();
|
||||||
|
try {
|
||||||
|
setCustomerBalance(BigDecimal.ZERO);
|
||||||
|
String customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
||||||
|
customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);
|
||||||
|
|
||||||
|
ensureTenantContext();
|
||||||
|
long beforeCount = playOrderInfoService.lambdaQuery()
|
||||||
|
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
|
||||||
|
.eq(PlayOrderInfoEntity::getPlaceType, OrderConstant.PlaceType.RANDOM.getCode())
|
||||||
|
.count();
|
||||||
|
|
||||||
|
String payload = "{" +
|
||||||
|
"\"sex\":\"2\"," +
|
||||||
|
"\"levelId\":\"" + ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID + "\"," +
|
||||||
|
"\"commodityId\":\"" + ApiTestDataSeeder.DEFAULT_COMMODITY_ID + "\"," +
|
||||||
|
"\"commodityQuantity\":1," +
|
||||||
|
"\"weiChatCode\":\"apitest-customer-wx\"," +
|
||||||
|
"\"excludeHistory\":\"0\"," +
|
||||||
|
"\"couponIds\":[]," +
|
||||||
|
"\"remark\":\"" + remark + "\"" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
mockMvc.perform(post("/wx/custom/order/random")
|
||||||
|
.header(USER_HEADER, DEFAULT_USER)
|
||||||
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||||
|
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(payload))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.code").value(998));
|
||||||
|
|
||||||
|
ensureTenantContext();
|
||||||
|
long afterCount = playOrderInfoService.lambdaQuery()
|
||||||
|
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
|
||||||
|
.eq(PlayOrderInfoEntity::getPlaceType, OrderConstant.PlaceType.RANDOM.getCode())
|
||||||
|
.count();
|
||||||
|
Assertions.assertThat(afterCount).isEqualTo(beforeCount);
|
||||||
|
} finally {
|
||||||
|
CustomSecurityContextHolder.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
// 测试用例:客户带随机下单请求命中默认等级与服务,接口应返回成功文案,
|
// 测试用例:客户带随机下单请求命中默认等级与服务,接口应返回成功文案,
|
||||||
// 并新增一条处于待接单状态的随机订单,金额、商品信息与 remark 与提交参数保持一致。
|
// 并新增一条处于待接单状态的随机订单,金额、商品信息与 remark 与提交参数保持一致。
|
||||||
|
|||||||
@@ -19,6 +19,48 @@ import org.springframework.http.MediaType;
|
|||||||
|
|
||||||
class WxCustomRewardOrderApiTest extends WxCustomOrderApiTestSupport {
|
class WxCustomRewardOrderApiTest extends WxCustomOrderApiTestSupport {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rewardOrderFailsWhenBalanceInsufficient() throws Exception {
|
||||||
|
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||||
|
String remark = "API reward insufficient " + IdUtils.getUuid();
|
||||||
|
try {
|
||||||
|
setCustomerBalance(BigDecimal.ZERO);
|
||||||
|
String customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
||||||
|
customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);
|
||||||
|
|
||||||
|
ensureTenantContext();
|
||||||
|
long beforeCount = playOrderInfoService.lambdaQuery()
|
||||||
|
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
|
||||||
|
.eq(PlayOrderInfoEntity::getPlaceType, OrderConstant.PlaceType.REWARD.getCode())
|
||||||
|
.count();
|
||||||
|
|
||||||
|
String payload = "{" +
|
||||||
|
"\"clerkId\":\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"," +
|
||||||
|
"\"money\":\"18.00\"," +
|
||||||
|
"\"weiChatCode\":\"apitest-customer-wx\"," +
|
||||||
|
"\"remark\":\"" + remark + "\"" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
mockMvc.perform(post("/wx/custom/order/reward")
|
||||||
|
.header(USER_HEADER, DEFAULT_USER)
|
||||||
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||||
|
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(payload))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.code").value(998));
|
||||||
|
|
||||||
|
ensureTenantContext();
|
||||||
|
long afterCount = playOrderInfoService.lambdaQuery()
|
||||||
|
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
|
||||||
|
.eq(PlayOrderInfoEntity::getPlaceType, OrderConstant.PlaceType.REWARD.getCode())
|
||||||
|
.count();
|
||||||
|
Assertions.assertThat(afterCount).isEqualTo(beforeCount);
|
||||||
|
} finally {
|
||||||
|
CustomSecurityContextHolder.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
// 测试用例:客户指定打赏金额下单时,应即时扣减账户余额、生成已完成的打赏订单并同步收益记录,
|
// 测试用例:客户指定打赏金额下单时,应即时扣减账户余额、生成已完成的打赏订单并同步收益记录,
|
||||||
// 同时校验订单归属陪玩师正确且金额与输入一致,确保余额打赏流程闭环。
|
// 同时校验订单归属陪玩师正确且金额与输入一致,确保余额打赏流程闭环。
|
||||||
|
|||||||
@@ -39,6 +39,55 @@ class WxCustomSpecifiedOrderApiTest extends WxCustomOrderApiTestSupport {
|
|||||||
@org.springframework.beans.factory.annotation.Autowired
|
@org.springframework.beans.factory.annotation.Autowired
|
||||||
private com.starry.admin.modules.weichat.service.WxTokenService clerkWxTokenService;
|
private com.starry.admin.modules.weichat.service.WxTokenService clerkWxTokenService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void specifiedOrderFailsWhenBalanceInsufficient() throws Exception {
|
||||||
|
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||||
|
String remark = "API specified insufficient " + IdUtils.getUuid();
|
||||||
|
try {
|
||||||
|
setCustomerBalance(BigDecimal.ZERO);
|
||||||
|
String customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
||||||
|
customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);
|
||||||
|
|
||||||
|
ensureTenantContext();
|
||||||
|
com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity clerk =
|
||||||
|
clerkUserInfoService.selectById(ApiTestDataSeeder.DEFAULT_CLERK_ID);
|
||||||
|
Assertions.assertThat(clerk.getLevelId())
|
||||||
|
.as("默认店员应绑定基础等级用于价格校验")
|
||||||
|
.isEqualTo(ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID);
|
||||||
|
|
||||||
|
ensureTenantContext();
|
||||||
|
long beforeCount = playOrderInfoService.lambdaQuery()
|
||||||
|
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
String payload = "{" +
|
||||||
|
"\"clerkId\":\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"," +
|
||||||
|
"\"commodityId\":\"" + ApiTestDataSeeder.DEFAULT_COMMODITY_ID + "\"," +
|
||||||
|
"\"commodityQuantity\":1," +
|
||||||
|
"\"weiChatCode\":\"apitest-customer-wx\"," +
|
||||||
|
"\"couponIds\":[]," +
|
||||||
|
"\"remark\":\"" + remark + "\"" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
mockMvc.perform(post("/wx/custom/order/commodity")
|
||||||
|
.header(USER_HEADER, DEFAULT_USER)
|
||||||
|
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||||
|
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(payload))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.code").value(998));
|
||||||
|
|
||||||
|
ensureTenantContext();
|
||||||
|
long afterCount = playOrderInfoService.lambdaQuery()
|
||||||
|
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
|
||||||
|
.count();
|
||||||
|
Assertions.assertThat(afterCount).isEqualTo(beforeCount);
|
||||||
|
} finally {
|
||||||
|
CustomSecurityContextHolder.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
// 测试用例:指定单取消后优惠券应恢复为未使用
|
// 测试用例:指定单取消后优惠券应恢复为未使用
|
||||||
void specifiedOrderCancellationReleasesCoupon() throws Exception {
|
void specifiedOrderCancellationReleasesCoupon() throws Exception {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ 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.OrderTriggerSource;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderType;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderType;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.PayMethod;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.PayMethod;
|
||||||
|
import com.starry.admin.modules.order.module.constant.OrderConstant.PaymentSource;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.PlaceType;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.PlaceType;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
|
||||||
import com.starry.admin.modules.order.module.constant.OrderConstant.RewardType;
|
import com.starry.admin.modules.order.module.constant.OrderConstant.RewardType;
|
||||||
@@ -45,6 +46,7 @@ import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
|||||||
import com.starry.admin.modules.order.module.vo.ClerkEstimatedRevenueVo;
|
import com.starry.admin.modules.order.module.vo.ClerkEstimatedRevenueVo;
|
||||||
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
|
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
|
||||||
import com.starry.admin.modules.order.service.support.ClerkRevenueCalculator;
|
import com.starry.admin.modules.order.service.support.ClerkRevenueCalculator;
|
||||||
|
import com.starry.admin.modules.personnel.service.IPlayBalanceDetailsInfoService;
|
||||||
import com.starry.admin.modules.shop.module.constant.CouponUseState;
|
import com.starry.admin.modules.shop.module.constant.CouponUseState;
|
||||||
import com.starry.admin.modules.shop.module.entity.PlayCouponDetailsEntity;
|
import com.starry.admin.modules.shop.module.entity.PlayCouponDetailsEntity;
|
||||||
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
|
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
|
||||||
@@ -101,6 +103,9 @@ class OrderLifecycleServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IPlayBalanceDetailsInfoService playBalanceDetailsInfoService;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void initStrategies() {
|
void initStrategies() {
|
||||||
lifecycleService.initPlacementStrategies();
|
lifecycleService.initPlacementStrategies();
|
||||||
@@ -1101,6 +1106,7 @@ private PlayOrderLogInfoMapper orderLogInfoMapper;
|
|||||||
customer.setId(order.getPurchaserBy());
|
customer.setId(order.getPurchaserBy());
|
||||||
customer.setAccountBalance(BigDecimal.valueOf(10));
|
customer.setAccountBalance(BigDecimal.valueOf(10));
|
||||||
when(customUserInfoService.getById(order.getPurchaserBy())).thenReturn(customer);
|
when(customUserInfoService.getById(order.getPurchaserBy())).thenReturn(customer);
|
||||||
|
when(playBalanceDetailsInfoService.existsCustomerConsumeRecord(order.getPurchaserBy(), orderId)).thenReturn(true);
|
||||||
|
|
||||||
OrderRefundContext context = new OrderRefundContext();
|
OrderRefundContext context = new OrderRefundContext();
|
||||||
context.setOrderId(orderId);
|
context.setOrderId(orderId);
|
||||||
@@ -1176,6 +1182,8 @@ private PlayOrderLogInfoMapper orderLogInfoMapper;
|
|||||||
context.setRefundAmount(BigDecimal.ONE);
|
context.setRefundAmount(BigDecimal.ONE);
|
||||||
context.withTriggerSource(OrderTriggerSource.ADMIN_API);
|
context.withTriggerSource(OrderTriggerSource.ADMIN_API);
|
||||||
|
|
||||||
|
when(playBalanceDetailsInfoService.existsCustomerConsumeRecord(order.getPurchaserBy(), orderId)).thenReturn(true);
|
||||||
|
|
||||||
lifecycleService.refundOrder(context);
|
lifecycleService.refundOrder(context);
|
||||||
|
|
||||||
verify(customUserInfoService, never()).updateAccountBalanceById(any(), any(), any(), any(), any(), any(), any(), any());
|
verify(customUserInfoService, never()).updateAccountBalanceById(any(), any(), any(), any(), any(), any(), any(), any());
|
||||||
@@ -1183,6 +1191,28 @@ private PlayOrderLogInfoMapper orderLogInfoMapper;
|
|||||||
verify(orderLogInfoMapper, never()).insert(any());
|
verify(orderLogInfoMapper, never()).insert(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void refundOrder_balanceWithoutConsumption_throws() {
|
||||||
|
String orderId = UUID.randomUUID().toString();
|
||||||
|
PlayOrderInfoEntity order = buildOrder(orderId, OrderStatus.ACCEPTED.getCode());
|
||||||
|
order.setFinalAmount(BigDecimal.TEN);
|
||||||
|
order.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||||
|
order.setPaymentSource(PaymentSource.BALANCE.getCode());
|
||||||
|
|
||||||
|
when(orderInfoMapper.selectById(orderId)).thenReturn(order);
|
||||||
|
when(playBalanceDetailsInfoService.existsCustomerConsumeRecord(order.getPurchaserBy(), orderId)).thenReturn(false);
|
||||||
|
|
||||||
|
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()).update(isNull(), any());
|
||||||
|
verify(customUserInfoService, never()).updateAccountBalanceById(any(), any(), any(), any(), any(), any(), any(), any());
|
||||||
|
verify(orderRefundInfoService, never()).add(anyString(), anyString(), anyString(), anyString(), anyString(), any(), anyString(), anyString(), anyString(), anyString(), anyString());
|
||||||
|
}
|
||||||
|
|
||||||
private PlayOrderInfoEntity buildOrder(String orderId, String status) {
|
private PlayOrderInfoEntity buildOrder(String orderId, String status) {
|
||||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity();
|
PlayOrderInfoEntity entity = new PlayOrderInfoEntity();
|
||||||
entity.setId(orderId);
|
entity.setId(orderId);
|
||||||
|
|||||||
Reference in New Issue
Block a user