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();
|
||||
|
||||
Reference in New Issue
Block a user