重构订单下单逻辑,引入策略模式和命令模式
- 为OrderTriggerSource枚举添加详细注释说明 - 将IOrderLifecycleService接口的initiateOrder方法重构为placeOrder - 新增OrderPlacementCommand、OrderPlacementResult、OrderAmountBreakdown等DTO - 实现订单下单策略模式,支持不同类型订单的差异化处理 - 优化金额计算逻辑,完善优惠券折扣计算 - 改进余额扣减逻辑,增强异常处理 - 更新相关控制器使用新的下单接口 - 完善单元测试覆盖,确保代码质量
This commit is contained in:
@@ -418,14 +418,41 @@ public class OrderConstant {
|
||||
|
||||
@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;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.starry.admin.modules.order.module.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 订单金额拆分结果:原价、优惠、实付。
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor(staticName = "of")
|
||||
public class OrderAmountBreakdown {
|
||||
|
||||
private final BigDecimal grossAmount;
|
||||
private final BigDecimal discountAmount;
|
||||
private final BigDecimal netAmount;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.starry.admin.modules.order.module.dto;
|
||||
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* 下单指令,封装不同业务场景需要的参数。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class OrderPlacementCommand {
|
||||
|
||||
@NonNull
|
||||
private final OrderCreationContext orderContext;
|
||||
|
||||
private final String balanceOperationAction;
|
||||
|
||||
@Builder.Default
|
||||
private final boolean deductBalance = true;
|
||||
|
||||
private final PricingInput pricingInput;
|
||||
|
||||
/**
|
||||
* 计价所需的输入参数。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public static class PricingInput {
|
||||
|
||||
@NonNull
|
||||
private final BigDecimal unitPrice;
|
||||
|
||||
private final int quantity;
|
||||
|
||||
@Builder.Default
|
||||
private final List<String> couponIds = Collections.emptyList();
|
||||
|
||||
private final String commodityId;
|
||||
|
||||
@NonNull
|
||||
private final OrderConstant.PlaceType placeType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.starry.admin.modules.order.module.dto;
|
||||
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 订单下单结果,包含订单实体和金额拆分。
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor(staticName = "of")
|
||||
public class OrderPlacementResult {
|
||||
|
||||
private final PlayOrderInfoEntity order;
|
||||
private final OrderAmountBreakdown amountBreakdown;
|
||||
}
|
||||
@@ -2,12 +2,14 @@ package com.starry.admin.modules.order.service;
|
||||
|
||||
import com.starry.admin.modules.order.module.dto.OrderCompletionContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementResult;
|
||||
import com.starry.admin.modules.order.module.dto.OrderRefundContext;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
|
||||
public interface IOrderLifecycleService {
|
||||
|
||||
PlayOrderInfoEntity initiateOrder(OrderCreationContext context);
|
||||
OrderPlacementResult placeOrder(OrderPlacementCommand command);
|
||||
|
||||
void completeOrder(String orderId, OrderCompletionContext context);
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.starry.admin.modules.order.service.impl;
|
||||
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.order.module.dto.OrderAmountBreakdown;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementResult;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
|
||||
abstract class AbstractOrderPlacementStrategy implements OrderPlacementStrategy {
|
||||
|
||||
protected final OrderLifecycleServiceImpl service;
|
||||
|
||||
AbstractOrderPlacementStrategy(OrderLifecycleServiceImpl service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderPlacementResult place(OrderPlacementCommand command) {
|
||||
OrderCreationContext context = command.getOrderContext();
|
||||
OrderAmountBreakdown breakdown = handlePricing(command, context);
|
||||
|
||||
PaymentInfo paymentInfo = context.getPaymentInfo();
|
||||
if (paymentInfo == null) {
|
||||
throw new CustomException("支付信息不能为空");
|
||||
}
|
||||
|
||||
PlayOrderInfoEntity order = service.createOrderRecord(context);
|
||||
|
||||
if (command.isDeductBalance()) {
|
||||
service.deductCustomerBalance(
|
||||
context.getPurchaserBy(),
|
||||
service.normalizeMoney(paymentInfo.getFinalAmount()),
|
||||
command.getBalanceOperationAction(),
|
||||
order.getId());
|
||||
}
|
||||
|
||||
OrderAmountBreakdown amountBreakdown =
|
||||
breakdown != null ? breakdown : service.fallbackBreakdown(paymentInfo);
|
||||
return OrderPlacementResult.of(order, amountBreakdown);
|
||||
}
|
||||
|
||||
protected abstract OrderAmountBreakdown handlePricing(OrderPlacementCommand command, OrderCreationContext context);
|
||||
}
|
||||
@@ -6,10 +6,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.common.exception.ServiceException;
|
||||
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.mapper.PlayOrderLogInfoMapper;
|
||||
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.OrderActor;
|
||||
@@ -25,8 +27,11 @@ 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;
|
||||
import com.starry.admin.modules.order.module.dto.CommodityInfo;
|
||||
import com.starry.admin.modules.order.module.dto.OrderAmountBreakdown;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCompletionContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementResult;
|
||||
import com.starry.admin.modules.order.module.dto.OrderRefundContext;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.dto.RandomOrderRequirements;
|
||||
@@ -37,15 +42,24 @@ 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.shop.module.constant.CouponUseState;
|
||||
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.enums.CouponDiscountType;
|
||||
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
|
||||
import com.starry.admin.modules.shop.service.IPlayCouponInfoService;
|
||||
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 com.starry.common.utils.IdUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -56,6 +70,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
|
||||
private static final LifecycleToken LIFECYCLE_TOKEN = new LifecycleToken();
|
||||
private static final int MONEY_SCALE = 2;
|
||||
|
||||
private enum LifecycleOperation {
|
||||
CREATE,
|
||||
@@ -81,21 +96,91 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
@Resource
|
||||
private IPlayCouponDetailsService playCouponDetailsService;
|
||||
|
||||
@Resource
|
||||
private IPlayCouponInfoService playCouponInfoService;
|
||||
|
||||
@Resource
|
||||
private ClerkRevenueCalculator clerkRevenueCalculator;
|
||||
|
||||
@Resource
|
||||
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
||||
|
||||
private Map<StrategyKey, OrderPlacementStrategy> placementStrategies;
|
||||
|
||||
@PostConstruct
|
||||
void initPlacementStrategies() {
|
||||
Map<StrategyKey, OrderPlacementStrategy> strategies = new HashMap<>();
|
||||
|
||||
OrderPlacementStrategy specifiedStrategy = new ServiceOrderPlacementStrategy(this, OrderConstant.PlaceType.SPECIFIED);
|
||||
registerStrategy(strategies, OrderConstant.PlaceType.SPECIFIED, OrderConstant.RewardType.NOT_APPLICABLE, specifiedStrategy);
|
||||
|
||||
OrderPlacementStrategy randomStrategy = new ServiceOrderPlacementStrategy(this, OrderConstant.PlaceType.RANDOM);
|
||||
registerStrategy(strategies, OrderConstant.PlaceType.RANDOM, OrderConstant.RewardType.NOT_APPLICABLE, randomStrategy);
|
||||
|
||||
OrderPlacementStrategy otherStrategy = new OtherOrderPlacementStrategy(this);
|
||||
registerStrategy(strategies, OrderConstant.PlaceType.OTHER, OrderConstant.RewardType.NOT_APPLICABLE, otherStrategy);
|
||||
|
||||
OrderPlacementStrategy rewardGiftStrategy = new RewardGiftOrderPlacementStrategy(this);
|
||||
registerStrategy(strategies, OrderConstant.PlaceType.REWARD, OrderConstant.RewardType.GIFT, rewardGiftStrategy);
|
||||
|
||||
OrderPlacementStrategy rewardTipStrategy = new RewardTipOrderPlacementStrategy(this);
|
||||
registerStrategy(strategies, OrderConstant.PlaceType.REWARD, OrderConstant.RewardType.BALANCE, rewardTipStrategy);
|
||||
registerStrategy(strategies, OrderConstant.PlaceType.REWARD, OrderConstant.RewardType.NOT_APPLICABLE, rewardTipStrategy);
|
||||
|
||||
this.placementStrategies = strategies;
|
||||
}
|
||||
|
||||
private void registerStrategy(
|
||||
Map<StrategyKey, OrderPlacementStrategy> strategies,
|
||||
OrderConstant.PlaceType placeType,
|
||||
OrderConstant.RewardType rewardType,
|
||||
OrderPlacementStrategy strategy) {
|
||||
StrategyKey key = StrategyKey.of(placeType, rewardType);
|
||||
if (strategies.containsKey(key)) {
|
||||
throw new IllegalStateException(String.format("Duplicate order placement strategy for %s/%s", placeType, rewardType));
|
||||
}
|
||||
strategies.put(key, strategy);
|
||||
}
|
||||
|
||||
private boolean isRewardScenario(OrderCreationContext context) {
|
||||
return context.getPlaceType() == OrderConstant.PlaceType.REWARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public PlayOrderInfoEntity initiateOrder(OrderCreationContext context) {
|
||||
public OrderPlacementResult placeOrder(OrderPlacementCommand command) {
|
||||
if (command == null || command.getOrderContext() == null) {
|
||||
throw new CustomException("下单参数不能为空");
|
||||
}
|
||||
|
||||
OrderCreationContext context = command.getOrderContext();
|
||||
OrderConstant.PlaceType placeType = context.getPlaceType();
|
||||
log.info("placeOrder placeType={} rewardFlag? {}", placeType, isRewardScenario(context));
|
||||
if (placeType == null) {
|
||||
throw new CustomException("下单类型不能为空");
|
||||
}
|
||||
|
||||
OrderConstant.RewardType rewardType = context.getRewardType() != null
|
||||
? context.getRewardType()
|
||||
: OrderConstant.RewardType.NOT_APPLICABLE;
|
||||
OrderPlacementStrategy strategy = placementStrategies.get(StrategyKey.of(placeType, rewardType));
|
||||
if (strategy == null && rewardType != OrderConstant.RewardType.NOT_APPLICABLE) {
|
||||
strategy = placementStrategies.get(StrategyKey.of(placeType, OrderConstant.RewardType.NOT_APPLICABLE));
|
||||
}
|
||||
if (strategy == null) {
|
||||
throw new CustomException("暂不支持该类型的下单逻辑");
|
||||
}
|
||||
return strategy.place(command);
|
||||
}
|
||||
|
||||
PlayOrderInfoEntity createOrderRecord(OrderCreationContext context) {
|
||||
validateOrderCreationRequest(context);
|
||||
|
||||
PlayOrderInfoEntity entity = buildOrderEntity(context);
|
||||
applyRandomOrderRequirements(entity, context.getRandomOrderRequirements());
|
||||
applyAcceptByInfo(entity, context);
|
||||
if (context.isRewardOrder()) {
|
||||
boolean rewardScenario = isRewardScenario(context);
|
||||
if (rewardScenario) {
|
||||
applyRewardOrderDefaults(entity);
|
||||
}
|
||||
|
||||
@@ -111,7 +196,7 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
creationOperationType);
|
||||
updateCouponUsage(context.getPaymentInfo().getCouponIds());
|
||||
|
||||
if (context.isRewardOrder() && StrUtil.isNotBlank(context.getAcceptBy())) {
|
||||
if (rewardScenario && StrUtil.isNotBlank(context.getAcceptBy())) {
|
||||
completeOrder(
|
||||
entity.getId(),
|
||||
OrderCompletionContext.of(OrderActor.SYSTEM, null, OrderTriggerSource.REWARD_ORDER)
|
||||
@@ -121,6 +206,162 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
return entity;
|
||||
}
|
||||
|
||||
OrderAmountBreakdown calculateOrderAmounts(OrderPlacementCommand.PricingInput pricingInput) {
|
||||
int quantity = pricingInput.getQuantity();
|
||||
if (quantity <= 0) {
|
||||
throw new CustomException("数量不能为空");
|
||||
}
|
||||
BigDecimal normalizedUnitPrice = normalizeMoney(pricingInput.getUnitPrice());
|
||||
BigDecimal grossAmount = normalizeMoney(normalizedUnitPrice.multiply(BigDecimal.valueOf(quantity)));
|
||||
|
||||
List<String> couponIds = pricingInput.getCouponIds();
|
||||
if (CollectionUtil.isEmpty(couponIds)) {
|
||||
return OrderAmountBreakdown.of(grossAmount, BigDecimal.ZERO, grossAmount);
|
||||
}
|
||||
|
||||
BigDecimal discountTotal = BigDecimal.ZERO;
|
||||
for (String couponId : couponIds) {
|
||||
PlayCouponDetailsEntity couponDetails = playCouponDetailsService.getById(couponId);
|
||||
if (couponDetails == null) {
|
||||
throw new CustomException("优惠券不存在");
|
||||
}
|
||||
PlayCouponInfoEntity couponInfo = playCouponInfoService.selectPlayCouponInfoById(couponDetails.getCouponId());
|
||||
String reason = playCouponInfoService.getCouponReasonForUnavailableUse(
|
||||
couponInfo,
|
||||
pricingInput.getPlaceType().getCode(),
|
||||
pricingInput.getCommodityId(),
|
||||
quantity,
|
||||
normalizedUnitPrice);
|
||||
if (StrUtil.isNotBlank(reason)) {
|
||||
throw new CustomException("优惠券不可用 - " + reason);
|
||||
}
|
||||
|
||||
CouponDiscountType discountType;
|
||||
try {
|
||||
discountType = CouponDiscountType.fromCode(couponInfo.getDiscountType());
|
||||
} catch (CustomException ex) {
|
||||
log.warn("Unsupported coupon discount type {}, couponId={}", couponInfo.getDiscountType(), couponInfo.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (discountType) {
|
||||
case FULL_REDUCTION:
|
||||
discountTotal = discountTotal.add(normalizeMoney(couponInfo.getDiscountAmount()));
|
||||
break;
|
||||
case PERCENTAGE:
|
||||
discountTotal = discountTotal.add(calculatePercentageDiscount(grossAmount, couponInfo.getDiscountAmount(), couponInfo.getId()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal discountAmount = normalizeMoney(discountTotal.min(grossAmount));
|
||||
BigDecimal netAmount = normalizeMoney(grossAmount.subtract(discountAmount));
|
||||
return OrderAmountBreakdown.of(grossAmount, discountAmount, netAmount);
|
||||
}
|
||||
|
||||
void deductCustomerBalance(
|
||||
String customerId,
|
||||
BigDecimal netAmount,
|
||||
String operationAction,
|
||||
String orderId) {
|
||||
PlayCustomUserInfoEntity customer = customUserInfoService.selectById(customerId);
|
||||
if (customer == null) {
|
||||
throw new CustomException("顾客不存在");
|
||||
}
|
||||
BigDecimal before = normalizeMoney(customer.getAccountBalance());
|
||||
if (netAmount.compareTo(before) > 0) {
|
||||
throw new ServiceException("余额不足", 998);
|
||||
}
|
||||
BigDecimal after = normalizeMoney(before.subtract(netAmount));
|
||||
String action = StrUtil.isNotBlank(operationAction) ? operationAction : "下单";
|
||||
customUserInfoService.updateAccountBalanceById(
|
||||
customerId,
|
||||
before,
|
||||
after,
|
||||
BalanceOperationType.CONSUME.getCode(),
|
||||
action,
|
||||
netAmount,
|
||||
BigDecimal.ZERO,
|
||||
orderId);
|
||||
}
|
||||
|
||||
OrderAmountBreakdown fallbackBreakdown(PaymentInfo paymentInfo) {
|
||||
return OrderAmountBreakdown.of(
|
||||
normalizeMoney(paymentInfo.getOrderMoney()),
|
||||
normalizeMoney(paymentInfo.getDiscountAmount()),
|
||||
normalizeMoney(paymentInfo.getFinalAmount()));
|
||||
}
|
||||
|
||||
private static final class StrategyKey {
|
||||
|
||||
private final OrderConstant.PlaceType placeType;
|
||||
private final OrderConstant.RewardType rewardType;
|
||||
|
||||
private StrategyKey(OrderConstant.PlaceType placeType, OrderConstant.RewardType rewardType) {
|
||||
this.placeType = placeType;
|
||||
this.rewardType = rewardType;
|
||||
}
|
||||
|
||||
static StrategyKey of(OrderConstant.PlaceType placeType, OrderConstant.RewardType rewardType) {
|
||||
return new StrategyKey(placeType, rewardType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof StrategyKey)) {
|
||||
return false;
|
||||
}
|
||||
StrategyKey that = (StrategyKey) o;
|
||||
return placeType == that.placeType && rewardType == that.rewardType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(placeType, rewardType);
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal calculatePercentageDiscount(BigDecimal grossAmount, BigDecimal discountFold, String couponId) {
|
||||
if (discountFold == null) {
|
||||
log.warn("Percentage coupon has no discount amount configured, couponId={}", couponId);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
BigDecimal normalizedFold = discountFold.abs();
|
||||
// REVIEW THIS: WHY NORMALIZE this if < 10?
|
||||
if (normalizedFold.compareTo(BigDecimal.TEN) > 0) {
|
||||
return normalizeMoney(grossAmount);
|
||||
}
|
||||
BigDecimal rate;
|
||||
if (normalizedFold.compareTo(BigDecimal.ONE) <= 0) {
|
||||
rate = normalizedFold;
|
||||
} else if (normalizedFold.compareTo(BigDecimal.TEN) <= 0) {
|
||||
rate = normalizedFold.divide(BigDecimal.TEN, MONEY_SCALE + 2, RoundingMode.HALF_UP);
|
||||
} else if (normalizedFold.compareTo(BigDecimal.valueOf(100)) <= 0) {
|
||||
rate = normalizedFold.divide(BigDecimal.valueOf(100), MONEY_SCALE + 2, RoundingMode.HALF_UP);
|
||||
} else {
|
||||
log.warn("Percentage coupon rate out of range, couponId={}, value={}", couponId, discountFold);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
if (rate.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
log.warn("Percentage coupon rate <= 0, couponId={}", couponId);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
if (rate.compareTo(BigDecimal.ONE) > 0) {
|
||||
log.warn("Percentage coupon rate > 1 after normalisation, couponId={}", couponId);
|
||||
rate = BigDecimal.ONE;
|
||||
}
|
||||
|
||||
BigDecimal discount = grossAmount.multiply(BigDecimal.ONE.subtract(rate));
|
||||
return normalizeMoney(discount.max(BigDecimal.ZERO));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void completeOrder(String orderId, OrderCompletionContext context) {
|
||||
@@ -395,6 +636,13 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
playCouponDetailsService.updateCouponUseStateByIds(couponIds, CouponUseState.USED.getCode());
|
||||
}
|
||||
|
||||
BigDecimal normalizeMoney(BigDecimal amount) {
|
||||
if (amount == null) {
|
||||
return BigDecimal.ZERO.setScale(MONEY_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
return amount.setScale(MONEY_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private String resolvePayMethod(String payMethodCode) {
|
||||
if (StrUtil.isBlank(payMethodCode)) {
|
||||
return PayMethod.BALANCE.getCode();
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.starry.admin.modules.order.service.impl;
|
||||
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementResult;
|
||||
|
||||
interface OrderPlacementStrategy {
|
||||
|
||||
OrderPlacementResult place(OrderPlacementCommand command);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.starry.admin.modules.order.service.impl;
|
||||
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.order.module.dto.OrderAmountBreakdown;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
|
||||
class OtherOrderPlacementStrategy extends AbstractOrderPlacementStrategy {
|
||||
|
||||
OtherOrderPlacementStrategy(OrderLifecycleServiceImpl service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OrderAmountBreakdown handlePricing(OrderPlacementCommand command, OrderCreationContext context) {
|
||||
PaymentInfo info = context.getPaymentInfo();
|
||||
if (info == null) {
|
||||
throw new CustomException("支付信息不能为空");
|
||||
}
|
||||
context.setPaymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(service.normalizeMoney(info.getOrderMoney()))
|
||||
.discountAmount(service.normalizeMoney(info.getDiscountAmount()))
|
||||
.finalAmount(service.normalizeMoney(info.getFinalAmount()))
|
||||
.couponIds(info.getCouponIds())
|
||||
.payMethod(info.getPayMethod())
|
||||
.build());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.starry.admin.modules.order.service.impl;
|
||||
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.dto.OrderAmountBreakdown;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
|
||||
class RewardGiftOrderPlacementStrategy extends AbstractOrderPlacementStrategy {
|
||||
|
||||
RewardGiftOrderPlacementStrategy(OrderLifecycleServiceImpl service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OrderAmountBreakdown handlePricing(OrderPlacementCommand command, OrderCreationContext context) {
|
||||
if (context.getRewardType() != OrderConstant.RewardType.GIFT) {
|
||||
throw new CustomException("礼物订单类型不匹配");
|
||||
}
|
||||
|
||||
PaymentInfo normalized = ensurePaymentInfoPresent(context);
|
||||
return service.fallbackBreakdown(normalized);
|
||||
}
|
||||
|
||||
private PaymentInfo ensurePaymentInfoPresent(OrderCreationContext context) {
|
||||
PaymentInfo info = context.getPaymentInfo();
|
||||
if (info == null) {
|
||||
throw new CustomException("支付信息不能为空");
|
||||
}
|
||||
PaymentInfo normalized = PaymentInfo.builder()
|
||||
.orderMoney(service.normalizeMoney(info.getOrderMoney()))
|
||||
.discountAmount(service.normalizeMoney(info.getDiscountAmount()))
|
||||
.finalAmount(service.normalizeMoney(info.getFinalAmount()))
|
||||
.couponIds(info.getCouponIds())
|
||||
.payMethod(info.getPayMethod())
|
||||
.build();
|
||||
context.setPaymentInfo(normalized);
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.starry.admin.modules.order.service.impl;
|
||||
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.dto.OrderAmountBreakdown;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
|
||||
class RewardTipOrderPlacementStrategy extends AbstractOrderPlacementStrategy {
|
||||
|
||||
RewardTipOrderPlacementStrategy(OrderLifecycleServiceImpl service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OrderAmountBreakdown handlePricing(OrderPlacementCommand command, OrderCreationContext context) {
|
||||
if (context.getRewardType() == OrderConstant.RewardType.GIFT) {
|
||||
throw new CustomException("礼物订单应使用礼物策略");
|
||||
}
|
||||
PaymentInfo normalized = ensurePaymentInfoPresent(context);
|
||||
return service.fallbackBreakdown(normalized);
|
||||
}
|
||||
|
||||
private PaymentInfo ensurePaymentInfoPresent(OrderCreationContext context) {
|
||||
PaymentInfo info = context.getPaymentInfo();
|
||||
if (info == null) {
|
||||
throw new CustomException("支付信息不能为空");
|
||||
}
|
||||
PaymentInfo normalized = PaymentInfo.builder()
|
||||
.orderMoney(service.normalizeMoney(info.getOrderMoney()))
|
||||
.discountAmount(service.normalizeMoney(info.getDiscountAmount()))
|
||||
.finalAmount(service.normalizeMoney(info.getFinalAmount()))
|
||||
.couponIds(info.getCouponIds())
|
||||
.payMethod(info.getPayMethod())
|
||||
.build();
|
||||
context.setPaymentInfo(normalized);
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.starry.admin.modules.order.service.impl;
|
||||
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.dto.OrderAmountBreakdown;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
|
||||
class ServiceOrderPlacementStrategy extends AbstractOrderPlacementStrategy {
|
||||
|
||||
private final OrderConstant.PlaceType placeType;
|
||||
|
||||
ServiceOrderPlacementStrategy(OrderLifecycleServiceImpl service, OrderConstant.PlaceType placeType) {
|
||||
super(service);
|
||||
this.placeType = placeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OrderAmountBreakdown handlePricing(OrderPlacementCommand command, OrderCreationContext context) {
|
||||
if (context.getPlaceType() != placeType) {
|
||||
throw new CustomException("下单类型与服务计价策略不匹配");
|
||||
}
|
||||
|
||||
OrderPlacementCommand.PricingInput pricingInput = command.getPricingInput();
|
||||
if (pricingInput == null) {
|
||||
PaymentInfo info = context.getPaymentInfo();
|
||||
if (info == null) {
|
||||
throw new CustomException("支付信息不能为空");
|
||||
}
|
||||
PaymentInfo normalized = PaymentInfo.builder()
|
||||
.orderMoney(service.normalizeMoney(info.getOrderMoney()))
|
||||
.discountAmount(service.normalizeMoney(info.getDiscountAmount()))
|
||||
.finalAmount(service.normalizeMoney(info.getFinalAmount()))
|
||||
.couponIds(info.getCouponIds())
|
||||
.payMethod(info.getPayMethod())
|
||||
.build();
|
||||
context.setPaymentInfo(normalized);
|
||||
return service.fallbackBreakdown(normalized);
|
||||
}
|
||||
if (pricingInput.getPlaceType() != placeType) {
|
||||
throw new CustomException("计价参数的下单类型不匹配");
|
||||
}
|
||||
|
||||
OrderAmountBreakdown breakdown = service.calculateOrderAmounts(pricingInput);
|
||||
PaymentInfo currentInfo = context.getPaymentInfo();
|
||||
PaymentInfo updated = PaymentInfo.builder()
|
||||
.orderMoney(breakdown.getGrossAmount())
|
||||
.discountAmount(breakdown.getDiscountAmount())
|
||||
.finalAmount(breakdown.getNetAmount())
|
||||
.couponIds(pricingInput.getCouponIds())
|
||||
.payMethod(currentInfo != null ? currentInfo.getPayMethod() : null)
|
||||
.build();
|
||||
context.setPaymentInfo(updated);
|
||||
return breakdown;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.starry.admin.modules.shop.module.enums;
|
||||
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 优惠券折扣类型枚举。
|
||||
*/
|
||||
@Getter
|
||||
public enum CouponDiscountType {
|
||||
|
||||
/**
|
||||
* 满减券(固定金额抵扣)。
|
||||
*/
|
||||
FULL_REDUCTION("0"),
|
||||
|
||||
/**
|
||||
* 折扣券(按比例折扣)。暂未支持具体计算逻辑。
|
||||
*/
|
||||
PERCENTAGE("1");
|
||||
|
||||
private final String code;
|
||||
|
||||
CouponDiscountType(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static CouponDiscountType fromCode(String code) {
|
||||
for (CouponDiscountType value : values()) {
|
||||
if (value.code.equals(code)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new CustomException("不支持的优惠券折扣类型: " + code);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.starry.admin.common.aspect.CustomUserLogin;
|
||||
import com.starry.admin.common.conf.ThreadLocalRequestDetail;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.common.exception.ServiceException;
|
||||
import com.starry.admin.common.task.OverdueOrderHandlerTask;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkCommodityService;
|
||||
@@ -23,6 +22,8 @@ import com.starry.admin.modules.order.module.constant.OrderConstant.OrderActor;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.RewardType;
|
||||
import com.starry.admin.modules.order.module.dto.CommodityInfo;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementResult;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.dto.RandomOrderRequirements;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderComplaintInfoEntity;
|
||||
@@ -32,8 +33,6 @@ 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;
|
||||
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.vo.PlayCommodityInfoVo;
|
||||
import com.starry.admin.modules.shop.service.*;
|
||||
import com.starry.admin.modules.weichat.entity.*;
|
||||
@@ -63,12 +62,13 @@ import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -230,10 +230,6 @@ public class WxCustomController {
|
||||
public R rewardToOrder(@ApiParam(value = "打赏信息", required = true) @Validated @RequestBody PlayOrderInfoRewardAdd vo) {
|
||||
MoneyUtils.verificationTypeIsNormal(vo.getMoney());
|
||||
String userId = ThreadLocalRequestDetail.getCustomUserInfo().getId();
|
||||
PlayCustomUserInfoEntity customUserInfo = customUserInfoService.selectById(userId);
|
||||
if (new BigDecimal(vo.getMoney()).compareTo(customUserInfo.getAccountBalance()) > 0) {
|
||||
throw new ServiceException("余额不足", 998);
|
||||
}
|
||||
String orderId = IdUtils.getUuid();
|
||||
// 记录订单信息
|
||||
OrderCreationContext orderRequest = OrderCreationContext.builder()
|
||||
@@ -265,9 +261,10 @@ public class WxCustomController {
|
||||
.weiChatCode(vo.getWeiChatCode())
|
||||
.remark(vo.getRemark())
|
||||
.build();
|
||||
orderLifecycleService.initiateOrder(orderRequest);
|
||||
// 顾客减少余额
|
||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(), customUserInfo.getAccountBalance().subtract(new BigDecimal(vo.getMoney())), "1", "打赏", new BigDecimal(vo.getMoney()), BigDecimal.ZERO, orderId);
|
||||
orderLifecycleService.placeOrder(OrderPlacementCommand.builder()
|
||||
.orderContext(orderRequest)
|
||||
.balanceOperationAction("打赏")
|
||||
.build());
|
||||
return R.ok("成功");
|
||||
}
|
||||
|
||||
@@ -301,34 +298,6 @@ public class WxCustomController {
|
||||
|
||||
PlayCommodityInfoVo commodityInfo = playCommodityInfoService.queryCommodityInfo(vo.getCommodityId(), clerkUserInfo.getLevelId());
|
||||
|
||||
BigDecimal couponMoney = BigDecimal.ZERO;
|
||||
for (String couponId : vo.getCouponIds()) {
|
||||
PlayCouponDetailsEntity couponDetailsEntity = couponDetailsService.getById(couponId);
|
||||
if (Objects.isNull(couponDetailsEntity)) {
|
||||
throw new CustomException("优惠券不存在");
|
||||
}
|
||||
PlayCouponInfoEntity couponInfo = playCouponInfoService.selectPlayCouponInfoById(couponDetailsEntity.getCouponId());
|
||||
String couponReasonForUnavailableUse = playCouponInfoService.getCouponReasonForUnavailableUse(couponInfo, "0", vo.getCommodityId(), vo.getCommodityQuantity(), commodityInfo.getCommodityPrice());
|
||||
if (StrUtil.isNotBlank(couponReasonForUnavailableUse)) {
|
||||
throw new CustomException("优惠券不可用");
|
||||
}
|
||||
if (couponInfo.getDiscountType().equals("0")) {
|
||||
couponMoney = couponMoney.add(couponInfo.getDiscountAmount());
|
||||
}
|
||||
}
|
||||
|
||||
PlayCustomUserInfoEntity customUserInfo = customUserInfoService.selectById(customId);
|
||||
|
||||
BigDecimal money = commodityInfo.getCommodityPrice().multiply(new BigDecimal(vo.getCommodityQuantity()));
|
||||
|
||||
money = money.subtract(couponMoney);
|
||||
if (money.compareTo(BigDecimal.ZERO) < 1) {
|
||||
money = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
if (money.compareTo(customUserInfo.getAccountBalance()) > 0) {
|
||||
throw new ServiceException("余额不足", 998);
|
||||
}
|
||||
String orderId = IdUtils.getUuid();
|
||||
String orderNo = playOrderInfoService.getOrderNo();
|
||||
// 记录订单信息
|
||||
@@ -351,21 +320,39 @@ public class WxCustomController {
|
||||
.commodityNumber(String.valueOf(vo.getCommodityQuantity()))
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(money)
|
||||
.finalAmount(money)
|
||||
.orderMoney(BigDecimal.ZERO)
|
||||
.finalAmount(BigDecimal.ZERO)
|
||||
.discountAmount(BigDecimal.ZERO)
|
||||
.couponIds(vo.getCouponIds())
|
||||
.couponIds(Collections.emptyList())
|
||||
.build())
|
||||
.purchaserBy(customId)
|
||||
.acceptBy(clerkUserInfo.getId())
|
||||
.weiChatCode(vo.getWeiChatCode())
|
||||
.remark(vo.getRemark())
|
||||
.build();
|
||||
orderLifecycleService.initiateOrder(orderRequest);
|
||||
// 顾客减少余额
|
||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(), customUserInfo.getAccountBalance().subtract(money), "1", "下单-指定单", money, BigDecimal.ZERO, orderId);
|
||||
List<String> couponIds = CollectionUtils.isEmpty(vo.getCouponIds()) ? Collections.emptyList() : vo.getCouponIds();
|
||||
OrderPlacementCommand command = OrderPlacementCommand.builder()
|
||||
.orderContext(orderRequest)
|
||||
.balanceOperationAction("下单-指定单")
|
||||
.pricingInput(OrderPlacementCommand.PricingInput.builder()
|
||||
.unitPrice(commodityInfo.getCommodityPrice())
|
||||
.quantity(vo.getCommodityQuantity())
|
||||
.couponIds(couponIds)
|
||||
.commodityId(commodityInfo.getCommodityId())
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.build())
|
||||
.build();
|
||||
OrderPlacementResult result = orderLifecycleService.placeOrder(command);
|
||||
PlayOrderInfoEntity order = result.getOrder();
|
||||
BigDecimal netAmount = result.getAmountBreakdown().getNetAmount();
|
||||
// 发送通知给店员
|
||||
wxCustomMpService.sendCreateOrderMessage(clerkUserInfo.getTenantId(), clerkUserInfo.getOpenid(), orderNo, money.toString(), commodityInfo.getCommodityName(),orderId );
|
||||
wxCustomMpService.sendCreateOrderMessage(
|
||||
clerkUserInfo.getTenantId(),
|
||||
clerkUserInfo.getOpenid(),
|
||||
orderNo,
|
||||
netAmount.toString(),
|
||||
commodityInfo.getCommodityName(),
|
||||
order.getId());
|
||||
return R.ok("成功");
|
||||
}
|
||||
|
||||
@@ -380,12 +367,7 @@ public class WxCustomController {
|
||||
public R randomToOrdder(@Validated @RequestBody PlayOrderInfoRandomAdd vo) {
|
||||
// 验证当前顾客余额是否充足
|
||||
String customId = ThreadLocalRequestDetail.getCustomUserInfo().getId();
|
||||
PlayCustomUserInfoEntity customUserInfo = customUserInfoService.selectById(customId);
|
||||
PlayCommodityInfoVo commodityInfo = playCommodityInfoService.queryCommodityInfo(vo.getCommodityId(), vo.getLevelId());
|
||||
BigDecimal money = commodityInfo.getCommodityPrice().multiply(new BigDecimal(vo.getCommodityQuantity()));
|
||||
if (money.compareTo(customUserInfo.getAccountBalance()) > 0) {
|
||||
throw new ServiceException("余额不足", 998);
|
||||
}
|
||||
String orderId = IdUtils.getUuid();
|
||||
String orderNo = playOrderInfoService.getOrderNo();
|
||||
// 记录订单信息
|
||||
@@ -408,10 +390,10 @@ public class WxCustomController {
|
||||
.commodityNumber(String.valueOf(vo.getCommodityQuantity()))
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(money)
|
||||
.finalAmount(money)
|
||||
.orderMoney(BigDecimal.ZERO)
|
||||
.finalAmount(BigDecimal.ZERO)
|
||||
.discountAmount(BigDecimal.ZERO)
|
||||
.couponIds(vo.getCouponIds())
|
||||
.couponIds(Collections.emptyList())
|
||||
.build())
|
||||
.purchaserBy(customId)
|
||||
.weiChatCode(vo.getWeiChatCode())
|
||||
@@ -422,13 +404,28 @@ public class WxCustomController {
|
||||
.excludeHistory(vo.getExcludeHistory())
|
||||
.build())
|
||||
.build();
|
||||
orderLifecycleService.initiateOrder(orderRequest);
|
||||
// 顾客减少余额
|
||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(), customUserInfo.getAccountBalance().subtract(money), "1", "下单-随机单", money, BigDecimal.ZERO, orderId);
|
||||
List<String> couponIds = CollectionUtils.isEmpty(vo.getCouponIds()) ? Collections.emptyList() : vo.getCouponIds();
|
||||
OrderPlacementCommand command = OrderPlacementCommand.builder()
|
||||
.orderContext(orderRequest)
|
||||
.balanceOperationAction("下单-随机单")
|
||||
.pricingInput(OrderPlacementCommand.PricingInput.builder()
|
||||
.unitPrice(commodityInfo.getCommodityPrice())
|
||||
.quantity(vo.getCommodityQuantity())
|
||||
.couponIds(couponIds)
|
||||
.commodityId(commodityInfo.getCommodityId())
|
||||
.placeType(OrderConstant.PlaceType.RANDOM)
|
||||
.build())
|
||||
.build();
|
||||
OrderPlacementResult result = orderLifecycleService.placeOrder(command);
|
||||
PlayOrderInfoEntity order = result.getOrder();
|
||||
BigDecimal netAmount = result.getAmountBreakdown().getNetAmount();
|
||||
// 给全部店员发送通知
|
||||
List<PlayClerkUserInfoEntity> clerkList = clerkUserInfoService.list(Wrappers.lambdaQuery(PlayClerkUserInfoEntity.class).isNotNull(PlayClerkUserInfoEntity::getOpenid).eq(PlayClerkUserInfoEntity::getClerkState, "1")
|
||||
.eq(PlayClerkUserInfoEntity::getOnlineState, "1").eq(PlayClerkUserInfoEntity::getSex, vo.getSex()));
|
||||
wxCustomMpService.sendCreateOrderMessageBatch(clerkList, orderNo, money.toString(), commodityInfo.getCommodityName(),orderId);
|
||||
List<PlayClerkUserInfoEntity> clerkList = clerkUserInfoService.list(Wrappers.lambdaQuery(PlayClerkUserInfoEntity.class)
|
||||
.isNotNull(PlayClerkUserInfoEntity::getOpenid)
|
||||
.eq(PlayClerkUserInfoEntity::getClerkState, "1")
|
||||
.eq(PlayClerkUserInfoEntity::getOnlineState, "1")
|
||||
.eq(PlayClerkUserInfoEntity::getSex, vo.getSex()));
|
||||
wxCustomMpService.sendCreateOrderMessageBatch(clerkList, orderNo, netAmount.toString(), commodityInfo.getCommodityName(),order.getId());
|
||||
// 记录订单,指定指定未接单后,进行退款处理
|
||||
overdueOrderHandlerTask.enqueue(orderId + "_" + SecurityUtils.getTenantId());
|
||||
// 下单成功后,先根据用户条件进行随机分配
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.starry.admin.modules.weichat.service;
|
||||
|
||||
import com.starry.admin.common.conf.ThreadLocalRequestDetail;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.common.exception.ServiceException;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomGiftInfoService;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
@@ -11,7 +10,10 @@ import com.starry.admin.modules.order.module.constant.OrderConstant.OrderActor;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.RewardType;
|
||||
import com.starry.admin.modules.order.module.dto.CommodityInfo;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementCommand;
|
||||
import com.starry.admin.modules.order.module.dto.OrderPlacementResult;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.service.IOrderLifecycleService;
|
||||
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
|
||||
import com.starry.admin.modules.shop.module.entity.PlayGiftInfoEntity;
|
||||
@@ -79,27 +81,21 @@ public class WxGiftOrderService {
|
||||
throw new CustomException("礼物金额必须大于0");
|
||||
}
|
||||
|
||||
BigDecimal currentBalance = customUserInfo.getAccountBalance() == null
|
||||
? BigDecimal.ZERO : customUserInfo.getAccountBalance();
|
||||
if (totalAmount.compareTo(currentBalance) > 0) {
|
||||
throw new ServiceException("余额不足", 998);
|
||||
}
|
||||
|
||||
String orderId = IdUtils.getUuid();
|
||||
OrderCreationContext orderRequest = buildOrderCreationContext(orderId, request, sessionUser.getId(),
|
||||
giftInfo, totalAmount);
|
||||
orderLifecycleService.initiateOrder(orderRequest);
|
||||
|
||||
BigDecimal newBalance = currentBalance.subtract(totalAmount);
|
||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), currentBalance, newBalance, "1",
|
||||
"赠送礼物", totalAmount, BigDecimal.ZERO, orderId);
|
||||
OrderPlacementResult result = orderLifecycleService.placeOrder(OrderPlacementCommand.builder()
|
||||
.orderContext(orderRequest)
|
||||
.balanceOperationAction("赠送礼物")
|
||||
.build());
|
||||
PlayOrderInfoEntity order = result.getOrder();
|
||||
|
||||
String tenantId = customUserInfo.getTenantId();
|
||||
long delta = giftQuantity;
|
||||
playCustomGiftInfoService.incrementGiftCount(customUserInfo.getId(), request.getGiftId(), tenantId, delta);
|
||||
playClerkGiftInfoService.incrementGiftCount(request.getClerkId(), request.getGiftId(), tenantId, delta);
|
||||
|
||||
return orderId;
|
||||
return order.getId();
|
||||
}
|
||||
|
||||
private OrderCreationContext buildOrderCreationContext(String orderId, PlayOrderInfoGiftAdd request,
|
||||
|
||||
Reference in New Issue
Block a user