# 注销店铺流程完整性分析报告 ## 一、流程概览 ``` 1. 商家提交注销申请 (logoutStore) ↓ 2. 设置店铺状态为注销中 (storeStatus=-1, logoutFlag=1) ↓ 3. 发送通知(7天冷静期) ↓ 4. 7天内可撤回 (cancelLogoutStore) ↓ 5. 超过7天后,定时任务自动删除店铺数据 (cancellationOfBusinessJob) ``` ## 二、当前实现分析 ### 1. 注销申请接口 ✅ **文件:** `StoreInfoServiceImpl.java:2691-2726` **实现逻辑:** - ✅ 设置 `storeStatus = -1`(注销中) - ✅ 设置 `logoutFlag = 1`(注销标记) - ✅ 记录 `logoutTime`(注销申请时间) - ✅ 记录 `logoutReason`(注销原因) - ✅ 记录 `logoutCode`(注销code) - ✅ 发送WebSocket通知 **问题:** - ⚠️ 通知内容说"7天冷静期",但定时任务判断是"8天后"删除(不一致) ### 2. 取消注销接口 ✅ **文件:** `StoreInfoServiceImpl.java:2743-2773` **实现逻辑:** - ✅ 恢复 `logoutFlag = 0` - ✅ 恢复 `storeStatus = 1`(可用) - ✅ 清空注销相关字段 - ✅ 发送撤回通知 **问题:** - ❌ **缺少状态检查**:没有检查是否还在冷静期内(理论上超过7天就不应该允许撤回) - ❌ **缺少时间验证**:没有验证 `logoutTime` 是否超过7天 ### 3. 定时任务删除 ⚠️ **文件:** `StoreMembershipCardJob.java:122-198` **实现逻辑:** - ✅ 查询 `logoutFlag = 1` 的店铺 - ✅ 判断 `logoutTime` 是否超过8天 - ✅ 删除店铺记录 - ✅ 清空 `StoreUser.storeId` - ✅ 删除粉丝关系 - ✅ 删除Redis token **问题:** - ❌ **时间不一致**:通知说7天冷静期,但判断是8天后删除(应该改为7天) - ❌ **数据清理不完整**:只清理了基础数据,缺少以下关联数据: - 订单数据(`life_user_order`) - 订单-优惠券中间表(`order_coupon_middle`) - 优惠券(`life_discount_coupon`) - 代金券(`life_coupon`) - 用户领取的优惠券(`life_discount_coupon_user`) - 团购券(`life_group_buy_main`) - 评论(`store_comment`、`life_comment`) - 通知消息(`life_notice`) - 店铺图片(`store_img`) - 店铺标签关系(`tag_store_relation`) - 其他关联数据 - ❌ **缺少事务保护**:没有 `@Transactional`,可能导致部分数据删除失败 - ❌ **缺少异常处理**:没有 try-catch,一个店铺删除失败会影响其他店铺 - ❌ **缺少详细日志**:删除操作缺少详细的日志记录 - ❌ **缺少状态检查**:删除前没有再次检查 `logoutFlag` 是否为1(可能已被撤回) ## 三、发现的问题汇总 ### 🔴 严重问题 1. **数据清理不完整** - 定时任务只清理了基础数据(StoreInfo、StoreUser、LifeFans、Redis) - 缺少订单、优惠券、商品、评论等大量关联数据的清理 - 参考 `MerchantUserServiceImpl.resetToInitialStatus` 应该清理更多数据 2. **时间逻辑不一致** - 通知说"7天冷静期" - 定时任务判断"8天后"删除 - 应该统一为7天 3. **缺少事务保护** - 定时任务没有事务,可能导致部分数据删除失败,造成数据不一致 ### 🟡 中等问题 4. **缺少状态检查** - 取消注销时没有检查是否还在冷静期内 - 定时任务删除前没有再次检查 `logoutFlag` 是否为1 5. **缺少异常处理** - 定时任务没有 try-catch,一个店铺删除失败会影响其他店铺 - 应该每个店铺单独处理,失败不影响其他 6. **缺少详细日志** - 删除操作缺少详细的日志记录 - 无法追踪删除过程和失败原因 ### 🟢 轻微问题 7. **代码可读性** - 定时任务中变量命名 `sevenDay` 实际是8天后的时间,容易误导 ## 四、建议修复方案 ### 1. 修复时间逻辑 ```java // 当前代码(错误) calendar.add(Calendar.DAY_OF_YEAR, 8); // 8天后删除 // 应该改为 calendar.add(Calendar.DAY_OF_YEAR, 7); // 7天后删除 ``` ### 2. 完善数据清理 参考 `MerchantUserServiceImpl.resetToInitialStatus`,添加以下清理逻辑: ```java // 1. 查询订单ID列表 List orders = lifeUserOrderMapper.selectList( new LambdaQueryWrapper().eq(LifeUserOrder::getStoreId, storeId) ); List orderIds = orders.stream().map(o -> o.getId().toString()).collect(Collectors.toList()); // 2. 删除订单-优惠券中间表 if (!orderIds.isEmpty()) { orderCouponMiddleMapper.delete( new LambdaQueryWrapper().in(OrderCouponMiddle::getOrderId, orderIds) ); } // 3. 删除订单 lifeUserOrderMapper.delete( new LambdaQueryWrapper().eq(LifeUserOrder::getStoreId, storeId) ); // 4. 删除优惠券 lifeDiscountCouponMapper.delete( new LambdaQueryWrapper().eq(LifeDiscountCoupon::getStoreId, storeId) ); // 5. 删除代金券 lifeCouponMapper.delete( new LambdaQueryWrapper().eq(LifeCoupon::getStoreId, storeId) ); // 6. 删除团购券 lifeGroupBuyMainMapper.delete( new LambdaQueryWrapper().eq(LifeGroupBuyMain::getStoreId, storeId) ); // 7. 删除通知消息 lifeNoticeMapper.delete( new LambdaQueryWrapper().eq(LifeNotice::getReceiverId, "store_" + storeUser.getPhone()) ); // 8. 删除店铺图片 storeImgMapper.delete( new LambdaQueryWrapper().eq(StoreImg::getStoreId, storeId) ); // 9. 删除标签关系 tagStoreRelationMapper.delete( new LambdaQueryWrapper().eq(TagStoreRelation::getStoreId, storeId) ); // 10. 删除评论(根据实际表结构调整) // storeCommentMapper.delete(...) // lifeCommentMapper.delete(...) ``` ### 3. 添加事务保护 ```java @XxlJob("cancellationOfBusinessJob") @Transactional(rollbackFor = Exception.class) public void cancellationOfBusinessJob() { // ... 删除逻辑 } ``` ### 4. 添加异常处理 ```java for (StoreInfo storeInfo : storeInfos) { try { // 再次检查状态 if (storeInfo.getLogoutFlag() != 1) { log.warn("店铺 {} 的注销状态已变更,跳过删除", storeInfo.getId()); continue; } // 检查时间 if (storeInfo.getLogoutTime() == null) { log.warn("店铺 {} 的注销时间为空,跳过删除", storeInfo.getId()); continue; } // 计算7天后的时间 Calendar calendar = Calendar.getInstance(); calendar.setTime(storeInfo.getLogoutTime()); calendar.add(Calendar.DAY_OF_YEAR, 7); // 改为7天 Date sevenDayLater = calendar.getTime(); if (new Date().compareTo(sevenDayLater) >= 0) { // 执行删除逻辑 deleteStoreData(storeInfo); log.info("成功删除店铺 {} 及其关联数据", storeInfo.getId()); } } catch (Exception e) { log.error("删除店铺 {} 失败: {}", storeInfo.getId(), e.getMessage(), e); // 继续处理下一个店铺,不中断整个任务 } } ``` ### 5. 添加取消注销时的状态检查 ```java public void cancelLogoutStore(StoreInfoVo storeInfo) { StoreInfo storeIn = storeInfoMapper.selectOne( new LambdaQueryWrapper().eq(StoreInfo::getId, storeInfo.getId()) ); if (storeIn == null) { throw new IllegalArgumentException("店铺不存在"); } // 检查是否还在冷静期内 if (storeIn.getLogoutTime() != null) { Calendar calendar = Calendar.getInstance(); calendar.setTime(storeIn.getLogoutTime()); calendar.add(Calendar.DAY_OF_YEAR, 7); Date sevenDayLater = calendar.getTime(); if (new Date().compareTo(sevenDayLater) >= 0) { throw new IllegalStateException("已超过7天冷静期,无法撤回注销申请"); } } // 检查当前状态 if (storeIn.getLogoutFlag() != 1) { throw new IllegalStateException("店铺未处于注销状态,无法撤回"); } // ... 后续恢复逻辑 } ``` ## 五、修复优先级 1. **P0(必须修复)** - 修复时间逻辑(7天 vs 8天) - 完善数据清理逻辑 - 添加异常处理 2. **P1(重要)** - 添加事务保护 - 添加状态检查 - 添加详细日志 3. **P2(优化)** - 代码可读性优化 - 性能优化(批量删除) ## 六、测试建议 1. **单元测试** - 测试注销申请后状态是否正确 - 测试7天内取消注销是否成功 - 测试7天后取消注销是否失败 - 测试定时任务是否正确删除数据 2. **集成测试** - 测试完整注销流程 - 测试数据清理是否完整 - 测试异常情况处理 3. **数据验证** - 验证删除后数据库中是否还有残留数据 - 验证关联数据是否全部清理