|
|
@@ -1,22 +1,23 @@
|
|
|
package com.ruoyi.app.service;
|
|
|
|
|
|
-import cn.hutool.core.util.ObjectUtil;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.ruoyi.common.exception.ConcurrentUpdateException;
|
|
|
import com.ruoyi.common.exception.ServiceException;
|
|
|
-import com.ruoyi.common.utils.MessageUtils;
|
|
|
-import com.ruoyi.system.domain.PointsTransaction;
|
|
|
+import com.ruoyi.system.domain.WalletTransaction;
|
|
|
import com.ruoyi.system.domain.UserWallet;
|
|
|
import com.ruoyi.system.service.IPointsTransactionService;
|
|
|
import com.ruoyi.system.service.IUserWalletService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.retry.annotation.Backoff;
|
|
|
import org.springframework.retry.annotation.Retryable;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
import java.util.Date;
|
|
|
|
|
|
+@Slf4j
|
|
|
@Component
|
|
|
public class WalletService {
|
|
|
@Autowired
|
|
|
@@ -27,71 +28,188 @@ public class WalletService {
|
|
|
/**
|
|
|
* 订单取消、退款成功返回积分
|
|
|
*
|
|
|
- * @param userId
|
|
|
- * @param ddId
|
|
|
- * @param points
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @param ddId 订单ID
|
|
|
+ * @param points 积分数量
|
|
|
*/
|
|
|
-
|
|
|
@Retryable(value = ConcurrentUpdateException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 1.5))
|
|
|
- @Transactional(rollbackFor = Exception.class) // 添加事务
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
public void returnPoints(Long userId, Long ddId, Long points) {
|
|
|
+ log.info("开始处理积分退回,用户ID: {}, 订单ID: {}, 积分: {}", userId, ddId, points);
|
|
|
+
|
|
|
// 1. 参数校验
|
|
|
- if (userId == null || ddId == null || points == null || points <= 0) {
|
|
|
- throw new ServiceException("参数错误");
|
|
|
+ validateReturnPointsParams(userId, ddId, points);
|
|
|
+
|
|
|
+ // 2. 获取用户钱包
|
|
|
+ UserWallet userWallet = getUserWallet(userId);
|
|
|
+
|
|
|
+ // 3. 检查是否已经退过积分(防重复)
|
|
|
+ checkDuplicateReturn(ddId);
|
|
|
+
|
|
|
+ // 4. 执行积分退回
|
|
|
+ executePointsReturn(userWallet, BigDecimal.valueOf(points), ddId);
|
|
|
+
|
|
|
+ log.info("积分退回成功,用户ID: {}, 订单ID: {}, 积分: {}", userId, ddId, points);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单取消、退款成功返回余额
|
|
|
+ *
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @param ddId 订单ID
|
|
|
+ * @param amount 金额
|
|
|
+ */
|
|
|
+ @Retryable(value = ConcurrentUpdateException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 1.5))
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void returnBalance(Long userId, Long ddId, BigDecimal amount) {
|
|
|
+ log.info("开始处理余额退回,用户ID: {}, 订单ID: {}, 金额: {}", userId, ddId, amount);
|
|
|
+
|
|
|
+ // 1. 参数校验
|
|
|
+ validateReturnBalanceParams(userId, ddId, amount);
|
|
|
+
|
|
|
+ // 2. 获取用户钱包
|
|
|
+ UserWallet userWallet = getUserWallet(userId);
|
|
|
+
|
|
|
+ // 3. 检查是否已经退过余额(防重复)
|
|
|
+ checkDuplicateBalanceReturn(ddId);
|
|
|
+
|
|
|
+ // 4. 执行余额退回
|
|
|
+ executeBalanceReturn(userWallet, amount, ddId);
|
|
|
+
|
|
|
+ log.info("余额退回成功,用户ID: {}, 订单ID: {}, 金额: {}", userId, ddId, amount);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验积分退回参数
|
|
|
+ */
|
|
|
+ private void validateReturnPointsParams(Long userId, Long ddId, Long points) {
|
|
|
+ if (userId == null || userId <= 0) {
|
|
|
+ throw new ServiceException("用户ID不能为空或无效");
|
|
|
+ }
|
|
|
+ if (ddId == null || ddId <= 0) {
|
|
|
+ throw new ServiceException("订单ID不能为空或无效");
|
|
|
}
|
|
|
+ if (points == null || points <= 0) {
|
|
|
+ throw new ServiceException("积分数量必须大于0");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验余额退回参数
|
|
|
+ */
|
|
|
+ private void validateReturnBalanceParams(Long userId, Long ddId, BigDecimal amount) {
|
|
|
+ if (userId == null || userId <= 0) {
|
|
|
+ throw new ServiceException("用户ID不能为空或无效");
|
|
|
+ }
|
|
|
+ if (ddId == null || ddId <= 0) {
|
|
|
+ throw new ServiceException("订单ID不能为空或无效");
|
|
|
+ }
|
|
|
+ if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ throw new ServiceException("金额必须大于0");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户钱包
|
|
|
+ */
|
|
|
+ private UserWallet getUserWallet(Long userId) {
|
|
|
LambdaQueryWrapper<UserWallet> walletQuery = new LambdaQueryWrapper<>();
|
|
|
walletQuery.eq(UserWallet::getUserId, userId);
|
|
|
UserWallet userWallet = userWalletService.getOne(walletQuery);
|
|
|
if (userWallet == null) {
|
|
|
- throw new ServiceException("用户钱包不存在");
|
|
|
+ throw new ServiceException("用户钱包不存在,用户ID: " + userId);
|
|
|
}
|
|
|
- // 3. 检查是否已经退过积分(防重复)
|
|
|
- PointsTransaction existingTransaction = pointsTransactionService.getOne(
|
|
|
- new LambdaQueryWrapper<PointsTransaction>()
|
|
|
- .eq(PointsTransaction::getDdId, ddId)
|
|
|
- .eq(PointsTransaction::getType, "2")
|
|
|
+ return userWallet;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查积分退回是否重复
|
|
|
+ */
|
|
|
+ private void checkDuplicateReturn(Long ddId) {
|
|
|
+ WalletTransaction existingTransaction = pointsTransactionService.getOne(
|
|
|
+ new LambdaQueryWrapper<WalletTransaction>()
|
|
|
+ .eq(WalletTransaction::getDdId, ddId)
|
|
|
+ .eq(WalletTransaction::getType, "2")
|
|
|
);
|
|
|
if (existingTransaction != null) {
|
|
|
- throw new ServiceException("该订单积分已退回,请勿重复操作");
|
|
|
+ throw new ServiceException("该订单积分已退回,请勿重复操作,订单ID: " + ddId);
|
|
|
}
|
|
|
- //该订单不存在退回的积分记录
|
|
|
- if (ObjectUtil.isNotNull(userWallet)) {
|
|
|
- // 4. 先创建积分流水记录(重要:先记录后更新)
|
|
|
- Long newBalance = userWallet.getPointsWallet() + points;
|
|
|
- createPointTransaction(userId, points, newBalance, ddId, "2");
|
|
|
-
|
|
|
- // 5. 乐观锁更新钱包
|
|
|
- LambdaQueryWrapper<UserWallet> updateQuery = new LambdaQueryWrapper<>();
|
|
|
- updateQuery.eq(UserWallet::getUserId, userId)
|
|
|
- .eq(UserWallet::getVersion, userWallet.getVersion()); // 版本控制
|
|
|
-
|
|
|
- userWallet.setPointsWallet(newBalance);
|
|
|
- userWallet.setVersion(userWallet.getVersion() + 1);
|
|
|
-
|
|
|
- boolean updateSuccess = userWalletService.update(userWallet, updateQuery);
|
|
|
- if (!updateSuccess) {
|
|
|
- throw new ConcurrentUpdateException("钱包更新冲突,请重试");
|
|
|
- }
|
|
|
- } else {
|
|
|
- throw new ServiceException(MessageUtils.message("no.points.insufficient"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查余额退回是否重复
|
|
|
+ */
|
|
|
+ private void checkDuplicateBalanceReturn(Long ddId) {
|
|
|
+ WalletTransaction existingTransaction = pointsTransactionService.getOne(
|
|
|
+ new LambdaQueryWrapper<WalletTransaction>()
|
|
|
+ .eq(WalletTransaction::getDdId, ddId)
|
|
|
+ .eq(WalletTransaction::getType, "3") // 余额退回类型
|
|
|
+ );
|
|
|
+ if (existingTransaction != null) {
|
|
|
+ throw new ServiceException("该订单余额已退回,请勿重复操作,订单ID: " + ddId);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 创建积分流水
|
|
|
+ * 执行积分退回
|
|
|
+ */
|
|
|
+ private void executePointsReturn(UserWallet userWallet, BigDecimal points, Long ddId) {
|
|
|
+ // 计算新的积分余额
|
|
|
+ BigDecimal newBalance = (userWallet.getPointsWallet() != null ? userWallet.getPointsWallet() : BigDecimal.ZERO).add( points);
|
|
|
+
|
|
|
+ // 先创建积分流水记录
|
|
|
+ createTransaction(userWallet.getUserId(), points, newBalance, String.valueOf(ddId), "2");
|
|
|
+
|
|
|
+ // 乐观锁更新钱包
|
|
|
+ updateWalletWithOptimisticLock(userWallet, newBalance, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行余额退回
|
|
|
+ */
|
|
|
+ private void executeBalanceReturn(UserWallet userWallet, BigDecimal amount, Long ddId) {
|
|
|
+ // 计算新的余额
|
|
|
+ BigDecimal currentBalance = userWallet.getBalanceWallet() != null ? userWallet.getBalanceWallet() : BigDecimal.ZERO;
|
|
|
+ BigDecimal newBalance = currentBalance.add(amount);
|
|
|
+
|
|
|
+ // 先创建余额流水记录
|
|
|
+ createTransaction(userWallet.getUserId(), amount, newBalance, ddId.toString(), "1");
|
|
|
+
|
|
|
+ // 乐观锁更新钱包
|
|
|
+ updateWalletWithOptimisticLock(userWallet, null, newBalance);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用乐观锁更新钱包
|
|
|
*/
|
|
|
- private void createPointTransaction(Long userid, Long pointsChange, Long currentPoints, Long ddId, String type) {
|
|
|
- PointsTransaction pointsTransaction = new PointsTransaction();
|
|
|
- pointsTransaction.setUserId(userid);
|
|
|
- if (type.equals("1")) {
|
|
|
- pointsTransaction.setPointsChange("-" + pointsChange);
|
|
|
- pointsTransaction.setType("1");
|
|
|
+ private void updateWalletWithOptimisticLock(UserWallet userWallet, BigDecimal newPointsBalance, BigDecimal newBalanceAmount) {
|
|
|
+ LambdaQueryWrapper<UserWallet> updateQuery = new LambdaQueryWrapper<>();
|
|
|
+ updateQuery.eq(UserWallet::getUserId, userWallet.getUserId())
|
|
|
+ .eq(UserWallet::getVersion, userWallet.getVersion());
|
|
|
+
|
|
|
+ if (newPointsBalance != null) {
|
|
|
+ userWallet.setPointsWallet(newPointsBalance);
|
|
|
+ }
|
|
|
+ if (newBalanceAmount != null) {
|
|
|
+ userWallet.setBalanceWallet(newBalanceAmount);
|
|
|
}
|
|
|
- if (type.equals("2")) {
|
|
|
- pointsTransaction.setPointsChange("+" + pointsChange);
|
|
|
- pointsTransaction.setType("2");
|
|
|
+ userWallet.setVersion(userWallet.getVersion() + 1);
|
|
|
+
|
|
|
+ boolean updateSuccess = userWalletService.update(userWallet, updateQuery);
|
|
|
+ if (!updateSuccess) {
|
|
|
+ throw new ConcurrentUpdateException("钱包更新冲突,请重试,用户ID: " + userWallet.getUserId());
|
|
|
}
|
|
|
- pointsTransaction.setCurrentPoints(currentPoints.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建积分流水
|
|
|
+ */
|
|
|
+ private void createTransaction(Long userId, BigDecimal change, BigDecimal currentBalance, String ddId, String type) {
|
|
|
+ WalletTransaction pointsTransaction = new WalletTransaction();
|
|
|
+ pointsTransaction.setUserId(userId);
|
|
|
+ pointsTransaction.setChange(change.toString());
|
|
|
+ pointsTransaction.setType(type);
|
|
|
+ pointsTransaction.setCurrentBalance(currentBalance.toString());
|
|
|
pointsTransaction.setDdId(ddId);
|
|
|
pointsTransaction.setCreateTime(new Date());
|
|
|
pointsTransactionService.save(pointsTransaction);
|