feat: 完善订单生命周期幂等与日志追踪
This commit is contained in:
@@ -2,6 +2,10 @@ package com.starry.admin.modules.custom.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
/**
|
||||
* 顾客Mapper接口
|
||||
@@ -11,4 +15,18 @@ import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
*/
|
||||
public interface PlayCustomUserInfoMapper extends MPJBaseMapper<PlayCustomUserInfoEntity> {
|
||||
|
||||
@Update({
|
||||
"<script>",
|
||||
"UPDATE play_custom_user_info",
|
||||
"SET accumulated_consumption_amount = COALESCE(accumulated_consumption_amount, 0) + #{consumptionDelta},",
|
||||
" last_purchase_time = #{completionTime},",
|
||||
" first_purchase_time = CASE WHEN first_purchase_time IS NULL THEN #{completionTime} ELSE first_purchase_time END",
|
||||
" <if test='weiChatCode != null and weiChatCode != \"\"'>, wei_chat_code = #{weiChatCode}</if>",
|
||||
"WHERE id = #{userId}",
|
||||
"</script>"
|
||||
})
|
||||
int applyOrderCompletionUpdate(@Param("userId") String userId,
|
||||
@Param("consumptionDelta") BigDecimal consumptionDelta,
|
||||
@Param("completionTime") Date completionTime,
|
||||
@Param("weiChatCode") String weiChatCode);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.starry.admin.modules.custom.service;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomLevelInfoEntity;
|
||||
import com.starry.admin.modules.system.module.entity.SysTenantEntity;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -100,4 +101,13 @@ public interface IPlayCustomLevelInfoService extends IService<PlayCustomLevelInf
|
||||
* 删除最大等级
|
||||
*/
|
||||
void delMaxLevelByLevel(Integer level);
|
||||
|
||||
/**
|
||||
* 根据累计消费金额匹配顾客等级。
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param totalConsumption 累计消费金额
|
||||
* @return 匹配到的等级,未匹配则返回 {@code null}
|
||||
*/
|
||||
PlayCustomLevelInfoEntity matchLevelByConsumption(String tenantId, BigDecimal totalConsumption);
|
||||
}
|
||||
|
||||
@@ -172,4 +172,11 @@ public interface IPlayCustomUserInfoService extends IService<PlayCustomUserInfoE
|
||||
* @author admin
|
||||
**/
|
||||
void saveOrderInfo(PlayOrderInfoEntity entity);
|
||||
|
||||
/**
|
||||
* 处理订单完成后的顾客统计更新。
|
||||
*
|
||||
* @param entity 完成的订单实体
|
||||
*/
|
||||
void handleOrderCompletion(PlayOrderInfoEntity entity);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ import com.starry.admin.modules.custom.module.entity.PlayCustomLevelInfoEntity;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomLevelInfoService;
|
||||
import com.starry.admin.modules.system.module.entity.SysTenantEntity;
|
||||
import com.starry.common.utils.IdUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -153,4 +155,39 @@ public class PlayCustomLevelInfoServiceImpl extends ServiceImpl<PlayCustomLevelI
|
||||
queryWrapper.eq(PlayCustomLevelInfoEntity::getLevel, level);
|
||||
this.baseMapper.delete(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayCustomLevelInfoEntity matchLevelByConsumption(String tenantId, BigDecimal totalConsumption) {
|
||||
BigDecimal consumption = Objects.requireNonNullElse(totalConsumption, BigDecimal.ZERO);
|
||||
LambdaQueryWrapper<PlayCustomLevelInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.orderByAsc(PlayCustomLevelInfoEntity::getLevel);
|
||||
if (StrUtil.isNotBlank(tenantId)) {
|
||||
lambdaQueryWrapper.eq(PlayCustomLevelInfoEntity::getTenantId, tenantId);
|
||||
}
|
||||
List<PlayCustomLevelInfoEntity> levels = this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
if (levels == null || levels.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
PlayCustomLevelInfoEntity matched = null;
|
||||
for (PlayCustomLevelInfoEntity level : levels) {
|
||||
BigDecimal threshold = parseConsumptionAmount(level.getConsumptionAmount());
|
||||
if (consumption.compareTo(threshold) >= 0) {
|
||||
matched = level;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return matched != null ? matched : levels.get(0);
|
||||
}
|
||||
|
||||
private BigDecimal parseConsumptionAmount(String rawValue) {
|
||||
if (StrUtil.isBlank(rawValue)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(rawValue.trim());
|
||||
} catch (NumberFormatException ex) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.starry.admin.modules.custom.module.vo.PlayCustomRankingQueryVo;
|
||||
import com.starry.admin.modules.custom.module.vo.PlayCustomRankingReturnVo;
|
||||
import com.starry.admin.modules.custom.module.vo.PlayCustomUserQueryVo;
|
||||
import com.starry.admin.modules.custom.module.vo.PlayCustomUserReturnVo;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomLevelInfoService;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.service.impl.PlayOrderInfoServiceImpl;
|
||||
@@ -23,6 +24,8 @@ import com.starry.admin.modules.personnel.service.IPlayBalanceDetailsInfoService
|
||||
import com.starry.common.utils.IdUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Resource;
|
||||
@@ -49,6 +52,9 @@ public class PlayCustomUserInfoServiceImpl extends ServiceImpl<PlayCustomUserInf
|
||||
@Resource
|
||||
private IPlayBalanceDetailsInfoService playBalanceDetailsInfoService;
|
||||
|
||||
@Resource
|
||||
private IPlayCustomLevelInfoService playCustomLevelInfoService;
|
||||
|
||||
@Override
|
||||
public PlayCustomUserInfoEntity selectByOpenid(String openId) {
|
||||
LambdaQueryWrapper<PlayCustomUserInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
@@ -369,6 +375,50 @@ public class PlayCustomUserInfoServiceImpl extends ServiceImpl<PlayCustomUserInf
|
||||
return baseMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleOrderCompletion(PlayOrderInfoEntity entity) {
|
||||
if (entity == null || StrUtil.isBlank(entity.getPurchaserBy())) {
|
||||
return;
|
||||
}
|
||||
PlayCustomUserInfoEntity userInfo = playCustomUserInfoMapper.selectById(entity.getPurchaserBy());
|
||||
if (userInfo == null) {
|
||||
log.warn("handleOrderCompletion skipped, userId={} missing, orderId={}", entity.getPurchaserBy(), entity.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal finalAmount = Objects.requireNonNullElse(entity.getFinalAmount(), BigDecimal.ZERO);
|
||||
Date completionTime = resolveCompletionTime(entity.getOrderEndTime());
|
||||
|
||||
int affected = playCustomUserInfoMapper.applyOrderCompletionUpdate(
|
||||
userInfo.getId(),
|
||||
finalAmount,
|
||||
completionTime,
|
||||
entity.getWeiChatCode());
|
||||
if (affected == 0) {
|
||||
log.warn("handleOrderCompletion update skipped for userId={}, orderId={}", userInfo.getId(), entity.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
PlayCustomUserInfoEntity latest = playCustomUserInfoMapper.selectById(userInfo.getId());
|
||||
if (latest == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlayCustomLevelInfoEntity matchedLevel = playCustomLevelInfoService
|
||||
.matchLevelByConsumption(latest.getTenantId(), latest.getAccumulatedConsumptionAmount());
|
||||
if (matchedLevel != null && !StrUtil.equals(matchedLevel.getId(), latest.getLevelId())) {
|
||||
this.update(Wrappers.<PlayCustomUserInfoEntity>lambdaUpdate()
|
||||
.eq(PlayCustomUserInfoEntity::getId, latest.getId())
|
||||
.set(PlayCustomUserInfoEntity::getLevelId, matchedLevel.getId()));
|
||||
log.info("顾客{}消费累计达到{},自动调整等级为{}", latest.getId(), latest.getAccumulatedConsumptionAmount(), matchedLevel.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private Date resolveCompletionTime(LocalDateTime orderEndTime) {
|
||||
LocalDateTime time = orderEndTime != null ? orderEndTime : LocalDateTime.now();
|
||||
return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveOrderInfo(PlayOrderInfoEntity entity) {
|
||||
String id = entity.getPurchaserBy();
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.starry.admin.modules.order.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderLogInfoEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface PlayOrderLogInfoMapper extends BaseMapper<PlayOrderLogInfoEntity> {
|
||||
}
|
||||
@@ -207,6 +207,14 @@ public class OrderConstant {
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public enum OrderActor {
|
||||
CUSTOMER,
|
||||
CLERK,
|
||||
ADMIN,
|
||||
SYSTEM;
|
||||
}
|
||||
|
||||
// 排除历史记录常量
|
||||
public static final String EXCLUDE_HISTORY_NO = "0";
|
||||
public static final String EXCLUDE_HISTORY_YES = "1";
|
||||
@@ -320,6 +328,93 @@ public class OrderConstant {
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public enum YesNoFlag {
|
||||
NO("0"),
|
||||
YES("1");
|
||||
|
||||
private final String code;
|
||||
|
||||
YesNoFlag(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static YesNoFlag fromCode(String code) {
|
||||
for (YesNoFlag flag : values()) {
|
||||
if (flag.code.equals(code)) {
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown yes/no flag code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public enum OrderSettlementState {
|
||||
NOT_SETTLED("0"),
|
||||
SETTLED("1");
|
||||
|
||||
private final String code;
|
||||
|
||||
OrderSettlementState(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static OrderSettlementState fromCode(String code) {
|
||||
for (OrderSettlementState state : values()) {
|
||||
if (state.code.equals(code)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown settlement state code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public enum OrdersExpiredState {
|
||||
NOT_EXPIRED("0"),
|
||||
EXPIRED("1");
|
||||
|
||||
private final String code;
|
||||
|
||||
OrdersExpiredState(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static OrdersExpiredState fromCode(String code) {
|
||||
for (OrdersExpiredState state : values()) {
|
||||
if (state.code.equals(code)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown orders expired state code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public enum PayMethod {
|
||||
BALANCE("0"),
|
||||
WECHAT("1"),
|
||||
ALIPAY("2"),
|
||||
BANK_CARD("3"),
|
||||
OTHER("4");
|
||||
|
||||
private final String code;
|
||||
|
||||
PayMethod(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static PayMethod fromCode(String code) {
|
||||
for (PayMethod method : values()) {
|
||||
if (method.code.equals(code)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown pay method code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
public enum OrderTriggerSource {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.starry.admin.modules.order.module.dto;
|
||||
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderActor;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
|
||||
import java.util.Objects;
|
||||
import lombok.Data;
|
||||
@@ -10,13 +11,11 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
@Data
|
||||
public class OrderCompletionContext {
|
||||
/**
|
||||
* 操作人类型(0:顾客;1:店员;2:管理员),可为空用于系统任务。
|
||||
*/
|
||||
@Nullable
|
||||
private String operatorType;
|
||||
|
||||
/** 操作人ID,可为空用于系统任务。 */
|
||||
/** 操作人类型,系统任务使用 SYSTEM。 */
|
||||
private OrderActor operatorActor = OrderActor.SYSTEM;
|
||||
|
||||
/** 操作人ID,SYSTEM 时允许为空。 */
|
||||
@Nullable
|
||||
private String operatorId;
|
||||
|
||||
@@ -30,26 +29,29 @@ public class OrderCompletionContext {
|
||||
/** 是否强制发送完成通知。 */
|
||||
private boolean forceNotify;
|
||||
|
||||
public static OrderCompletionContext of(@Nullable String operatorType, @Nullable String operatorId, OrderTriggerSource triggerSource) {
|
||||
public static OrderCompletionContext of(OrderActor actor, @Nullable String operatorId, OrderTriggerSource triggerSource) {
|
||||
Objects.requireNonNull(actor, "operator actor cannot be null");
|
||||
Objects.requireNonNull(triggerSource, "triggerSource cannot be null");
|
||||
if (actor != OrderActor.SYSTEM && operatorId == null) {
|
||||
throw new IllegalArgumentException("operatorId is required for actor " + actor);
|
||||
}
|
||||
OrderCompletionContext context = new OrderCompletionContext();
|
||||
context.setOperatorType(operatorType);
|
||||
context.setOperatorActor(actor);
|
||||
context.setOperatorId(operatorId);
|
||||
context.setTriggerSource(triggerSource);
|
||||
return context;
|
||||
}
|
||||
|
||||
public static OrderCompletionContext of(@Nullable String operatorType, @Nullable String operatorId, OrderTriggerSource triggerSource, @Nullable String comment) {
|
||||
OrderCompletionContext context = of(operatorType, operatorId, triggerSource);
|
||||
return context.withComment(comment);
|
||||
public static OrderCompletionContext of(OrderActor actor, @Nullable String operatorId, OrderTriggerSource triggerSource, @Nullable String comment) {
|
||||
return of(actor, operatorId, triggerSource).withComment(comment);
|
||||
}
|
||||
|
||||
public static OrderCompletionContext scheduler(@Nullable String comment) {
|
||||
return of(null, null, OrderTriggerSource.SCHEDULER, comment);
|
||||
return of(OrderActor.SYSTEM, null, OrderTriggerSource.SCHEDULER, comment);
|
||||
}
|
||||
|
||||
public static OrderCompletionContext system(OrderTriggerSource triggerSource, @Nullable String comment) {
|
||||
return of(null, null, triggerSource, comment);
|
||||
return of(OrderActor.SYSTEM, null, triggerSource, comment);
|
||||
}
|
||||
|
||||
public OrderCompletionContext withForceNotify(boolean forceNotify) {
|
||||
|
||||
@@ -1,127 +1,77 @@
|
||||
package com.starry.admin.modules.order.module.dto;
|
||||
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.RewardType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderActor;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* 订单创建请求对象 - 使用Builder模式替换20+参数的方法
|
||||
*
|
||||
* @author admin
|
||||
* 订单创建上下文,用于聚合下单所需的全部信息。
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class OrderCreationRequest {
|
||||
public class OrderCreationContext {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
@NotBlank(message = "订单ID不能为空")
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
@NotBlank(message = "订单编号不能为空")
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 订单状态
|
||||
*/
|
||||
@NotNull(message = "订单状态不能为空")
|
||||
private OrderConstant.OrderStatus orderStatus;
|
||||
|
||||
/**
|
||||
* 订单类型
|
||||
*/
|
||||
@NotNull(message = "订单类型不能为空")
|
||||
private OrderConstant.OrderType orderType;
|
||||
|
||||
/**
|
||||
* 下单类型
|
||||
*/
|
||||
@NotNull(message = "下单类型不能为空")
|
||||
private OrderConstant.PlaceType placeType;
|
||||
|
||||
/**
|
||||
* 打赏类型(0:余额;1:礼物)
|
||||
*/
|
||||
private RewardType rewardType;
|
||||
private OrderConstant.RewardType rewardType;
|
||||
|
||||
/**
|
||||
* 是否是首单
|
||||
*/
|
||||
private boolean isFirstOrder;
|
||||
|
||||
/**
|
||||
* 商品信息
|
||||
*/
|
||||
@Valid
|
||||
@NotNull(message = "商品信息不能为空")
|
||||
private CommodityInfo commodityInfo;
|
||||
|
||||
/**
|
||||
* 支付信息
|
||||
*/
|
||||
@Valid
|
||||
@NotNull(message = "支付信息不能为空")
|
||||
private PaymentInfo paymentInfo;
|
||||
|
||||
/**
|
||||
* 下单人
|
||||
*/
|
||||
@NotBlank(message = "下单人不能为空")
|
||||
private String purchaserBy;
|
||||
|
||||
/**
|
||||
* 接单人(可选)
|
||||
*/
|
||||
private String acceptBy;
|
||||
|
||||
/**
|
||||
* 微信号码
|
||||
*/
|
||||
private String weiChatCode;
|
||||
|
||||
/**
|
||||
* 订单备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 随机单要求(仅随机单时需要)
|
||||
*/
|
||||
private RandomOrderRequirements randomOrderRequirements;
|
||||
|
||||
/**
|
||||
* 获取首单标识字符串(兼容现有系统)
|
||||
*/
|
||||
@Builder.Default
|
||||
private OrderActor creatorActor = OrderActor.SYSTEM;
|
||||
|
||||
@Nullable
|
||||
private String creatorId;
|
||||
|
||||
public String getFirstOrderString() {
|
||||
return isFirstOrder ? "1" : "0";
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证随机单要求
|
||||
*/
|
||||
public boolean isValidForRandomOrder() {
|
||||
return placeType == OrderConstant.PlaceType.RANDOM
|
||||
&& randomOrderRequirements != null;
|
||||
return placeType == OrderConstant.PlaceType.RANDOM && randomOrderRequirements != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为打赏单
|
||||
*/
|
||||
public boolean isRewardOrder() {
|
||||
return placeType == OrderConstant.PlaceType.REWARD;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为指定单
|
||||
*/
|
||||
public boolean isSpecifiedOrder() {
|
||||
return placeType == OrderConstant.PlaceType.SPECIFIED;
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.starry.admin.common.conf.StringTypeHandler;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
|
||||
import com.starry.admin.modules.order.service.impl.OrderLifecycleServiceImpl;
|
||||
import com.starry.common.domain.BaseEntity;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -329,4 +331,31 @@ public class PlayOrderInfoEntity extends BaseEntity<PlayOrderInfoEntity> {
|
||||
public void setOrderEndTime(LocalDateTime orderEndTime) {
|
||||
this.orderEndTime = orderEndTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新订单生命周期状态,仅允许订单生命周期服务进行访问。
|
||||
*
|
||||
* @param token 授权令牌
|
||||
* @param orderStatus 新的订单状态
|
||||
* @param orderEndTime 可选的订单结束时间
|
||||
*/
|
||||
public void updateOrderStatus(OrderLifecycleServiceImpl.LifecycleToken token, OrderStatus orderStatus) {
|
||||
if (token == null) {
|
||||
throw new IllegalStateException("Lifecycle token is required");
|
||||
}
|
||||
if (orderStatus == null) {
|
||||
throw new IllegalArgumentException("orderStatus cannot be null");
|
||||
}
|
||||
this.orderStatus = orderStatus.getCode();
|
||||
}
|
||||
|
||||
public void updateOrderEndTime(OrderLifecycleServiceImpl.LifecycleToken token, LocalDateTime orderEndTime) {
|
||||
if (token == null) {
|
||||
throw new IllegalStateException("Lifecycle token is required");
|
||||
}
|
||||
if (orderEndTime == null) {
|
||||
throw new IllegalArgumentException("orderEndTime cannot be null");
|
||||
}
|
||||
this.orderEndTime = orderEndTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.starry.admin.modules.order.module.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.starry.common.domain.BaseEntity;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/**
|
||||
* 订单生命周期日志实体。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("play_order_log_info")
|
||||
public class PlayOrderLogInfoEntity extends BaseEntity<PlayOrderLogInfoEntity> {
|
||||
|
||||
@TableId
|
||||
private String id;
|
||||
|
||||
private String tenantId;
|
||||
|
||||
private String orderId;
|
||||
|
||||
private String operationType;
|
||||
|
||||
private String operatorType;
|
||||
|
||||
private String operatorId;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime operTime;
|
||||
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -1,10 +1,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.OrderRefundContext;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
|
||||
public interface IOrderLifecycleService {
|
||||
|
||||
PlayOrderInfoEntity initiateOrder(OrderCreationContext context);
|
||||
|
||||
void completeOrder(String orderId, OrderCompletionContext context);
|
||||
|
||||
void refundOrder(OrderRefundContext context);
|
||||
|
||||
@@ -45,46 +45,6 @@ public interface IPlayOrderInfoService extends IService<PlayOrderInfoEntity> {
|
||||
* @author admin
|
||||
* @since 2024/6/3 10:53
|
||||
**/
|
||||
void createOrderInfo(OrderCreationRequest request);
|
||||
|
||||
/**
|
||||
* 新增订单信息 - 旧版本方法(已废弃,建议使用OrderCreationRequest)
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @param orderNo 订单编号
|
||||
* @param orderState 订单状态【0:已下单(待接单);1:已接单(待开始);2:已开始(服务中);3;已完成:4:已取消】
|
||||
* @param orderType 订单类型【-1:退款订单;0:充值订单;1:提现订单;2:普通订单】
|
||||
* @param placeType 下单类型(-1:其他类型;0:指定单;1:随机单;2:打赏单)
|
||||
* @param rewardType (0:余额;1:礼物)
|
||||
* @param firstOrder 是否是首单【0:不是,1:是】
|
||||
* @param commodityId 商品ID
|
||||
* @param commodityType 商品类型[0:礼物,1:服务]
|
||||
* @param commodityPrice 商品属性-商品单价
|
||||
* @param serviceDuration 商品属性-服务时长
|
||||
* @param commodityName 商品名称
|
||||
* @param commodityNumber 商品数量
|
||||
* @param orderMoney 订单金额
|
||||
* @param finalAmount 订单最终金额(支付金额)
|
||||
* @param discountAmount 优惠金额
|
||||
* @param purchaserBy 下单人
|
||||
* @param acceptBy 接单人
|
||||
* @param weiChatCode 订单微信号码
|
||||
* @param couponIds 优惠券ID列表
|
||||
* @param remark 订单备注
|
||||
* @param clerkSex 随机单要求-店员性别(0:未知;1:男;2:女)
|
||||
* @param clerkLevelId 随机单要求-店员等级ID
|
||||
* @param excludeHistory 随机单要求-是否排除下单过的成员(0:不排除;1:排除)
|
||||
* @author admin
|
||||
* @since 2024/6/3 10:53
|
||||
* @deprecated 请使用 {@link #createOrderInfo(OrderCreationRequest)} 替代
|
||||
**/
|
||||
@Deprecated
|
||||
void createOrderInfo(String orderId, String orderNo, String orderState, String orderType, String placeType,
|
||||
String rewardType, String firstOrder, String commodityId, String commodityType, BigDecimal commodityPrice,
|
||||
String serviceDuration, String commodityName, String commodityNumber, BigDecimal orderMoney,
|
||||
BigDecimal finalAmount, BigDecimal discountAmount, String purchaserBy, String acceptBy, String weiChatCode,
|
||||
List<String> couponIds, String remark, String clerkSex, String clerkLevelId, String excludeHistory);
|
||||
|
||||
/**
|
||||
* 据店员等级和订单金额,获取店员预计收入
|
||||
*
|
||||
|
||||
@@ -1,29 +1,51 @@
|
||||
package com.starry.admin.modules.order.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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.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.BalanceOperationType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderActor;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundFlag;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundRecordType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundState;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderSettlementState;
|
||||
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.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.OrderCompletionContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
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;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderLogInfoEntity;
|
||||
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.shop.module.constant.CouponUseState;
|
||||
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
|
||||
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.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -33,6 +55,14 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Service
|
||||
public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
|
||||
private static final LifecycleToken LIFECYCLE_TOKEN = new LifecycleToken();
|
||||
|
||||
private enum LifecycleOperation {
|
||||
CREATE,
|
||||
COMPLETE,
|
||||
REFUND
|
||||
}
|
||||
|
||||
@Resource
|
||||
private PlayOrderInfoMapper orderInfoMapper;
|
||||
|
||||
@@ -48,6 +78,49 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
@Resource
|
||||
private IPlayCustomUserInfoService customUserInfoService;
|
||||
|
||||
@Resource
|
||||
private IPlayCouponDetailsService playCouponDetailsService;
|
||||
|
||||
@Resource
|
||||
private ClerkRevenueCalculator clerkRevenueCalculator;
|
||||
|
||||
@Resource
|
||||
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public PlayOrderInfoEntity initiateOrder(OrderCreationContext context) {
|
||||
validateOrderCreationRequest(context);
|
||||
|
||||
PlayOrderInfoEntity entity = buildOrderEntity(context);
|
||||
applyRandomOrderRequirements(entity, context.getRandomOrderRequirements());
|
||||
applyAcceptByInfo(entity, context);
|
||||
if (context.isRewardOrder()) {
|
||||
applyRewardOrderDefaults(entity);
|
||||
}
|
||||
|
||||
customUserInfoService.saveOrderInfo(entity);
|
||||
orderInfoMapper.insert(entity);
|
||||
String creationOperationType = resolveCreationOperationType(context.getCreatorActor());
|
||||
recordOrderLog(
|
||||
entity,
|
||||
context.getCreatorActor(),
|
||||
context.getCreatorId(),
|
||||
LifecycleOperation.CREATE,
|
||||
context.getRemark(),
|
||||
creationOperationType);
|
||||
updateCouponUsage(context.getPaymentInfo().getCouponIds());
|
||||
|
||||
if (context.isRewardOrder() && StrUtil.isNotBlank(context.getAcceptBy())) {
|
||||
completeOrder(
|
||||
entity.getId(),
|
||||
OrderCompletionContext.of(OrderActor.SYSTEM, null, OrderTriggerSource.REWARD_ORDER)
|
||||
.withForceNotify(true)
|
||||
.withComment("auto-complete reward order"));
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void completeOrder(String orderId, OrderCompletionContext context) {
|
||||
@@ -71,17 +144,34 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime endTime = order.getOrderEndTime() != null ? order.getOrderEndTime() : now;
|
||||
|
||||
boolean statusUpdated = false;
|
||||
boolean transitioned = false;
|
||||
if (!alreadyCompleted) {
|
||||
PlayOrderInfoEntity update = new PlayOrderInfoEntity(orderId, OrderStatus.COMPLETED.getCode());
|
||||
update.setOrderEndTime(endTime);
|
||||
orderInfoMapper.updateById(update);
|
||||
statusUpdated = true;
|
||||
} else if (order.getOrderEndTime() == null) {
|
||||
PlayOrderInfoEntity update = new PlayOrderInfoEntity(orderId, OrderStatus.COMPLETED.getCode());
|
||||
update.setOrderEndTime(endTime);
|
||||
orderInfoMapper.updateById(update);
|
||||
statusUpdated = true;
|
||||
UpdateWrapper<PlayOrderInfoEntity> transitionWrapper = new UpdateWrapper<>();
|
||||
transitionWrapper.eq("id", orderId)
|
||||
.eq("order_status", OrderStatus.IN_PROGRESS.getCode())
|
||||
.set("order_status", OrderStatus.COMPLETED.getCode())
|
||||
.set("order_end_time", endTime);
|
||||
transitioned = orderInfoMapper.update(null, transitionWrapper) > 0;
|
||||
if (!transitioned) {
|
||||
PlayOrderInfoEntity refreshed = orderInfoMapper.selectById(orderId);
|
||||
if (refreshed == null) {
|
||||
throw new CustomException("订单不存在");
|
||||
}
|
||||
if (!OrderStatus.COMPLETED.getCode().equals(refreshed.getOrderStatus())) {
|
||||
log.warn("Failed to transition order {} to completed, current status {}", orderId, refreshed.getOrderStatus());
|
||||
throw new CustomException("订单状态异常,无法完成");
|
||||
}
|
||||
order = refreshed;
|
||||
alreadyCompleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (alreadyCompleted && order.getOrderEndTime() == null) {
|
||||
UpdateWrapper<PlayOrderInfoEntity> endTimeWrapper = new UpdateWrapper<>();
|
||||
endTimeWrapper.eq("id", orderId)
|
||||
.isNull("order_end_time")
|
||||
.set("order_end_time", endTime);
|
||||
orderInfoMapper.update(null, endTimeWrapper);
|
||||
}
|
||||
|
||||
PlayOrderInfoEntity latest = orderInfoMapper.selectById(orderId);
|
||||
@@ -89,9 +179,34 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
throw new CustomException("订单不存在");
|
||||
}
|
||||
|
||||
boolean earningsCreated = ensureEarnings(latest, source);
|
||||
boolean forceNotify = context != null && context.isForceNotify();
|
||||
boolean shouldNotify = statusUpdated || (forceNotify && earningsCreated);
|
||||
boolean earningsCreated = false;
|
||||
boolean shouldNotify = false;
|
||||
|
||||
boolean completionLogged = hasLifecycleLog(orderId, LifecycleOperation.COMPLETE);
|
||||
boolean shouldApplyCompletion = transitioned;
|
||||
if (!shouldApplyCompletion && forceNotify) {
|
||||
shouldApplyCompletion = !completionLogged;
|
||||
}
|
||||
|
||||
if (shouldApplyCompletion) {
|
||||
customUserInfoService.handleOrderCompletion(latest);
|
||||
earningsCreated = ensureEarnings(latest, source);
|
||||
shouldNotify = true;
|
||||
OrderActor actor = context != null ? context.getOperatorActor() : OrderActor.SYSTEM;
|
||||
String operationType = resolveCompletionOperationType(context, transitioned);
|
||||
recordOrderLog(
|
||||
latest,
|
||||
actor,
|
||||
context != null ? context.getOperatorId() : null,
|
||||
LifecycleOperation.COMPLETE,
|
||||
context != null ? context.getComment() : null,
|
||||
operationType);
|
||||
} else if (forceNotify) {
|
||||
earningsCreated = ensureEarnings(latest, source);
|
||||
shouldNotify = earningsCreated || OrderStatus.COMPLETED.getCode().equals(latest.getOrderStatus());
|
||||
}
|
||||
|
||||
if (shouldNotify) {
|
||||
wxCustomMpService.sendOrderFinishMessageAsync(latest);
|
||||
}
|
||||
@@ -128,12 +243,22 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
throw new CustomException("每个订单只能退款一次~");
|
||||
}
|
||||
|
||||
PlayOrderInfoEntity update = new PlayOrderInfoEntity();
|
||||
update.setId(order.getId());
|
||||
update.setRefundType(OrderRefundFlag.REFUNDED.getCode());
|
||||
update.setOrderStatus(OrderStatus.CANCELLED.getCode());
|
||||
update.setRefundAmount(refundAmount);
|
||||
orderInfoMapper.updateById(update);
|
||||
UpdateWrapper<PlayOrderInfoEntity> refundUpdate = new UpdateWrapper<>();
|
||||
refundUpdate.eq("id", order.getId())
|
||||
.eq("refund_type", OrderRefundFlag.NOT_REFUNDED.getCode())
|
||||
.set("refund_type", OrderRefundFlag.REFUNDED.getCode())
|
||||
.set("refund_amount", refundAmount)
|
||||
.set("refund_reason", context.getRefundReason())
|
||||
.set("order_status", OrderStatus.CANCELLED.getCode());
|
||||
boolean refundApplied = orderInfoMapper.update(null, refundUpdate) > 0;
|
||||
if (!refundApplied) {
|
||||
PlayOrderInfoEntity latest = orderInfoMapper.selectById(order.getId());
|
||||
if (latest != null && OrderRefundFlag.REFUNDED.getCode().equals(latest.getRefundType())) {
|
||||
log.info("Refund already processed for order {}, skipping duplicate credit", order.getId());
|
||||
return;
|
||||
}
|
||||
throw new CustomException("订单已处理或状态异常,无法退款");
|
||||
}
|
||||
|
||||
PlayCustomUserInfoEntity customUser = customUserInfoService.getById(order.getPurchaserBy());
|
||||
if (customUser == null) {
|
||||
@@ -171,6 +296,132 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
refundById,
|
||||
OrderRefundState.PROCESSING.getCode(),
|
||||
ReviewRequirement.NOT_REQUIRED.getCode());
|
||||
|
||||
PlayOrderInfoEntity latest = orderInfoMapper.selectById(order.getId());
|
||||
|
||||
String refundOperationType = String.format("%s_%s", LifecycleOperation.REFUND.name(), refundRecordType.name());
|
||||
recordOrderLog(
|
||||
latest != null ? latest : order,
|
||||
resolveCompletionActor(context.getOperatorType()),
|
||||
context.getOperatorId(),
|
||||
LifecycleOperation.REFUND,
|
||||
context.getRefundReason(),
|
||||
refundOperationType);
|
||||
}
|
||||
|
||||
private void validateOrderCreationRequest(OrderCreationContext context) {
|
||||
if (context == null) {
|
||||
throw new CustomException("订单创建请求不能为空");
|
||||
}
|
||||
if (context.getPlaceType() == PlaceType.RANDOM && !context.isValidForRandomOrder()) {
|
||||
throw new CustomException("随机单必须提供店员要求信息");
|
||||
}
|
||||
}
|
||||
|
||||
private PlayOrderInfoEntity buildOrderEntity(OrderCreationContext context) {
|
||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity();
|
||||
entity.setId(context.getOrderId());
|
||||
entity.setOrderNo(context.getOrderNo());
|
||||
entity.updateOrderStatus(LIFECYCLE_TOKEN, context.getOrderStatus());
|
||||
entity.setOrderType(context.getOrderType().getCode());
|
||||
entity.setPlaceType(context.getPlaceType().getCode());
|
||||
entity.setRewardType(context.getRewardType().getCode());
|
||||
entity.setFirstOrder(resolveFirstOrderFlag(context));
|
||||
entity.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||
entity.setBackendEntry(YesNoFlag.NO.getCode());
|
||||
entity.setOrderSettlementState(OrderSettlementState.NOT_SETTLED.getCode());
|
||||
entity.setOrdersExpiredState(OrdersExpiredState.NOT_EXPIRED.getCode());
|
||||
|
||||
CommodityInfo commodityInfo = context.getCommodityInfo();
|
||||
entity.setCommodityId(commodityInfo.getCommodityId());
|
||||
entity.setCommodityType(commodityInfo.getCommodityType().getCode());
|
||||
entity.setCommodityPrice(commodityInfo.getCommodityPrice());
|
||||
entity.setServiceDuration(commodityInfo.getServiceDuration());
|
||||
entity.setCommodityName(commodityInfo.getCommodityName());
|
||||
entity.setCommodityNumber(commodityInfo.getCommodityNumber());
|
||||
|
||||
PaymentInfo paymentInfo = context.getPaymentInfo();
|
||||
entity.setOrderMoney(paymentInfo.getOrderMoney());
|
||||
entity.setFinalAmount(paymentInfo.getFinalAmount());
|
||||
entity.setDiscountAmount(paymentInfo.getDiscountAmount());
|
||||
entity.setCouponIds(paymentInfo.getCouponIds());
|
||||
entity.setUseCoupon(CollectionUtil.isNotEmpty(paymentInfo.getCouponIds())
|
||||
? YesNoFlag.YES.getCode()
|
||||
: YesNoFlag.NO.getCode());
|
||||
entity.setPayMethod(resolvePayMethod(paymentInfo.getPayMethod()));
|
||||
|
||||
entity.setPurchaserBy(context.getPurchaserBy());
|
||||
entity.setPurchaserTime(LocalDateTime.now());
|
||||
entity.setWeiChatCode(context.getWeiChatCode());
|
||||
entity.setRemark(context.getRemark());
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void applyRandomOrderRequirements(PlayOrderInfoEntity entity, RandomOrderRequirements requirements) {
|
||||
if (requirements == null) {
|
||||
return;
|
||||
}
|
||||
entity.setSex(requirements.getClerkGender().getCode());
|
||||
entity.setLevelId(requirements.getClerkLevelId());
|
||||
entity.setExcludeHistory(requirements.getExcludeHistory());
|
||||
}
|
||||
|
||||
private void applyAcceptByInfo(PlayOrderInfoEntity entity, OrderCreationContext context) {
|
||||
if (StrUtil.isBlank(context.getAcceptBy())) {
|
||||
return;
|
||||
}
|
||||
entity.setAcceptBy(context.getAcceptBy());
|
||||
ClerkEstimatedRevenueVo estimatedRevenue = clerkRevenueCalculator.calculateEstimatedRevenue(
|
||||
context.getAcceptBy(),
|
||||
context.getPaymentInfo().getCouponIds(),
|
||||
context.getPlaceType().getCode(),
|
||||
entity.getFirstOrder(),
|
||||
context.getPaymentInfo().getFinalAmount());
|
||||
entity.setEstimatedRevenue(estimatedRevenue.getRevenueAmount());
|
||||
entity.setEstimatedRevenueRatio(estimatedRevenue.getRevenueRatio());
|
||||
}
|
||||
|
||||
private void applyRewardOrderDefaults(PlayOrderInfoEntity entity) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
entity.setAcceptTime(now);
|
||||
entity.setOrderStartTime(now);
|
||||
entity.setOrderEndTime(now);
|
||||
}
|
||||
|
||||
private void updateCouponUsage(List<String> couponIds) {
|
||||
if (CollectionUtil.isEmpty(couponIds)) {
|
||||
return;
|
||||
}
|
||||
playCouponDetailsService.updateCouponUseStateByIds(couponIds, CouponUseState.USED.getCode());
|
||||
}
|
||||
|
||||
private String resolvePayMethod(String payMethodCode) {
|
||||
if (StrUtil.isBlank(payMethodCode)) {
|
||||
return PayMethod.BALANCE.getCode();
|
||||
}
|
||||
try {
|
||||
return PayMethod.fromCode(payMethodCode).getCode();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
log.warn("Unknown pay method code {}, fallback to BALANCE", payMethodCode);
|
||||
return PayMethod.BALANCE.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveFirstOrderFlag(OrderCreationContext context) {
|
||||
if (StrUtil.isBlank(context.getAcceptBy()) || StrUtil.isBlank(context.getPurchaserBy())) {
|
||||
return context.getFirstOrderString();
|
||||
}
|
||||
return isFirstOrder(context.getPurchaserBy(), context.getAcceptBy())
|
||||
? YesNoFlag.YES.getCode()
|
||||
: YesNoFlag.NO.getCode();
|
||||
}
|
||||
|
||||
private boolean isFirstOrder(String customerId, String clerkId) {
|
||||
LambdaQueryWrapper<PlayOrderInfoEntity> wrapper = Wrappers.lambdaQuery(PlayOrderInfoEntity.class)
|
||||
.eq(PlayOrderInfoEntity::getPurchaserBy, customerId)
|
||||
.eq(PlayOrderInfoEntity::getAcceptBy, clerkId)
|
||||
.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.COMPLETED.getCode());
|
||||
return orderInfoMapper.selectCount(wrapper) == 0;
|
||||
}
|
||||
|
||||
private boolean ensureEarnings(PlayOrderInfoEntity order, OrderTriggerSource source) {
|
||||
@@ -192,4 +443,77 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean hasLifecycleLog(String orderId, LifecycleOperation operation) {
|
||||
if (StrUtil.isBlank(orderId)) {
|
||||
return false;
|
||||
}
|
||||
return orderLogInfoMapper.selectCount(Wrappers.<PlayOrderLogInfoEntity>lambdaQuery()
|
||||
.eq(PlayOrderLogInfoEntity::getOrderId, orderId)
|
||||
.likeRight(PlayOrderLogInfoEntity::getOperationType, operation.name())) > 0;
|
||||
}
|
||||
|
||||
private String resolveCreationOperationType(OrderActor creatorActor) {
|
||||
String actorSegment = creatorActor != null ? creatorActor.name() : OrderActor.SYSTEM.name();
|
||||
return String.format("%s_%s", LifecycleOperation.CREATE.name(), actorSegment);
|
||||
}
|
||||
|
||||
private String resolveCompletionOperationType(OrderCompletionContext context, boolean transitioned) {
|
||||
OrderActor operatorActor = context != null ? context.getOperatorActor() : OrderActor.SYSTEM;
|
||||
String actorSegment = operatorActor != null ? operatorActor.name() : OrderActor.SYSTEM.name();
|
||||
if (transitioned) {
|
||||
return String.format("%s_%s", LifecycleOperation.COMPLETE.name(), actorSegment);
|
||||
}
|
||||
if (context != null && context.isForceNotify()) {
|
||||
return String.format("%s_FORCE_%s", LifecycleOperation.COMPLETE.name(), actorSegment);
|
||||
}
|
||||
return LifecycleOperation.COMPLETE.name();
|
||||
}
|
||||
|
||||
public static final class LifecycleToken {
|
||||
private LifecycleToken() {}
|
||||
}
|
||||
|
||||
private void recordOrderLog(PlayOrderInfoEntity order,
|
||||
OrderActor actor,
|
||||
String operatorId,
|
||||
LifecycleOperation operation,
|
||||
String remark,
|
||||
String operationType) {
|
||||
if (order == null || StrUtil.isBlank(order.getId())) {
|
||||
return;
|
||||
}
|
||||
PlayOrderLogInfoEntity log = new PlayOrderLogInfoEntity();
|
||||
log.setId(IdUtils.getUuid());
|
||||
log.setTenantId(order.getTenantId());
|
||||
log.setOrderId(order.getId());
|
||||
log.setOperationType(StrUtil.isNotBlank(operationType) ? operationType : operation.name());
|
||||
log.setOperatorType(actor != null ? actor.name() : null);
|
||||
log.setOperatorId(operatorId);
|
||||
log.setOperTime(LocalDateTime.now());
|
||||
log.setRemark(remark);
|
||||
orderLogInfoMapper.insert(log);
|
||||
}
|
||||
|
||||
private OrderActor resolveCompletionActor(String operatorType) {
|
||||
if (StrUtil.isBlank(operatorType)) {
|
||||
return OrderActor.SYSTEM;
|
||||
}
|
||||
try {
|
||||
OperatorType type = OperatorType.fromCode(operatorType);
|
||||
switch (type) {
|
||||
case CUSTOMER:
|
||||
return OrderActor.CUSTOMER;
|
||||
case CLERK:
|
||||
return OrderActor.CLERK;
|
||||
case ADMIN:
|
||||
return OrderActor.ADMIN;
|
||||
default:
|
||||
return OrderActor.SYSTEM;
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
log.warn("Unknown operator type {}, fallback to SYSTEM", operatorType, ex);
|
||||
return OrderActor.SYSTEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.starry.admin.modules.order.module.vo.PlayOrderEvaluateReturnVo;
|
||||
import com.starry.admin.modules.order.service.IPlayOrderEvaluateInfoService;
|
||||
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
|
||||
import com.starry.admin.utils.SecurityUtils;
|
||||
import com.starry.common.enums.EvaluateHiddenState;
|
||||
import com.starry.common.utils.IdUtils;
|
||||
import com.starry.common.utils.StringUtils;
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.BalanceOperationType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderActor;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundFlag;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundRecordType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundState;
|
||||
@@ -37,6 +38,7 @@ 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.order.service.IPlayOrderRefundInfoService;
|
||||
import com.starry.admin.modules.order.service.support.ClerkRevenueCalculator;
|
||||
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
|
||||
import com.starry.admin.modules.shop.module.vo.PlayCouponDetailsReturnVo;
|
||||
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
|
||||
@@ -102,6 +104,9 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
@Resource
|
||||
private IOrderLifecycleService orderLifecycleService;
|
||||
|
||||
@Resource
|
||||
private ClerkRevenueCalculator clerkRevenueCalculator;
|
||||
|
||||
@Override
|
||||
public List<PlayOrderInfoEntity> getTotalOrderInfo(String tenantId) {
|
||||
MPJLambdaWrapper<PlayOrderInfoEntity> lambdaWrapper = new MPJLambdaWrapper<>();
|
||||
@@ -109,306 +114,10 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
return this.baseMapper.selectList(lambdaWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void createOrderInfo(String orderId, String orderNo, String orderState, String orderType, String placeType,
|
||||
String rewardType, String firstOrder, String commodityId, String commodityType, BigDecimal commodityPrice,
|
||||
String serviceDuration, String commodityName, String commodityNumber, BigDecimal orderMoney,
|
||||
BigDecimal finalAmount, BigDecimal discountAmount, String purchaserBy, String acceptBy, String weiChatCode,
|
||||
List<String> couponIds, String remark, String clerkSex, String clerkLevelId, String excludeHistory) {
|
||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity();
|
||||
entity.setId(orderId);
|
||||
entity.setOrderNo(orderNo);
|
||||
entity.setOrderStatus(orderState);
|
||||
entity.setOrderType(orderType);
|
||||
entity.setPlaceType(placeType);
|
||||
entity.setRewardType(rewardType);
|
||||
entity.setFirstOrder(firstOrder);
|
||||
entity.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||
entity.setCommodityId(commodityId);
|
||||
entity.setCommodityType(commodityType);
|
||||
entity.setCommodityPrice(commodityPrice);
|
||||
entity.setServiceDuration(serviceDuration);
|
||||
entity.setCommodityName(commodityName);
|
||||
entity.setCommodityNumber(commodityNumber);
|
||||
entity.setBackendEntry("0");
|
||||
entity.setPayMethod("0");
|
||||
entity.setOrderMoney(orderMoney);
|
||||
entity.setFinalAmount(finalAmount);
|
||||
entity.setDiscountAmount(discountAmount);
|
||||
entity.setWeiChatCode(weiChatCode);
|
||||
entity.setRemark(remark);
|
||||
entity.setOrderSettlementState("0");
|
||||
entity.setOrdersExpiredState("0");
|
||||
entity.setPurchaserBy(purchaserBy);
|
||||
entity.setPurchaserTime(LocalDateTime.now());
|
||||
entity.setCouponIds(couponIds);
|
||||
entity.setUseCoupon(couponIds != null && !couponIds.isEmpty() ? "1" : "0");
|
||||
if ("1".equals(placeType)) {
|
||||
entity.setSex(clerkSex);
|
||||
entity.setLevelId(clerkLevelId);
|
||||
entity.setExcludeHistory(excludeHistory);
|
||||
}
|
||||
if (StrUtil.isNotBlank(acceptBy)) {
|
||||
entity.setAcceptBy(acceptBy);
|
||||
ClerkEstimatedRevenueVo estimatedRevenueVo = getClerkEstimatedRevenue(acceptBy, couponIds, placeType,
|
||||
firstOrder, finalAmount);
|
||||
entity.setEstimatedRevenue(estimatedRevenueVo.getRevenueAmount());
|
||||
entity.setEstimatedRevenueRatio(estimatedRevenueVo.getRevenueRatio());
|
||||
}
|
||||
// 如果订单是打赏单,订单直接完成
|
||||
if ("2".equals(placeType)) {
|
||||
entity.setAcceptTime(LocalDateTime.now());
|
||||
entity.setOrderStartTime(LocalDateTime.now());
|
||||
entity.setOrderEndTime(LocalDateTime.now());
|
||||
}
|
||||
// 修改顾客下单信息
|
||||
userInfoService.saveOrderInfo(entity);
|
||||
// 保存订单
|
||||
this.baseMapper.insert(entity);
|
||||
// 修改优惠券状态
|
||||
playCouponDetailsService.updateCouponUseStateByIds(couponIds, "2");
|
||||
if ("2".equals(placeType) && StrUtil.isNotBlank(acceptBy)) {
|
||||
orderLifecycleService.completeOrder(
|
||||
orderId,
|
||||
OrderCompletionContext.of(null, null, OrderTriggerSource.REWARD_ORDER)
|
||||
.withForceNotify(true)
|
||||
.withComment("auto-complete reward order"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createOrderInfo(OrderCreationRequest request) {
|
||||
// 验证请求
|
||||
validateOrderCreationRequest(request);
|
||||
|
||||
PlayOrderInfoEntity entity = buildOrderEntity(request);
|
||||
|
||||
// 处理随机单要求
|
||||
if (request.getPlaceType() == OrderConstant.PlaceType.RANDOM) {
|
||||
setRandomOrderRequirements(entity, request.getRandomOrderRequirements());
|
||||
}
|
||||
|
||||
// 处理接单人信息
|
||||
if (StrUtil.isNotBlank(request.getAcceptBy())) {
|
||||
setAcceptByInfo(entity, request);
|
||||
}
|
||||
|
||||
// 处理打赏单自动完成逻辑
|
||||
if (request.isRewardOrder()) {
|
||||
setRewardOrderCompleted(entity);
|
||||
}
|
||||
// 处理首单逻辑
|
||||
if (StrUtil.isNotBlank(request.getAcceptBy()) && StrUtil.isNotBlank(request.getPurchaserBy())) {
|
||||
entity.setFirstOrder(this.checkFirstOrderFlag(request.getPurchaserBy(), request.getAcceptBy()) ? "1" : "0");
|
||||
}
|
||||
|
||||
// 保存订单
|
||||
userInfoService.saveOrderInfo(entity);
|
||||
this.baseMapper.insert(entity);
|
||||
|
||||
// 修改优惠券状态
|
||||
playCouponDetailsService.updateCouponUseStateByIds(
|
||||
request.getPaymentInfo().getCouponIds(), "2");
|
||||
|
||||
// 打赏单立即入账
|
||||
if (request.isRewardOrder() && StrUtil.isNotBlank(request.getAcceptBy())) {
|
||||
orderLifecycleService.completeOrder(
|
||||
entity.getId(),
|
||||
OrderCompletionContext.of(null, null, OrderTriggerSource.REWARD_ORDER)
|
||||
.withForceNotify(true)
|
||||
.withComment("auto-complete reward order"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证订单创建请求
|
||||
*/
|
||||
private void validateOrderCreationRequest(OrderCreationRequest request) {
|
||||
if (request.getPlaceType() == OrderConstant.PlaceType.RANDOM
|
||||
&& !request.isValidForRandomOrder()) {
|
||||
throw new CustomException("随机单必须提供店员要求信息");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建订单实体
|
||||
*/
|
||||
private PlayOrderInfoEntity buildOrderEntity(OrderCreationRequest request) {
|
||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity();
|
||||
|
||||
// 基本信息
|
||||
entity.setId(request.getOrderId());
|
||||
entity.setOrderNo(request.getOrderNo());
|
||||
entity.setOrderStatus(request.getOrderStatus().getCode());
|
||||
entity.setOrderType(request.getOrderType().getCode());
|
||||
entity.setPlaceType(request.getPlaceType().getCode());
|
||||
entity.setRewardType(request.getRewardType().getCode());
|
||||
entity.setFirstOrder(request.getFirstOrderString());
|
||||
|
||||
// 固定默认值
|
||||
entity.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||
entity.setBackendEntry("0");
|
||||
entity.setPayMethod("0");
|
||||
entity.setOrderSettlementState("0");
|
||||
entity.setOrdersExpiredState("0");
|
||||
|
||||
// 商品信息
|
||||
CommodityInfo commodityInfo = request.getCommodityInfo();
|
||||
entity.setCommodityId(commodityInfo.getCommodityId());
|
||||
entity.setCommodityType(commodityInfo.getCommodityType().getCode());
|
||||
entity.setCommodityPrice(commodityInfo.getCommodityPrice());
|
||||
entity.setServiceDuration(commodityInfo.getServiceDuration());
|
||||
entity.setCommodityName(commodityInfo.getCommodityName());
|
||||
entity.setCommodityNumber(commodityInfo.getCommodityNumber());
|
||||
|
||||
// 支付信息
|
||||
PaymentInfo paymentInfo = request.getPaymentInfo();
|
||||
entity.setOrderMoney(paymentInfo.getOrderMoney());
|
||||
entity.setFinalAmount(paymentInfo.getFinalAmount());
|
||||
entity.setDiscountAmount(paymentInfo.getDiscountAmount());
|
||||
entity.setCouponIds(paymentInfo.getCouponIds());
|
||||
entity.setUseCoupon(
|
||||
paymentInfo.getCouponIds() != null && !paymentInfo.getCouponIds().isEmpty() ? "1" : "0");
|
||||
|
||||
// 用户信息
|
||||
entity.setPurchaserBy(request.getPurchaserBy());
|
||||
entity.setPurchaserTime(LocalDateTime.now());
|
||||
entity.setWeiChatCode(request.getWeiChatCode());
|
||||
entity.setRemark(request.getRemark());
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置随机单要求
|
||||
*/
|
||||
private void setRandomOrderRequirements(PlayOrderInfoEntity entity, RandomOrderRequirements requirements) {
|
||||
if (requirements != null) {
|
||||
entity.setSex(requirements.getClerkGender().getCode());
|
||||
entity.setLevelId(requirements.getClerkLevelId());
|
||||
entity.setExcludeHistory(requirements.getExcludeHistory());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置接单人信息
|
||||
*/
|
||||
private void setAcceptByInfo(PlayOrderInfoEntity entity, OrderCreationRequest request) {
|
||||
entity.setAcceptBy(request.getAcceptBy());
|
||||
ClerkEstimatedRevenueVo estimatedRevenueVo = getClerkEstimatedRevenue(
|
||||
request.getAcceptBy(),
|
||||
request.getPaymentInfo().getCouponIds(),
|
||||
request.getPlaceType().getCode(),
|
||||
request.getFirstOrderString(),
|
||||
request.getPaymentInfo().getFinalAmount());
|
||||
entity.setEstimatedRevenue(estimatedRevenueVo.getRevenueAmount());
|
||||
entity.setEstimatedRevenueRatio(estimatedRevenueVo.getRevenueRatio());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置打赏单为已完成状态
|
||||
*/
|
||||
private void setRewardOrderCompleted(PlayOrderInfoEntity entity) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
entity.setAcceptTime(now);
|
||||
entity.setOrderStartTime(now);
|
||||
entity.setOrderEndTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClerkEstimatedRevenueVo getClerkEstimatedRevenue(String clerkId, List<String> croupIds, String placeType,
|
||||
String firstOrder, BigDecimal finalAmount) {
|
||||
PlayClerkLevelInfoEntity entity = playClerkUserInfoService.queryLevelCommission(clerkId);
|
||||
ClerkEstimatedRevenueVo estimatedRevenueVo = new ClerkEstimatedRevenueVo();
|
||||
switch (placeType) {
|
||||
case "0": {
|
||||
if ("1".equals(firstOrder)) {
|
||||
estimatedRevenueVo.setRevenueRatio(entity.getFirstRegularRatio());
|
||||
estimatedRevenueVo
|
||||
.setRevenueAmount(
|
||||
finalAmount
|
||||
.multiply(new BigDecimal(entity.getFirstRegularRatio())
|
||||
.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
||||
.setScale(2, RoundingMode.HALF_UP));
|
||||
} else {
|
||||
estimatedRevenueVo.setRevenueRatio(entity.getNotFirstRegularRatio());
|
||||
estimatedRevenueVo
|
||||
.setRevenueAmount(finalAmount
|
||||
.multiply(new BigDecimal(entity.getNotFirstRegularRatio())
|
||||
.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
||||
.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "1": {
|
||||
if ("1".equals(firstOrder)) {
|
||||
estimatedRevenueVo.setRevenueRatio(entity.getFirstRandomRadio());
|
||||
estimatedRevenueVo
|
||||
.setRevenueAmount(
|
||||
finalAmount
|
||||
.multiply(new BigDecimal(entity.getFirstRandomRadio())
|
||||
.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
||||
.setScale(2, RoundingMode.HALF_UP));
|
||||
} else {
|
||||
estimatedRevenueVo.setRevenueRatio(entity.getNotFirstRandomRadio());
|
||||
estimatedRevenueVo
|
||||
.setRevenueAmount(finalAmount
|
||||
.multiply(new BigDecimal(entity.getNotFirstRandomRadio())
|
||||
.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
||||
.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "2": {
|
||||
if ("1".equals(firstOrder)) {
|
||||
estimatedRevenueVo.setRevenueRatio(entity.getFirstRewardRatio());
|
||||
estimatedRevenueVo
|
||||
.setRevenueAmount(
|
||||
finalAmount
|
||||
.multiply(new BigDecimal(entity.getFirstRewardRatio())
|
||||
.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
||||
.setScale(2, RoundingMode.HALF_UP));
|
||||
} else {
|
||||
estimatedRevenueVo.setRevenueRatio(entity.getNotFirstRewardRatio());
|
||||
estimatedRevenueVo
|
||||
.setRevenueAmount(finalAmount
|
||||
.multiply(new BigDecimal(entity.getNotFirstRewardRatio())
|
||||
.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
||||
.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "-1": {
|
||||
log.error("下单类型异常,placeType={}", placeType);
|
||||
estimatedRevenueVo.setRevenueAmount(finalAmount);
|
||||
estimatedRevenueVo.setRevenueRatio(100);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
log.error("下单类型错误,placeType={}", placeType);
|
||||
estimatedRevenueVo.setRevenueAmount(finalAmount);
|
||||
estimatedRevenueVo.setRevenueRatio(100);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果优惠券不由店铺承担,那么店员预计收入减去优惠金额
|
||||
for (String croupId : croupIds) {
|
||||
PlayCouponDetailsReturnVo couponInfo = playCouponDetailsService.selectPlayCouponDetailsById(croupId);
|
||||
if ("0".equals(couponInfo.getAttributionDiscounts())) {
|
||||
BigDecimal revenueAmount = estimatedRevenueVo.getRevenueAmount();
|
||||
if ("0".equals(couponInfo.getDiscountType())) {
|
||||
revenueAmount = revenueAmount.subtract(couponInfo.getDiscountAmount());
|
||||
} else {
|
||||
revenueAmount = revenueAmount.subtract(revenueAmount.subtract(couponInfo.getDiscountAmount()));
|
||||
}
|
||||
log.debug("优惠券ID={},优惠券类型={},优惠金额={},优惠前金额={},优惠前金额={}", croupId, couponInfo.getDiscountType(),
|
||||
couponInfo.getDiscountAmount(), estimatedRevenueVo.getRevenueAmount(), revenueAmount);
|
||||
estimatedRevenueVo.setRevenueAmount(revenueAmount);
|
||||
}
|
||||
}
|
||||
return estimatedRevenueVo;
|
||||
return clerkRevenueCalculator.calculateEstimatedRevenue(clerkId, croupIds, placeType, firstOrder, finalAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -425,81 +134,59 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
@Override
|
||||
public BigDecimal getEstimatedRevenue(String clerkId, String placeType, String firstOrder, BigDecimal finalAmount) {
|
||||
PlayClerkLevelInfoEntity entity = playClerkUserInfoService.queryLevelCommission(clerkId);
|
||||
switch (placeType) {
|
||||
case "0": {
|
||||
if ("1".equals(firstOrder)) {
|
||||
return finalAmount.multiply(new BigDecimal(entity.getFirstRegularRatio())
|
||||
.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
|
||||
} else {
|
||||
return finalAmount.multiply(new BigDecimal(entity.getNotFirstRegularRatio())
|
||||
.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
case "1": {
|
||||
if ("1".equals(firstOrder)) {
|
||||
return finalAmount.multiply(new BigDecimal(entity.getFirstRandomRadio()).divide(new BigDecimal(100),
|
||||
4, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
|
||||
} else {
|
||||
return finalAmount.multiply(new BigDecimal(entity.getNotFirstRandomRadio())
|
||||
.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
case "2": {
|
||||
if ("1".equals(firstOrder)) {
|
||||
return finalAmount.multiply(new BigDecimal(entity.getFirstRewardRatio()).divide(new BigDecimal(100),
|
||||
4, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
|
||||
} else {
|
||||
return finalAmount.multiply(new BigDecimal(entity.getNotFirstRewardRatio())
|
||||
.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
case "-1": {
|
||||
log.error("下单类型异常,placeType={}", placeType);
|
||||
return finalAmount;
|
||||
}
|
||||
default: {
|
||||
log.error("下单类型错误,placeType={}", placeType);
|
||||
return finalAmount;
|
||||
boolean isFirst = OrderConstant.YesNoFlag.YES.getCode().equals(firstOrder);
|
||||
try {
|
||||
OrderConstant.PlaceType place = OrderConstant.PlaceType.fromCode(placeType);
|
||||
switch (place) {
|
||||
case SPECIFIED:
|
||||
return calculateRevenue(finalAmount,
|
||||
isFirst ? entity.getFirstRegularRatio() : entity.getNotFirstRegularRatio());
|
||||
case RANDOM:
|
||||
return calculateRevenue(finalAmount,
|
||||
isFirst ? entity.getFirstRandomRadio() : entity.getNotFirstRandomRadio());
|
||||
case REWARD:
|
||||
return calculateRevenue(finalAmount,
|
||||
isFirst ? entity.getFirstRewardRatio() : entity.getNotFirstRewardRatio());
|
||||
case OTHER:
|
||||
default:
|
||||
log.error("下单类型异常,placeType={}", placeType);
|
||||
return finalAmount;
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
log.error("下单类型错误,placeType={}", placeType, ex);
|
||||
return finalAmount;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getEstimatedRevenueRatio(String clerkId, String placeType, String firstOrder) {
|
||||
PlayClerkLevelInfoEntity entity = playClerkUserInfoService.queryLevelCommission(clerkId);
|
||||
switch (placeType) {
|
||||
case "0": {
|
||||
if ("1".equals(firstOrder)) {
|
||||
return entity.getFirstRegularRatio();
|
||||
} else {
|
||||
return entity.getNotFirstRegularRatio();
|
||||
}
|
||||
}
|
||||
case "1": {
|
||||
if ("1".equals(firstOrder)) {
|
||||
return entity.getFirstRandomRadio();
|
||||
} else {
|
||||
return entity.getNotFirstRandomRadio();
|
||||
}
|
||||
}
|
||||
case "2": {
|
||||
if ("1".equals(firstOrder)) {
|
||||
return entity.getFirstRewardRatio();
|
||||
} else {
|
||||
return entity.getNotFirstRewardRatio();
|
||||
}
|
||||
}
|
||||
case "-1": {
|
||||
log.error("下单类型异常,placeType={}", placeType);
|
||||
return 100;
|
||||
}
|
||||
default: {
|
||||
log.error("下单类型错误,placeType={}", placeType);
|
||||
return 100;
|
||||
boolean isFirst = OrderConstant.YesNoFlag.YES.getCode().equals(firstOrder);
|
||||
try {
|
||||
OrderConstant.PlaceType place = OrderConstant.PlaceType.fromCode(placeType);
|
||||
switch (place) {
|
||||
case SPECIFIED:
|
||||
return isFirst ? entity.getFirstRegularRatio() : entity.getNotFirstRegularRatio();
|
||||
case RANDOM:
|
||||
return isFirst ? entity.getFirstRandomRadio() : entity.getNotFirstRandomRadio();
|
||||
case REWARD:
|
||||
return isFirst ? entity.getFirstRewardRatio() : entity.getNotFirstRewardRatio();
|
||||
case OTHER:
|
||||
default:
|
||||
log.error("下单类型异常,placeType={}", placeType);
|
||||
return 100;
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
log.error("下单类型错误,placeType={}", placeType, ex);
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal calculateRevenue(BigDecimal amount, Integer ratio) {
|
||||
return amount.multiply(new BigDecimal(ratio).divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增充值订单
|
||||
*
|
||||
@@ -646,9 +333,11 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
|
||||
@Override
|
||||
public Boolean checkFirstOrderFlag(String customId, String clerkId) {
|
||||
// 检查是否是首单
|
||||
LambdaQueryWrapper<PlayOrderInfoEntity> wrapper = Wrappers.lambdaQuery(PlayOrderInfoEntity.class).eq(PlayOrderInfoEntity::getPurchaserBy, customId).eq(PlayOrderInfoEntity::getAcceptBy, clerkId).eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.COMPLETED.getCode());
|
||||
return this.baseMapper.selectCount(wrapper) > 0;
|
||||
LambdaQueryWrapper<PlayOrderInfoEntity> wrapper = Wrappers.lambdaQuery(PlayOrderInfoEntity.class)
|
||||
.eq(PlayOrderInfoEntity::getPurchaserBy, customId)
|
||||
.eq(PlayOrderInfoEntity::getAcceptBy, clerkId)
|
||||
.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.COMPLETED.getCode());
|
||||
return this.baseMapper.selectCount(wrapper) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -884,15 +573,18 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
if (isClerkOperator && OrderConstant.PlaceType.RANDOM.getCode().equals(orderInfo.getPlaceType())) {
|
||||
validateClerkQualificationForRandomOrder(orderInfo, clerkUserInfoEntity, acceptBy);
|
||||
}
|
||||
String firstOrderFlag = resolveFirstOrderFlag(orderInfo.getPurchaserBy(), acceptBy);
|
||||
|
||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity(orderId, OrderStatus.ACCEPTED.getCode());
|
||||
LocalDateTime acceptTime = LocalDateTime.now();
|
||||
entity.setAcceptBy(acceptBy);
|
||||
entity.setAcceptTime(acceptTime);
|
||||
entity.setFirstOrder(firstOrderFlag);
|
||||
ClerkEstimatedRevenueVo estimatedRevenueVo = this.getClerkEstimatedRevenue(
|
||||
acceptBy,
|
||||
orderInfo.getCouponIds(),
|
||||
orderInfo.getPlaceType(),
|
||||
orderInfo.getFirstOrder(),
|
||||
firstOrderFlag,
|
||||
orderInfo.getFinalAmount());
|
||||
BigDecimal revenueAmount = estimatedRevenueVo.getRevenueAmount();
|
||||
entity.setEstimatedRevenue(revenueAmount);
|
||||
@@ -926,6 +618,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
orderInfo.setOrderStatus(OrderStatus.ACCEPTED.getCode());
|
||||
orderInfo.setEstimatedRevenue(revenueAmount);
|
||||
orderInfo.setEstimatedRevenueRatio(estimatedRevenueVo.getRevenueRatio());
|
||||
orderInfo.setFirstOrder(firstOrderFlag);
|
||||
log.info("Order accepted successfully. orderId={}, orderNo={}, acceptBy={}, operatorByType={}",
|
||||
orderId, orderInfo.getOrderNo(), acceptBy, operatorByType);
|
||||
wxCustomMpService.sendOrderMessageAsync(orderInfo);
|
||||
@@ -951,6 +644,19 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveFirstOrderFlag(String purchaserId, String clerkId) {
|
||||
if (StrUtil.isBlank(purchaserId) || StrUtil.isBlank(clerkId)) {
|
||||
return OrderConstant.YesNoFlag.NO.getCode();
|
||||
}
|
||||
Long completedCount = orderInfoMapper.selectCount(Wrappers.lambdaQuery(PlayOrderInfoEntity.class)
|
||||
.eq(PlayOrderInfoEntity::getPurchaserBy, purchaserId)
|
||||
.eq(PlayOrderInfoEntity::getAcceptBy, clerkId));
|
||||
|
||||
return (completedCount == null || completedCount == 0)
|
||||
? OrderConstant.YesNoFlag.YES.getCode()
|
||||
: OrderConstant.YesNoFlag.NO.getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通用的订单查询对象 订单作为主表 连接顾客用户表、店员用户表、商品表
|
||||
*
|
||||
@@ -1038,12 +744,13 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
log.error("订单状态异常,不能完成订单,orderId={},orderStace={}", orderId, orderState);
|
||||
throw new CustomException("订单状态异常,不能开始订单");
|
||||
}
|
||||
OrderActor actor = resolveCompletionActor(operatorByType);
|
||||
orderLifecycleService.completeOrder(
|
||||
orderId,
|
||||
OrderCompletionContext.of(
|
||||
operatorByType,
|
||||
operatorBy,
|
||||
resolveCompletionSource(operatorByType),
|
||||
actor,
|
||||
actor == OrderActor.SYSTEM ? null : operatorBy,
|
||||
resolveCompletionSource(actor),
|
||||
"manual"));
|
||||
} else {
|
||||
log.error("修改订单状态异常,orderId={},orderStace={}", orderId, orderState);
|
||||
@@ -1182,25 +889,41 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
return orderInfoMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private OrderTriggerSource resolveCompletionSource(String operatorType) {
|
||||
private OrderActor resolveCompletionActor(String operatorType) {
|
||||
if (operatorType == null) {
|
||||
return OrderTriggerSource.UNKNOWN;
|
||||
return OrderActor.SYSTEM;
|
||||
}
|
||||
try {
|
||||
OperatorType type = OperatorType.fromCode(operatorType);
|
||||
switch (type) {
|
||||
case CUSTOMER:
|
||||
return OrderTriggerSource.WX_CUSTOMER;
|
||||
return OrderActor.CUSTOMER;
|
||||
case CLERK:
|
||||
return OrderTriggerSource.WX_CLERK;
|
||||
return OrderActor.CLERK;
|
||||
case ADMIN:
|
||||
return OrderTriggerSource.ADMIN_CONSOLE;
|
||||
return OrderActor.ADMIN;
|
||||
default:
|
||||
return OrderTriggerSource.UNKNOWN;
|
||||
return OrderActor.SYSTEM;
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
log.warn("Unknown operator type {}, fallback to unknown", operatorType, ex);
|
||||
log.warn("Unknown operator type {}, fallback to SYSTEM", operatorType, ex);
|
||||
return OrderActor.SYSTEM;
|
||||
}
|
||||
}
|
||||
|
||||
private OrderTriggerSource resolveCompletionSource(OrderActor actor) {
|
||||
if (actor == null) {
|
||||
return OrderTriggerSource.UNKNOWN;
|
||||
}
|
||||
switch (actor) {
|
||||
case CUSTOMER:
|
||||
return OrderTriggerSource.WX_CUSTOMER;
|
||||
case CLERK:
|
||||
return OrderTriggerSource.WX_CLERK;
|
||||
case ADMIN:
|
||||
return OrderTriggerSource.ADMIN_CONSOLE;
|
||||
default:
|
||||
return OrderTriggerSource.SYSTEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.starry.admin.modules.order.service.support;
|
||||
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.vo.ClerkEstimatedRevenueVo;
|
||||
import com.starry.admin.modules.shop.module.vo.PlayCouponDetailsReturnVo;
|
||||
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 计算店员预计收入的工具类,供订单生命周期与订单查询等场景复用。
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ClerkRevenueCalculator {
|
||||
|
||||
@Resource
|
||||
private IPlayClerkUserInfoService playClerkUserInfoService;
|
||||
|
||||
@Resource
|
||||
private IPlayCouponDetailsService playCouponDetailsService;
|
||||
|
||||
public ClerkEstimatedRevenueVo calculateEstimatedRevenue(
|
||||
String clerkId,
|
||||
List<String> couponIds,
|
||||
String placeType,
|
||||
String firstOrder,
|
||||
BigDecimal finalAmount) {
|
||||
PlayClerkLevelInfoEntity levelInfo = playClerkUserInfoService.queryLevelCommission(clerkId);
|
||||
ClerkEstimatedRevenueVo estimatedRevenueVo = new ClerkEstimatedRevenueVo();
|
||||
|
||||
boolean fallbackToOther = false;
|
||||
OrderConstant.PlaceType placeTypeEnum;
|
||||
try {
|
||||
placeTypeEnum = OrderConstant.PlaceType.fromCode(placeType);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
fallbackToOther = true;
|
||||
placeTypeEnum = OrderConstant.PlaceType.OTHER;
|
||||
log.warn("无法识别的下单类型,placeType={},clerkId={}。已按其他类型处理。", placeType, clerkId, ex);
|
||||
}
|
||||
|
||||
switch (placeTypeEnum) {
|
||||
case SPECIFIED: // 指定单
|
||||
fillRegularOrderRevenue(firstOrder, finalAmount, levelInfo, estimatedRevenueVo);
|
||||
break;
|
||||
case RANDOM: // 随机单
|
||||
fillRandomOrderRevenue(firstOrder, finalAmount, levelInfo, estimatedRevenueVo);
|
||||
break;
|
||||
case REWARD: // 打赏单
|
||||
fillRewardOrderRevenue(firstOrder, finalAmount, levelInfo, estimatedRevenueVo);
|
||||
break;
|
||||
case OTHER:
|
||||
default:
|
||||
if (!fallbackToOther) {
|
||||
log.warn("按其他下单类型计算预计收益,placeType={},clerkId={}", placeType, clerkId);
|
||||
}
|
||||
estimatedRevenueVo.setRevenueAmount(finalAmount);
|
||||
estimatedRevenueVo.setRevenueRatio(100);
|
||||
break;
|
||||
}
|
||||
|
||||
adjustRevenueByCoupon(clerkId, couponIds, estimatedRevenueVo);
|
||||
return estimatedRevenueVo;
|
||||
}
|
||||
|
||||
private void fillRegularOrderRevenue(String firstOrder, BigDecimal finalAmount,
|
||||
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
||||
if ("1".equals(firstOrder)) {
|
||||
vo.setRevenueRatio(levelInfo.getFirstRegularRatio());
|
||||
vo.setRevenueAmount(scaleAmount(finalAmount, levelInfo.getFirstRegularRatio()));
|
||||
} else {
|
||||
vo.setRevenueRatio(levelInfo.getNotFirstRegularRatio());
|
||||
vo.setRevenueAmount(scaleAmount(finalAmount, levelInfo.getNotFirstRegularRatio()));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillRandomOrderRevenue(String firstOrder, BigDecimal finalAmount,
|
||||
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
||||
if ("1".equals(firstOrder)) {
|
||||
vo.setRevenueRatio(levelInfo.getFirstRandomRadio());
|
||||
vo.setRevenueAmount(scaleAmount(finalAmount, levelInfo.getFirstRandomRadio()));
|
||||
} else {
|
||||
vo.setRevenueRatio(levelInfo.getNotFirstRandomRadio());
|
||||
vo.setRevenueAmount(scaleAmount(finalAmount, levelInfo.getNotFirstRandomRadio()));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillRewardOrderRevenue(String firstOrder, BigDecimal finalAmount,
|
||||
PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) {
|
||||
if ("1".equals(firstOrder)) {
|
||||
vo.setRevenueRatio(levelInfo.getFirstRewardRatio());
|
||||
vo.setRevenueAmount(scaleAmount(finalAmount, levelInfo.getFirstRewardRatio()));
|
||||
} else {
|
||||
vo.setRevenueRatio(levelInfo.getNotFirstRewardRatio());
|
||||
vo.setRevenueAmount(scaleAmount(finalAmount, levelInfo.getNotFirstRewardRatio()));
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal scaleAmount(BigDecimal baseAmount, Integer ratio) {
|
||||
return baseAmount
|
||||
.multiply(new BigDecimal(ratio).divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private void adjustRevenueByCoupon(String clerkId,
|
||||
List<String> couponIds,
|
||||
ClerkEstimatedRevenueVo estimatedRevenueVo) {
|
||||
List<String> safeCouponIds = couponIds == null ? Collections.emptyList() : couponIds;
|
||||
for (String couponId : safeCouponIds) {
|
||||
PlayCouponDetailsReturnVo couponInfo = playCouponDetailsService.selectPlayCouponDetailsById(couponId);
|
||||
if (couponInfo == null) {
|
||||
log.warn("优惠券信息不存在,couponId={}, clerkId={}", couponId, clerkId);
|
||||
continue;
|
||||
}
|
||||
if ("0".equals(couponInfo.getAttributionDiscounts())) {
|
||||
BigDecimal revenueAmount = estimatedRevenueVo.getRevenueAmount();
|
||||
if ("0".equals(couponInfo.getDiscountType())) {
|
||||
revenueAmount = revenueAmount.subtract(couponInfo.getDiscountAmount());
|
||||
} else {
|
||||
revenueAmount = revenueAmount.subtract(revenueAmount.subtract(couponInfo.getDiscountAmount()));
|
||||
}
|
||||
log.debug("优惠券ID={},优惠券类型={},优惠金额={},优惠前金额={},优惠后金额={}",
|
||||
couponId,
|
||||
couponInfo.getDiscountType(),
|
||||
couponInfo.getDiscountAmount(),
|
||||
estimatedRevenueVo.getRevenueAmount(),
|
||||
revenueAmount);
|
||||
estimatedRevenueVo.setRevenueAmount(revenueAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.starry.admin.modules.shop.module.constant;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 优惠券使用状态
|
||||
*/
|
||||
@Getter
|
||||
public enum CouponUseState {
|
||||
UNUSED("1"),
|
||||
USED("2"),
|
||||
RECYCLED("3");
|
||||
|
||||
private final String code;
|
||||
|
||||
CouponUseState(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static CouponUseState fromCode(String code) {
|
||||
for (CouponUseState state : values()) {
|
||||
if (state.code.equals(code)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown coupon use state code: " + code);
|
||||
}
|
||||
}
|
||||
@@ -19,14 +19,16 @@ import com.starry.admin.modules.custom.service.IPlayCustomGiftInfoService;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomLeaveMsgService;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
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.OrderCreationRequest;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
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;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderEvaluateInfoEntity;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
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;
|
||||
@@ -103,6 +105,9 @@ public class WxCustomController {
|
||||
@Resource
|
||||
private IPlayOrderInfoService playOrderInfoService;
|
||||
|
||||
@Resource
|
||||
private IOrderLifecycleService orderLifecycleService;
|
||||
|
||||
@Resource
|
||||
private IPlayCustomLeaveMsgService playCustomLeaveMsgService;
|
||||
|
||||
@@ -231,14 +236,16 @@ public class WxCustomController {
|
||||
}
|
||||
String orderId = IdUtils.getUuid();
|
||||
// 记录订单信息
|
||||
OrderCreationRequest orderRequest = OrderCreationRequest.builder()
|
||||
OrderCreationContext orderRequest = OrderCreationContext.builder()
|
||||
.orderId(orderId)
|
||||
.orderNo(playOrderInfoService.getOrderNo())
|
||||
.orderStatus(OrderConstant.OrderStatus.COMPLETED)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.REWARD)
|
||||
.rewardType(RewardType.BALANCE)
|
||||
.isFirstOrder(true)
|
||||
.isFirstOrder(false)
|
||||
.creatorActor(OrderActor.CUSTOMER)
|
||||
.creatorId(userId)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("")
|
||||
.commodityType(OrderConstant.CommodityType.GIFT)
|
||||
@@ -258,7 +265,7 @@ public class WxCustomController {
|
||||
.weiChatCode(vo.getWeiChatCode())
|
||||
.remark(vo.getRemark())
|
||||
.build();
|
||||
playOrderInfoService.createOrderInfo(orderRequest);
|
||||
orderLifecycleService.initiateOrder(orderRequest);
|
||||
// 顾客减少余额
|
||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(), customUserInfo.getAccountBalance().subtract(new BigDecimal(vo.getMoney())), "1", "打赏", new BigDecimal(vo.getMoney()), BigDecimal.ZERO, orderId);
|
||||
return R.ok("成功");
|
||||
@@ -325,7 +332,7 @@ public class WxCustomController {
|
||||
String orderId = IdUtils.getUuid();
|
||||
String orderNo = playOrderInfoService.getOrderNo();
|
||||
// 记录订单信息
|
||||
OrderCreationRequest orderRequest = OrderCreationRequest.builder()
|
||||
OrderCreationContext orderRequest = OrderCreationContext.builder()
|
||||
.orderId(orderId)
|
||||
.orderNo(orderNo)
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
@@ -333,6 +340,8 @@ public class WxCustomController {
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.rewardType(RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(true)
|
||||
.creatorActor(OrderActor.CUSTOMER)
|
||||
.creatorId(customId)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId(commodityInfo.getCommodityId())
|
||||
.commodityType(OrderConstant.CommodityType.SERVICE)
|
||||
@@ -352,7 +361,7 @@ public class WxCustomController {
|
||||
.weiChatCode(vo.getWeiChatCode())
|
||||
.remark(vo.getRemark())
|
||||
.build();
|
||||
playOrderInfoService.createOrderInfo(orderRequest);
|
||||
orderLifecycleService.initiateOrder(orderRequest);
|
||||
// 顾客减少余额
|
||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(), customUserInfo.getAccountBalance().subtract(money), "1", "下单-指定单", money, BigDecimal.ZERO, orderId);
|
||||
// 发送通知给店员
|
||||
@@ -380,7 +389,7 @@ public class WxCustomController {
|
||||
String orderId = IdUtils.getUuid();
|
||||
String orderNo = playOrderInfoService.getOrderNo();
|
||||
// 记录订单信息
|
||||
OrderCreationRequest orderRequest = OrderCreationRequest.builder()
|
||||
OrderCreationContext orderRequest = OrderCreationContext.builder()
|
||||
.orderId(orderId)
|
||||
.orderNo(orderNo)
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
@@ -388,6 +397,8 @@ public class WxCustomController {
|
||||
.placeType(OrderConstant.PlaceType.RANDOM)
|
||||
.rewardType(RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(true)
|
||||
.creatorActor(OrderActor.CUSTOMER)
|
||||
.creatorId(customId)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId(commodityInfo.getCommodityId())
|
||||
.commodityType(OrderConstant.CommodityType.SERVICE)
|
||||
@@ -411,7 +422,7 @@ public class WxCustomController {
|
||||
.excludeHistory(vo.getExcludeHistory())
|
||||
.build())
|
||||
.build();
|
||||
playOrderInfoService.createOrderInfo(orderRequest);
|
||||
orderLifecycleService.initiateOrder(orderRequest);
|
||||
// 顾客减少余额
|
||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(), customUserInfo.getAccountBalance().subtract(money), "1", "下单-随机单", money, BigDecimal.ZERO, orderId);
|
||||
// 给全部店员发送通知
|
||||
|
||||
@@ -7,10 +7,12 @@ 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;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
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.OrderCreationRequest;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
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;
|
||||
import com.starry.admin.modules.shop.service.IPlayClerkGiftInfoService;
|
||||
@@ -36,6 +38,9 @@ public class WxGiftOrderService {
|
||||
@Resource
|
||||
private IPlayOrderInfoService playOrderInfoService;
|
||||
|
||||
@Resource
|
||||
private IOrderLifecycleService orderLifecycleService;
|
||||
|
||||
@Resource
|
||||
private IPlayCustomUserInfoService customUserInfoService;
|
||||
|
||||
@@ -81,9 +86,9 @@ public class WxGiftOrderService {
|
||||
}
|
||||
|
||||
String orderId = IdUtils.getUuid();
|
||||
OrderCreationRequest orderRequest = buildOrderCreationRequest(orderId, request, sessionUser.getId(),
|
||||
OrderCreationContext orderRequest = buildOrderCreationContext(orderId, request, sessionUser.getId(),
|
||||
giftInfo, totalAmount);
|
||||
playOrderInfoService.createOrderInfo(orderRequest);
|
||||
orderLifecycleService.initiateOrder(orderRequest);
|
||||
|
||||
BigDecimal newBalance = currentBalance.subtract(totalAmount);
|
||||
customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), currentBalance, newBalance, "1",
|
||||
@@ -97,10 +102,10 @@ public class WxGiftOrderService {
|
||||
return orderId;
|
||||
}
|
||||
|
||||
private OrderCreationRequest buildOrderCreationRequest(String orderId, PlayOrderInfoGiftAdd request,
|
||||
private OrderCreationContext buildOrderCreationContext(String orderId, PlayOrderInfoGiftAdd request,
|
||||
String purchaserId, PlayGiftInfoEntity giftInfo,
|
||||
BigDecimal totalAmount) {
|
||||
return OrderCreationRequest.builder()
|
||||
return OrderCreationContext.builder()
|
||||
.orderId(orderId)
|
||||
.orderNo(playOrderInfoService.getOrderNo())
|
||||
.orderStatus(OrderConstant.OrderStatus.COMPLETED)
|
||||
@@ -108,6 +113,8 @@ public class WxGiftOrderService {
|
||||
.placeType(OrderConstant.PlaceType.REWARD)
|
||||
.rewardType(RewardType.GIFT)
|
||||
.isFirstOrder(true)
|
||||
.creatorActor(OrderActor.CUSTOMER)
|
||||
.creatorId(purchaserId)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId(giftInfo.getId())
|
||||
.commodityType(OrderConstant.CommodityType.GIFT)
|
||||
|
||||
Reference in New Issue
Block a user