# 注销店铺流程风险分析报告 ## 一、已修复的问题 ✅ 1. **时间不一致问题**:定时任务从8天改为7天,与通知一致 2. **重新入驻问题**:清理商户用户注销状态,允许重新入驻 3. **身份验证问题**:入驻申请时排除已注销的店铺 --- ## 二、潜在风险分析 ⚠️ ### 1. **事务性问题** 🔴 高风险 **问题描述**: - 定时任务删除店铺时没有使用 `@Transactional` 注解 - 如果删除过程中某个步骤失败,可能导致数据不一致 **影响范围**: - 店铺已删除,但商户用户注销状态未清理 → 无法重新入驻 - 店铺已删除,但粉丝关系未清理 → 数据冗余 - 店铺已删除,但Redis地理位置未清理 → 搜索结果包含已删除店铺 **风险场景**: ```java // 当前代码:没有事务保护 storeUser.setLogoutFlag(0); storeUserMapper.updateById(storeUser); // 如果这里失败 storeInfoMapper.deleteById(storeInfo.getId()); // 店铺已删除,但用户状态未更新 ``` **建议修复**: ```java @Transactional(rollbackFor = Exception.class) public void cancellationOfBusinessJob() { // 删除逻辑 } ``` --- ### 2. **异常处理缺失** 🔴 高风险 **问题描述**: - 定时任务没有 try-catch 异常处理 - 单个店铺删除失败会导致整个任务中断 - 后续店铺无法被删除 **影响范围**: - 如果第一个店铺删除失败,后续所有店铺都不会被处理 - 错误信息无法记录,难以排查问题 **风险场景**: ```java // 当前代码:没有异常处理 for (StoreInfo storeInfo : storeInfos) { // 如果这里抛出异常,整个循环中断 storeInfoMapper.deleteById(storeInfo.getId()); } ``` **建议修复**: ```java 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` 的完整清理逻辑,补充以下清理: ```java // 1. 清理Redis地理位置 nearMeService.removeGeolocation(Boolean.TRUE, storeInfo.getId().toString()); // 2. 清理订单数据(根据业务需求决定是否物理删除) // 3. 清理优惠券数据 // 4. 清理店铺图片 // 5. 清理评论数据 // 6. 清理通知消息 ``` --- ### 4. **取消注销逻辑不完善** 🟡 中风险 **问题描述**: - `cancelLogoutStore` 方法没有检查是否超过7天 - 如果超过7天,店铺可能已被定时任务删除,取消注销会失败 **影响范围**: - 用户在第8天尝试取消注销,但店铺已被删除 - 可能导致 `NullPointerException` 或其他异常 **当前代码**: ```java public void cancelLogoutStore(StoreInfoVo storeInfo) { StoreInfo storeIn = storeInfoMapper.selectOne(...); // 没有检查 storeIn 是否为 null // 没有检查是否超过7天 storeIn.setLogoutFlag(0); // ... } ``` **建议修复**: ```java 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` 是幂等的,但清理操作可能重复执行 **建议修复**: ```java @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. **时间精度问题** 🟢 低风险 **问题描述**: - 使用 `Date` 和 `Calendar` 进行时间比较 - 可能存在时区或精度问题 **影响范围**: - 不同时区可能导致判断不准确 - 毫秒级精度可能导致边界情况 **建议修复**: 使用 `LocalDateTime` 或 `Instant` 进行时间计算,更精确: ```java 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. **日志记录不完整** 🟢 低风险 **问题描述**: - 删除操作缺少详细的日志记录 - 无法追踪删除历史和问题排查 **建议修复**: ```java 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. **告警设置**: - 定时任务执行失败告警 - 删除数量异常告警