This commit is contained in:
@@ -134,7 +134,16 @@ public class PlayClerkUserInfoServiceImpl extends ServiceImpl<PlayClerkUserInfoM
|
||||
lambdaWrapper.leftJoin(PlayClerkLevelInfoEntity.class, PlayClerkLevelInfoEntity::getId,
|
||||
PlayClerkUserInfoEntity::getLevelId);
|
||||
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.OrdersExpiredState;
|
||||
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.ReviewRequirement;
|
||||
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.IPlayOrderRefundInfoService;
|
||||
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.entity.PlayCouponDetailsEntity;
|
||||
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
|
||||
@@ -105,6 +107,9 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
@Resource
|
||||
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
||||
|
||||
@Resource
|
||||
private IPlayBalanceDetailsInfoService playBalanceDetailsInfoService;
|
||||
|
||||
private Map<StrategyKey, OrderPlacementStrategy> placementStrategies;
|
||||
|
||||
@PostConstruct
|
||||
@@ -515,6 +520,10 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
throw new CustomException("每个订单只能退款一次~");
|
||||
}
|
||||
|
||||
if (isBalancePaidOrder(order) && !playBalanceDetailsInfoService.existsCustomerConsumeRecord(order.getPurchaserBy(), order.getId())) {
|
||||
throw new CustomException("订单未发生余额扣款,无法退款");
|
||||
}
|
||||
|
||||
UpdateWrapper<PlayOrderInfoEntity> refundUpdate = new UpdateWrapper<>();
|
||||
refundUpdate.eq("id", order.getId())
|
||||
.eq("refund_type", OrderRefundFlag.NOT_REFUNDED.getCode())
|
||||
@@ -581,6 +590,19 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
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) {
|
||||
if (context == null) {
|
||||
throw new CustomException("订单创建请求不能为空");
|
||||
|
||||
@@ -37,6 +37,13 @@ public class ClerkRevenueCalculator {
|
||||
BigDecimal baseAmount = orderAmount == null ? BigDecimal.ZERO : orderAmount;
|
||||
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;
|
||||
OrderConstant.PlaceType placeTypeEnum;
|
||||
try {
|
||||
@@ -49,13 +56,13 @@ public class ClerkRevenueCalculator {
|
||||
|
||||
switch (placeTypeEnum) {
|
||||
case SPECIFIED: // 指定单
|
||||
fillRegularOrderRevenue(firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
fillRegularOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
break;
|
||||
case RANDOM: // 随机单
|
||||
fillRandomOrderRevenue(firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
fillRandomOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
break;
|
||||
case REWARD: // 打赏单
|
||||
fillRewardOrderRevenue(firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
fillRewardOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo);
|
||||
break;
|
||||
case OTHER:
|
||||
default:
|
||||
@@ -71,42 +78,56 @@ public class ClerkRevenueCalculator {
|
||||
return estimatedRevenueVo;
|
||||
}
|
||||
|
||||
private void fillRegularOrderRevenue(String firstOrder, BigDecimal orderAmount,
|
||||
private void fillRegularOrderRevenue(String clerkId, String firstOrder, BigDecimal orderAmount,
|
||||
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
||||
if ("1".equals(firstOrder)) {
|
||||
vo.setRevenueRatio(levelInfo.getFirstRegularRatio());
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getFirstRegularRatio()));
|
||||
int ratio = safeRatio(levelInfo.getFirstRegularRatio(), "firstRegularRatio", clerkId);
|
||||
vo.setRevenueRatio(ratio);
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||
} else {
|
||||
vo.setRevenueRatio(levelInfo.getNotFirstRegularRatio());
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getNotFirstRegularRatio()));
|
||||
int ratio = safeRatio(levelInfo.getNotFirstRegularRatio(), "notFirstRegularRatio", clerkId);
|
||||
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) {
|
||||
if ("1".equals(firstOrder)) {
|
||||
vo.setRevenueRatio(levelInfo.getFirstRandomRadio());
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getFirstRandomRadio()));
|
||||
int ratio = safeRatio(levelInfo.getFirstRandomRadio(), "firstRandomRatio", clerkId);
|
||||
vo.setRevenueRatio(ratio);
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||
} else {
|
||||
vo.setRevenueRatio(levelInfo.getNotFirstRandomRadio());
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getNotFirstRandomRadio()));
|
||||
int ratio = safeRatio(levelInfo.getNotFirstRandomRadio(), "notFirstRandomRatio", clerkId);
|
||||
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) {
|
||||
if ("1".equals(firstOrder)) {
|
||||
vo.setRevenueRatio(levelInfo.getFirstRewardRatio());
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getFirstRewardRatio()));
|
||||
int ratio = safeRatio(levelInfo.getFirstRewardRatio(), "firstRewardRatio", clerkId);
|
||||
vo.setRevenueRatio(ratio);
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, ratio));
|
||||
} else {
|
||||
vo.setRevenueRatio(levelInfo.getNotFirstRewardRatio());
|
||||
vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getNotFirstRewardRatio()));
|
||||
int ratio = safeRatio(levelInfo.getNotFirstRewardRatio(), "notFirstRewardRatio", clerkId);
|
||||
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
|
||||
.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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 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.service.impl.ServiceImpl;
|
||||
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.order.module.constant.OrderConstant.BalanceOperationType;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||
import com.starry.admin.modules.personnel.mapper.PlayBalanceDetailsInfoMapper;
|
||||
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.PlayBalanceDetailsReturnVo;
|
||||
import com.starry.admin.modules.personnel.service.IPlayBalanceDetailsInfoService;
|
||||
@@ -114,7 +117,12 @@ public class PlayBalanceDetailsInfoServiceImpl
|
||||
public void insertBalanceDetailsInfo(String userType, String userId, BigDecimal balanceBeforeOperation,
|
||||
BigDecimal balanceAfterOperation, String operationType, String operationAction, BigDecimal balanceMoney,
|
||||
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();
|
||||
entity.setId(IdUtils.getUuid());
|
||||
entity.setUserType(userType);
|
||||
@@ -180,4 +188,15 @@ public class PlayBalanceDetailsInfoServiceImpl
|
||||
public int deletePlayBalanceDetailsInfoById(String 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user