debug:高并发情况下,智能退款失败(因为乐观锁了余额)

This commit is contained in:
磷叶 2025-02-11 10:29:19 +08:00
parent 91fcd92952
commit a88d5c407c
4 changed files with 57 additions and 44 deletions

View File

@ -100,10 +100,10 @@ public class IotServiceImpl implements IotService {
if (res != null && res.isNotOnline()) { if (res != null && res.isNotOnline()) {
status = DeviceOnlineStatus.OFFLINE.getStatus(); status = DeviceOnlineStatus.OFFLINE.getStatus();
} }
// 若是命令超时则累加redis数据超过1次则判断为离线 // 若是命令超时则累加redis数据超过2次则判断为离线
else if (res == null || res.isCmdTimeout()) { else if (res == null || res.isCmdTimeout()) {
long offlineCount = redisCache.incrementCacheValue(incrementCacheKey, 60, TimeUnit.SECONDS); long offlineCount = redisCache.incrementCacheValue(incrementCacheKey, 60, TimeUnit.SECONDS);
if (offlineCount >= 1) { if (offlineCount >= 2) {
status = DeviceOnlineStatus.OFFLINE.getStatus(); status = DeviceOnlineStatus.OFFLINE.getStatus();
} else { } else {
status = DeviceOnlineStatus.ONLINE.getStatus(); status = DeviceOnlineStatus.ONLINE.getStatus();

View File

@ -1,12 +1,13 @@
package com.ruoyi.ss.user.mapper; package com.ruoyi.ss.user.mapper;
import java.math.BigDecimal;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.common.core.domain.entity.SmUser; import com.ruoyi.common.core.domain.entity.SmUser;
import com.ruoyi.ss.user.domain.SmUserQuery; import com.ruoyi.ss.user.domain.SmUserQuery;
import com.ruoyi.ss.user.domain.SmUserVO; import com.ruoyi.ss.user.domain.SmUserVO;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.util.List;
/** /**
* 普通用户信息Mapper接口 * 普通用户信息Mapper接口
@ -66,16 +67,6 @@ public interface SmUserMapper
int selectCount(SmUserQuery dto); int selectCount(SmUserQuery dto);
/**
* 增加余额
* @param userId 用户id
* @param amount 金额
* @param beforeBalance 变动前的余额并发
*/
int addBalance(@Param("userId") Long userId,
@Param("amount") BigDecimal amount,
@Param("beforeBalance") BigDecimal beforeBalance
);
SmUserVO selectSmUserByWxOpenId(String openId); SmUserVO selectSmUserByWxOpenId(String openId);
@ -106,12 +97,19 @@ public interface SmUserMapper
* @param userId 用户id * @param userId 用户id
* @param amount 金额 * @param amount 金额
* @param check 校验余额是否充足 * @param check 校验余额是否充足
* @param beforeBalance 变动前的余额并发
*/ */
int subtractBalance(@Param("userId") Long userId, int subtractBalance(@Param("userId") Long userId,
@Param("amount") BigDecimal amount, @Param("amount") BigDecimal amount,
@Param("check") boolean check, @Param("check") boolean check
@Param("beforeBalance") BigDecimal beforeBalance );
/**
* 增加余额
* @param userId 用户id
* @param amount 金额
*/
int addBalance(@Param("userId") Long userId,
@Param("amount") BigDecimal amount
); );
/** /**
@ -146,4 +144,11 @@ public interface SmUserMapper
* 解除实名认证 * 解除实名认证
*/ */
int resetRealName(Long userId); int resetRealName(Long userId);
/**
* 查询用户余额
*/
BigDecimal selectBalanceForUpdate(@Param("userId") Long userId);
} }

View File

@ -286,12 +286,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where user_id = #{userId} and del_flag = '0' where user_id = #{userId} and del_flag = '0'
</update> </update>
<update id="addBalance">
update sm_user
set balance = balance + #{amount}
where user_id = #{userId} and del_flag = '0' and balance = #{beforeBalance}
</update>
<update id="updateSmUser" parameterType="SmUser"> <update id="updateSmUser" parameterType="SmUser">
update sm_user update sm_user
<trim prefix="SET" suffixOverrides=","> <trim prefix="SET" suffixOverrides=",">
@ -362,13 +356,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<update id="subtractBalance"> <update id="subtractBalance">
update sm_user update sm_user
set balance = balance - #{amount} set balance = balance - #{amount}
<where> where user_id = #{userId} and del_flag = '0'
user_id = #{userId} and del_flag = '0' and balance = #{beforeBalance} <if test="check">
<if test="check"> and balance >= #{amount}
and balance >= #{amount} </if>
</if> </update>
</where> <update id="addBalance">
update sm_user
set balance = balance + #{amount}
where user_id = #{userId} and del_flag = '0'
</update> </update>
<update id="updateServiceRate"> <update id="updateServiceRate">
@ -426,4 +423,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
from sm_user su from sm_user su
where su.phonenumber = #{phonenumber} and su.del_flag != '2' where su.phonenumber = #{phonenumber} and su.del_flag != '2'
</select> </select>
<!-- selectBalanceForUpdate -->
<select id="selectBalanceForUpdate">
select balance
from sm_user
where user_id = #{userId} and del_flag = '0'
for update
</select>
</mapper> </mapper>

View File

@ -231,19 +231,19 @@ public class UserServiceImpl implements UserService
if (BigDecimal.ZERO.compareTo(amount) == 0) { if (BigDecimal.ZERO.compareTo(amount) == 0) {
return 1; return 1;
} }
// 查询用户余额
// 查询用户 BigDecimal balance = smUserMapper.selectBalanceForUpdate(userId);
SmUserVO user = selectSmUserByUserId(userId); ServiceUtil.assertion(balance == null, "增加ID为%s的用户余额%s元失败请重试", userId, amount);
// 修改余额 // 修改余额
int updateCount = smUserMapper.addBalance(userId, amount, user.getBalance()); int update = smUserMapper.addBalance(userId, amount);
ServiceUtil.assertion(updateCount != 1, "增加用户余额失败,请重试"); ServiceUtil.assertion(update != 1, "增加用户余额失败,请重试");
// 余额变动记录 // 余额变动记录
int record = recordBalanceService.record(userId, user.getBalance(), amount, reason, bstType, bstId); int record = recordBalanceService.record(userId, balance, amount, reason, bstType, bstId);
ServiceUtil.assertion(record != 1, "用户余额变动记录失败"); ServiceUtil.assertion(record != 1, "用户余额变动记录失败");
return updateCount; return update;
} }
@Override @Override
@ -259,18 +259,19 @@ public class UserServiceImpl implements UserService
return 1; return 1;
} }
// 查询用户 // 查询用户余额
SmUserVO user = selectSmUserByUserId(userId); BigDecimal balance = smUserMapper.selectBalanceForUpdate(userId);
ServiceUtil.assertion(balance == null || balance.compareTo(amount) < 0, "减少ID为%s的用户余额%s元失败请重试", userId, amount);
// 更新用户余额 // 更新用户余额
int updateCount = smUserMapper.subtractBalance(userId, amount, check, user.getBalance()); int update = smUserMapper.subtractBalance(userId, amount, check);
ServiceUtil.assertion(updateCount != 1, "减少ID为%s的用户余额%s元失败请重试", userId, amount); ServiceUtil.assertion(update != 1, "减少ID为%s的用户余额%s元失败请重试", userId, amount);
// 余额变动记录 // 余额变动记录
int record = recordBalanceService.record(userId, user.getBalance(), amount.negate(), reason, bstType, bstId); int record = recordBalanceService.record(userId, balance, amount.negate(), reason, bstType, bstId);
ServiceUtil.assertion(record != 1, "记录ID为%s的用户余额变动记录%s元失败", userId, amount); ServiceUtil.assertion(record != 1, "记录ID为%s的用户余额变动记录%s元失败", userId, amount);
return updateCount; return update;
} }