注销店铺流程风险分析.md 7.8 KB

注销店铺流程风险分析报告

一、已修复的问题 ✅

  1. 时间不一致问题:定时任务从8天改为7天,与通知一致
  2. 重新入驻问题:清理商户用户注销状态,允许重新入驻
  3. 身份验证问题:入驻申请时排除已注销的店铺

二、潜在风险分析 ⚠️

1. 事务性问题 🔴 高风险

问题描述

  • 定时任务删除店铺时没有使用 @Transactional 注解
  • 如果删除过程中某个步骤失败,可能导致数据不一致

影响范围

  • 店铺已删除,但商户用户注销状态未清理 → 无法重新入驻
  • 店铺已删除,但粉丝关系未清理 → 数据冗余
  • 店铺已删除,但Redis地理位置未清理 → 搜索结果包含已删除店铺

风险场景

// 当前代码:没有事务保护
storeUser.setLogoutFlag(0);
storeUserMapper.updateById(storeUser);  // 如果这里失败
storeInfoMapper.deleteById(storeInfo.getId());  // 店铺已删除,但用户状态未更新

建议修复

@Transactional(rollbackFor = Exception.class)
public void cancellationOfBusinessJob() {
    // 删除逻辑
}

2. 异常处理缺失 🔴 高风险

问题描述

  • 定时任务没有 try-catch 异常处理
  • 单个店铺删除失败会导致整个任务中断
  • 后续店铺无法被删除

影响范围

  • 如果第一个店铺删除失败,后续所有店铺都不会被处理
  • 错误信息无法记录,难以排查问题

风险场景

// 当前代码:没有异常处理
for (StoreInfo storeInfo : storeInfos) {
    // 如果这里抛出异常,整个循环中断
    storeInfoMapper.deleteById(storeInfo.getId());
}

建议修复

for (StoreInfo storeInfo : storeInfos) {
    try {
        // 删除逻辑
    } catch (Exception e) {
        log.error("删除店铺失败: storeId={}, error={}", storeInfo.getId(), e.getMessage(), e);
        // 继续处理下一个店铺
    }
}

3. 关联数据清理不完整 🟡 中风险

问题描述

  • 当前只清理了:StoreUser、LifeFans、Redis Token
  • 未清理的数据:
    • Redis 地理位置信息(GEO)
    • 订单数据(LifeUserOrder)
    • 优惠券数据(LifeCoupon、LifeDiscountCoupon)
    • 店铺图片(StoreImg)
    • 评论数据(StoreComment)
    • 通知消息(LifeNotice)
    • 其他业务关联数据

影响范围

  • Redis GEO 中残留已删除店铺 → 搜索结果包含已删除店铺
  • 订单数据残留 → 数据统计不准确
  • 图片数据残留 → 存储空间浪费

建议修复: 参考 MerchantUserServiceImpl.resetToInitialStatus 的完整清理逻辑,补充以下清理:

// 1. 清理Redis地理位置
nearMeService.removeGeolocation(Boolean.TRUE, storeInfo.getId().toString());

// 2. 清理订单数据(根据业务需求决定是否物理删除)
// 3. 清理优惠券数据
// 4. 清理店铺图片
// 5. 清理评论数据
// 6. 清理通知消息

4. 取消注销逻辑不完善 🟡 中风险

问题描述

  • cancelLogoutStore 方法没有检查是否超过7天
  • 如果超过7天,店铺可能已被定时任务删除,取消注销会失败

影响范围

  • 用户在第8天尝试取消注销,但店铺已被删除
  • 可能导致 NullPointerException 或其他异常

当前代码

public void cancelLogoutStore(StoreInfoVo storeInfo) {
    StoreInfo storeIn = storeInfoMapper.selectOne(...);
    // 没有检查 storeIn 是否为 null
    // 没有检查是否超过7天
    storeIn.setLogoutFlag(0);
    // ...
}

建议修复

public void cancelLogoutStore(StoreInfoVo storeInfo) {
    StoreInfo storeIn = storeInfoMapper.selectOne(...);
    if (storeIn == null) {
        throw new BusinessException("店铺不存在或已被删除");
    }
    
    // 检查是否超过7天
    if (storeIn.getLogoutTime() != null) {
        Date logoutTime = storeIn.getLogoutTime();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(logoutTime);
        calendar.add(Calendar.DAY_OF_YEAR, 7);
        if (new Date().after(calendar.getTime())) {
            throw new BusinessException("已超过7天冷静期,无法取消注销");
        }
    }
    
    // 取消注销逻辑
}

5. 并发安全问题 🟡 中风险

问题描述

  • 定时任务没有分布式锁保护
  • 如果多个实例同时执行,可能重复删除

影响范围

  • 多实例部署时,可能重复执行删除操作
  • 虽然 MyBatis-Plus 的 deleteById 是幂等的,但清理操作可能重复执行

建议修复

@XxlJob("cancellationOfBusinessJob")
public void cancellationOfBusinessJob() {
    String lockKey = "job:cancellationOfBusinessJob";
    String lockValue = baseRedisService.lock(lockKey, 300000, 10000); // 5分钟锁,10秒超时
    if (lockValue == null) {
        log.warn("获取分布式锁失败,跳过本次执行");
        return;
    }
    
    try {
        // 删除逻辑
    } finally {
        baseRedisService.unlock(lockKey, lockValue);
    }
}

6. 时间精度问题 🟢 低风险

问题描述

  • 使用 DateCalendar 进行时间比较
  • 可能存在时区或精度问题

影响范围

  • 不同时区可能导致判断不准确
  • 毫秒级精度可能导致边界情况

建议修复: 使用 LocalDateTimeInstant 进行时间计算,更精确:

LocalDateTime logoutTime = storeInfo.getLogoutTime().toInstant()
    .atZone(ZoneId.systemDefault())
    .toLocalDateTime();
LocalDateTime sevenDaysLater = logoutTime.plusDays(7);
LocalDateTime now = LocalDateTime.now();
if (now.isAfter(sevenDaysLater) || now.isEqual(sevenDaysLater)) {
    // 删除逻辑
}

7. 日志记录不完整 🟢 低风险

问题描述

  • 删除操作缺少详细的日志记录
  • 无法追踪删除历史和问题排查

建议修复

log.info("开始删除店铺: storeId={}, storeName={}, logoutTime={}, relatedUsers={}", 
    storeInfo.getId(), storeInfo.getStoreName(), logoutTime, relatedStoreUsers.size());
// ... 删除操作
log.info("店铺删除完成: storeId={}, deletedUsers={}, deletedFans={}", 
    storeInfo.getId(), deletedUsers, deletedFans);

三、修复优先级

优先级 风险项 影响 修复难度
P0 事务性问题 数据不一致
P0 异常处理缺失 任务中断
P1 关联数据清理不完整 数据冗余
P1 取消注销逻辑不完善 用户体验
P2 并发安全问题 重复执行
P3 时间精度问题 边界情况
P3 日志记录不完整 问题排查

四、建议修复方案

方案1:完整修复(推荐)

  1. 添加事务保护
  2. 添加异常处理
  3. 补充关联数据清理
  4. 完善取消注销逻辑
  5. 添加分布式锁
  6. 优化时间计算
  7. 完善日志记录

方案2:快速修复(最小改动)

  1. 添加异常处理(最重要)
  2. 添加事务保护
  3. 完善取消注销逻辑

五、测试建议

  1. 单元测试

    • 测试7天边界情况(6天23小时59分 vs 7天0分1秒)
    • 测试删除过程中的异常情况
  2. 集成测试

    • 测试定时任务执行
    • 测试取消注销功能
    • 测试重新入驻功能
  3. 压力测试

    • 测试大量店铺同时删除
    • 测试并发执行情况

六、监控建议

  1. 定时任务监控

    • 监控任务执行时间
    • 监控删除数量
    • 监控失败次数
  2. 数据一致性监控

    • 监控 logoutFlag=1 且超过7天的店铺数量
    • 监控 Redis GEO 中的店铺数量
  3. 告警设置

    • 定时任务执行失败告警
    • 删除数量异常告警