diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/AccountIsBindCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/AccountIsBindCmdExe.java index 700e1b85..3894db38 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/AccountIsBindCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/AccountIsBindCmdExe.java @@ -24,7 +24,13 @@ public class AccountIsBindCmdExe { AuthTypeEnum.ACCOUNT.name(), cmd.requiredReqUserId().toString() ); - return Objects.nonNull(authType); + if (Objects.isNull(authType)) { + return authTypeService.getAuthInfo( + AuthTypeEnum.MOBILE.name(), + cmd.requiredReqUserId().toString() + ) != null; + } + return true; } diff --git a/rc-service/rc-service-other/other-start/src/test/java/RedisTest.java b/rc-service/rc-service-other/other-start/src/test/java/RedisTest.java index bb0312e5..e9cc60c1 100644 --- a/rc-service/rc-service-other/other-start/src/test/java/RedisTest.java +++ b/rc-service/rc-service-other/other-start/src/test/java/RedisTest.java @@ -2,6 +2,7 @@ import com.red.circle.OtherServiceApplication; import com.red.circle.component.redis.service.RedisService; import com.red.circle.order.inner.model.enums.MonthlyRechargeType; import com.red.circle.other.app.service.activity.ActivityRechargeTicketService; +import com.red.circle.other.infra.database.rds.dao.activity.UserActivityRechargeDAO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -12,6 +13,9 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ScanOptions; import org.springframework.test.context.junit4.SpringRunner; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; @SpringBootTest(classes = OtherServiceApplication.class) @@ -22,6 +26,14 @@ public class RedisTest { private RedisTemplate redisTemplate; @Autowired private ActivityRechargeTicketService activityRechargeTicketService; + @Autowired + private RedisService redisService; + @Autowired + private UserActivityRechargeDAO userActivityRechargeDAO; + + // 常量定义(与业务代码保持一致) + private static final Long FIXED_ACTIVITY_ID = 2007771533988204877L; + private static final long TICKET_THRESHOLD = 100000L; // 每10000发一张券,请根据实际值修改 @Test public void testRedis(){ @@ -56,4 +68,176 @@ public class RedisTest { } + /** + * 查找脏数据用户ID + * 条件:Redis中记录的threshold值 > 数据库中的totalWinCoins + */ + @Test + public void findDirtyDataUsers() { + System.out.println("==========开始查找脏数据用户=========="); + System.out.println("活动ID: " + FIXED_ACTIVITY_ID); + System.out.println("券阈值: " + TICKET_THRESHOLD); + System.out.println(); + + String pattern = String.format("lucky:draw:threshold:*:%s", FIXED_ACTIVITY_ID); + List dirtyUserIds = new ArrayList<>(); + List dirtyDetails = new ArrayList<>(); + + // 使用 scan 遍历匹配的 key + ScanOptions options = ScanOptions.scanOptions() + .match(pattern) + .count(1000) // 每次扫描1000个 + .build(); + + redisTemplate.execute((RedisCallback) connection -> { + Cursor cursor = connection.scan(options); + int totalCount = 0; + int dirtyCount = 0; + + while (cursor.hasNext()) { + String key = new String(cursor.next()); + totalCount++; + + try { + // 从key中提取userId + // key格式: lucky:draw:threshold:{userId}:{activityId} + String[] parts = key.split(":"); + if (parts.length >= 4) { + Long userId = Long.parseLong(parts[3]); + + // 获取Redis中的threshold值 + String thresholdStr = redisService.getString(key); + if (thresholdStr == null) { + continue; + } + long redisThreshold = Long.parseLong(thresholdStr); + + // 获取数据库中的实际充值总额 + BigDecimal totalWinCoins = userActivityRechargeDAO.getUserTotalAmount( + userId, FIXED_ACTIVITY_ID, null); + long dbTotal = (totalWinCoins != null) ? totalWinCoins.longValue() : 0L; + + // 检查是否为脏数据:Redis中的值 > 数据库中的值 + if (redisThreshold > dbTotal) { + dirtyCount++; + dirtyUserIds.add(userId); + + String detail = String.format( + "脏数据 #%d - 用户ID: %d, Redis阈值: %d, 数据库总额: %d, 差额: %d", + dirtyCount, userId, redisThreshold, dbTotal, (redisThreshold - dbTotal) + ); + dirtyDetails.add(detail); + System.out.println(detail); + } + + // 每处理100条打印进度 + if (totalCount % 100 == 0) { + System.out.println("已扫描: " + totalCount + " 条记录, 发现脏数据: " + dirtyCount + " 条"); + } + } + } catch (Exception e) { + System.err.println("处理key失败: " + key + ", 错误: " + e.getMessage()); + } + } + cursor.close(); + + System.out.println(); + System.out.println("==========扫描完成=========="); + System.out.println("总扫描记录数: " + totalCount); + System.out.println("脏数据记录数: " + dirtyCount); + System.out.println(); + + return null; + }); + + // 输出汇总结果 + if (!dirtyUserIds.isEmpty()) { + System.out.println("==========脏数据用户ID列表=========="); + System.out.println(dirtyUserIds); + System.out.println(); + + System.out.println("==========脏数据详情=========="); + for (String detail : dirtyDetails) { + System.out.println(detail); + } + System.out.println(); + + System.out.println("==========SQL修复语句(仅供参考)=========="); + System.out.println("-- 可以通过以下方式清理Redis中的脏数据:"); + for (Long userId : dirtyUserIds) { + String key = String.format("lucky:draw:threshold:%s:%s", userId, FIXED_ACTIVITY_ID); + System.out.println("-- DEL " + key); + } + } else { + System.out.println("未发现脏数据!"); + } + } + + /** + * 批量修复脏数据(慎用!) + * 将Redis中的threshold值重置为数据库中的实际值 + */ + @Test + public void fixDirtyData() { + System.out.println("==========开始修复脏数据=========="); + System.out.println("警告:此操作将修改Redis数据,请确认后执行!"); + System.out.println(); + + String pattern = String.format("lucky:draw:threshold:*:%s", FIXED_ACTIVITY_ID); + + ScanOptions options = ScanOptions.scanOptions() + .match(pattern) + .count(1000) + .build(); + + redisTemplate.execute((RedisCallback) connection -> { + Cursor cursor = connection.scan(options); + int fixedCount = 0; + + while (cursor.hasNext()) { + String key = new String(cursor.next()); + + try { + String[] parts = key.split(":"); + if (parts.length >= 4) { + Long userId = Long.parseLong(parts[3]); + + String thresholdStr = redisService.getString(key); + if (thresholdStr == null) { + continue; + } + long redisThreshold = Long.parseLong(thresholdStr); + + BigDecimal totalWinCoins = userActivityRechargeDAO.getUserTotalAmount( + userId, FIXED_ACTIVITY_ID, null); + long dbTotal = (totalWinCoins != null) ? totalWinCoins.longValue() : 0L; + + // 如果是脏数据,修复为数据库中的实际值 + if (redisThreshold > dbTotal) { + // 计算应该设置的正确threshold值(向下取整到阈值倍数) + long correctThreshold = 0; + + redisService.setString(key, String.valueOf(correctThreshold)); + fixedCount++; + + System.out.println(String.format( + "已修复 - 用户ID: %d, 原值: %d, 新值: %d, 数据库总额: %d", + userId, redisThreshold, correctThreshold, dbTotal + )); + } + } + } catch (Exception e) { + System.err.println("修复key失败: " + key + ", 错误: " + e.getMessage()); + } + } + cursor.close(); + + System.out.println(); + System.out.println("==========修复完成=========="); + System.out.println("共修复: " + fixedCount + " 条脏数据"); + + return null; + }); + } + }