package cc.mrbird.febs.mall.service.impl;
|
|
import cc.mrbird.febs.common.entity.FebsResponse;
|
import cc.mrbird.febs.common.enumerates.OrderStatusEnum;
|
import cc.mrbird.febs.common.exception.FebsException;
|
import cc.mrbird.febs.common.utils.RedisUtils;
|
import cc.mrbird.febs.mall.controller.dependentStation.constant.OrderConstants;
|
import cc.mrbird.febs.mall.controller.dependentStation.enums.SalesServiceEnums;
|
import cc.mrbird.febs.mall.entity.DataDictionaryCustom;
|
import cc.mrbird.febs.mall.entity.MallOrderInfo;
|
import cc.mrbird.febs.mall.mapper.DataDictionaryCustomMapper;
|
import cc.mrbird.febs.mall.mapper.MallOrderInfoMapper;
|
import cc.mrbird.febs.mall.service.ApiChatPayService;
|
import cc.mrbird.febs.mall.vo.ApiOrderPayVo;
|
import cn.hutool.core.util.StrUtil;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.Resource;
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.util.HashMap;
|
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.TimeUnit;
|
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor
|
public class ApiChatPayServiceImpl implements ApiChatPayService {
|
|
@Resource
|
private RedisTemplate<String, String> redisTemplate;
|
private final MallOrderInfoMapper mallOrderInfoMapper;
|
private final DataDictionaryCustomMapper dataDictionaryCustomMapper;
|
@Override
|
public FebsResponse usPay(MallOrderInfo chatOrder) {
|
|
//判断是否redis已经缓存订单
|
BigDecimal amount = chatOrder.getAmount();
|
if (StrUtil.isEmpty(chatOrder.getTradeHash())) {
|
amount = generateUniqueNumber(chatOrder.getOrderNo(), amount);
|
}else{
|
amount = new BigDecimal(chatOrder.getTradeHash());
|
}
|
// 调用Mapper更新数据库中的订单信息
|
mallOrderInfoMapper.update(null,
|
Wrappers.lambdaUpdate(MallOrderInfo.class)
|
.set(MallOrderInfo::getPayMethod, OrderConstants.PAY_TYPE_USDT)
|
.set(MallOrderInfo::getTradeHash, amount.toString())
|
.eq(MallOrderInfo::getId, chatOrder.getId())
|
);
|
|
DataDictionaryCustom dataDictionaryCustom = dataDictionaryCustomMapper.selectDicDataByTypeAndCode(
|
SalesServiceEnums.TRC_ADDRESS.getType(),
|
SalesServiceEnums.TRC_ADDRESS.getCode()
|
);
|
String addressSystem = dataDictionaryCustom.getValue();
|
ApiOrderPayVo apiOrderPayVo = new ApiOrderPayVo();
|
apiOrderPayVo.setOrderCode(chatOrder.getOrderNo());
|
apiOrderPayVo.setAmount(amount.toString());
|
apiOrderPayVo.setAddress(addressSystem);
|
apiOrderPayVo.setCreatedTime(chatOrder.getCreatedTime());
|
return new FebsResponse().success().data(apiOrderPayVo);
|
}
|
|
/**
|
* 生成唯一数字
|
* 通过随机数和给定的金额计算生成一个唯一数字,并确保这个数字在Redis中是唯一的
|
*
|
* @param amount 金额
|
* @param orderCode 订单编码
|
* @return 生成的唯一数字
|
* @throws RuntimeException 如果在指定次数内无法生成唯一数字
|
*/
|
private BigDecimal generateUniqueNumber(String orderCode,BigDecimal amount) {
|
int maxAttempts = 50;
|
int attempts = 0;
|
int num;
|
BigDecimal bigDecimal;
|
|
do {
|
if (attempts++ >= maxAttempts) {
|
throw new FebsException("Payment exception");
|
}
|
num = ThreadLocalRandom.current().nextInt(1, 51);
|
|
bigDecimal = calculateAmount(amount, num);
|
|
} while (!setRedisValue(bigDecimal, orderCode)); // 原子操作检查并写入
|
|
return bigDecimal;
|
}
|
|
/**
|
* 计算调整后的金额
|
* 根据给定的金额和一个随机数计算新的金额,用于生成唯一数字
|
*
|
* @param amount 原始金额
|
* @param num 随机生成的数字
|
* @return 调整后的金额
|
*/
|
private BigDecimal calculateAmount(BigDecimal amount, int num) {
|
BigDecimal multiplier = new BigDecimal("0.01");
|
BigDecimal increment = new BigDecimal(num).multiply(multiplier);
|
return amount.add(increment).setScale(2, RoundingMode.DOWN);
|
}
|
|
/**
|
* 将生成的数字设置到Redis中
|
* 此方法确保生成的数字是唯一的,通过在Redis中设置值并使用NX参数来实现
|
*
|
* @param amountReal 生成的唯一数字
|
* @param orderCode 订单编码
|
* @return 如果设置成功则返回true,否则返回false
|
*/
|
private boolean setRedisValue(BigDecimal amountReal, String orderCode) {
|
String key = OrderConstants.TRC20_ORDER_KEY + amountReal; // 添加前缀避免键冲突
|
return redisTemplate.opsForValue().setIfAbsent(key, orderCode, 60L * Integer.parseInt("30"), TimeUnit.SECONDS);
|
}
|
}
|