Forráskód Böngészése

Merge branch 'sit' into sit-lutong-dev

# Conflicts:
#	alien-job/src/main/java/shop/alien/job/feign/AlienStoreFeign.java
lutong 6 órája
szülő
commit
6fb3133f7a
17 módosított fájl, 795 hozzáadás és 448 törlés
  1. 14 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java
  2. 25 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/AccountLogoutCompleteRequestDTO.java
  3. 29 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/AccountLogoutCompleteResultVo.java
  4. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/PerformanceGuestVo.java
  5. 6 0
      alien-job/src/main/java/shop/alien/job/feign/AlienStoreFeign.java
  6. 18 251
      alien-job/src/main/java/shop/alien/job/store/StoreMembershipCardJob.java
  7. 2 84
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreBusinessServiceImpl.java
  8. 32 0
      alien-store/src/main/java/shop/alien/store/controller/AccountLogoutJobController.java
  9. 52 0
      alien-store/src/main/java/shop/alien/store/controller/AccountLogoutTestController.java
  10. 16 0
      alien-store/src/main/java/shop/alien/store/service/AccountLogoutCompleteService.java
  11. 25 0
      alien-store/src/main/java/shop/alien/store/service/StoreLogoutSyncService.java
  12. 292 0
      alien-store/src/main/java/shop/alien/store/service/impl/AccountLogoutCompleteServiceImpl.java
  13. 1 0
      alien-store/src/main/java/shop/alien/store/service/impl/PerformanceListServiceImpl.java
  14. 2 113
      alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java
  15. 162 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreLogoutSyncServiceImpl.java
  16. 6 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreUserServiceImpl.java
  17. 109 0
      alien-util/src/main/java/shop/alien/util/common/AccountLogoutUtil.java

+ 14 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java

@@ -21,6 +21,20 @@ import java.util.Date;
 @ApiModel(value = "StoreInfo对象", description = "门店信息")
 public class StoreInfo {
 
+    /** 注销标记:0-未注销 */
+    public static final int LOGOUT_FLAG_NORMAL = 0;
+    /** 注销标记:1-已注销 */
+    public static final int LOGOUT_FLAG_DONE = 1;
+    /** 注销标记:2-注销冷静期 */
+    public static final int LOGOUT_FLAG_COOLING = 2;
+
+    /** 营业状态:0-正常营业 */
+    public static final int BUSINESS_STATUS_NORMAL = 0;
+    /** 营业状态:1-暂停营业(注销冷静期) */
+    public static final int BUSINESS_STATUS_PAUSE = 1;
+    /** 营业状态:99-永久关门(注销完成) */
+    public static final int BUSINESS_STATUS_PERMANENT_CLOSE = 99;
+
     @ApiModelProperty(value = "主键")
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;

+ 25 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/AccountLogoutCompleteRequestDTO.java

@@ -0,0 +1,25 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 注销冷静期完成(测试/手动触发)请求
+ */
+@Data
+@ApiModel("注销冷静期完成请求")
+public class AccountLogoutCompleteRequestDTO {
+
+    @ApiModelProperty(value = "账号类型:ALL/STORE_USER/LIFE_USER/LAWYER/STORE,默认 ALL", example = "ALL")
+    private String accountType;
+
+    @ApiModelProperty("主键ID(与 accountType 对应:商家用户ID/用户ID/律师ID/店铺ID)")
+    private Integer id;
+
+    @ApiModelProperty("手机号(STORE_USER/LIFE_USER/LAWYER 可按手机号筛选单条)")
+    private String phone;
+
+    @ApiModelProperty(value = "是否跳过7天冷静期校验(测试接口默认 true)", example = "true")
+    private Boolean force;
+}

+ 29 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/AccountLogoutCompleteResultVo.java

@@ -0,0 +1,29 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@ApiModel("注销冷静期完成结果")
+public class AccountLogoutCompleteResultVo {
+
+    @ApiModelProperty("成功条数")
+    private int successCount;
+
+    @ApiModelProperty("失败条数")
+    private int failCount;
+
+    @ApiModelProperty("跳过条数(未满7天且未 force)")
+    private int skippedCount;
+
+    @ApiModelProperty("明细")
+    private List<String> details = new ArrayList<>();
+
+    public void addDetail(String detail) {
+        details.add(detail);
+    }
+}

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/PerformanceGuestVo.java

@@ -56,5 +56,9 @@ public class PerformanceGuestVo implements Serializable {
      */
     @ApiModelProperty(value = "点赞数量")
     private Integer likeCount;
+
+
+    @ApiModelProperty(value = "标签")
+    private  String tag;
 }
 

+ 6 - 0
alien-job/src/main/java/shop/alien/job/feign/AlienStoreFeign.java

@@ -128,6 +128,12 @@ public interface AlienStoreFeign {
     R<Integer> syncPushTaskStatistics();
 
     /**
+     * 注销冷静期到期处理(cancellationOfBusinessJob,满7天才会处理)
+     */
+    @PostMapping("/account/logout/job/completeCooling")
+    R<AccountLogoutCompleteResultVo> completeAccountLogoutCooling();
+
+    /**
      * 平台埋点:零点归档昨日明细
      */
     @PostMapping("/analytics/job/archiveDaily")

+ 18 - 251
alien-job/src/main/java/shop/alien/job/store/StoreMembershipCardJob.java

@@ -1,23 +1,21 @@
 package shop.alien.job.store;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.xxl.job.core.handler.annotation.XxlJob;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
-import shop.alien.config.redis.BaseRedisService;
-import shop.alien.entity.store.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreMembershipCard;
+import shop.alien.entity.store.StoreMembershipCardOrder;
+import shop.alien.entity.store.vo.AccountLogoutCompleteResultVo;
 import shop.alien.job.feign.AlienStoreFeign;
-import shop.alien.mapper.*;
-import shop.alien.util.common.constant.CouponStatusEnum;
-import shop.alien.util.common.constant.DiscountCouponEnum;
-import shop.alien.util.type.LifeNoticeUtil;
+import shop.alien.mapper.StoreMembershipCardMapper;
+import shop.alien.mapper.StoreMembershipCardOrderMapper;
 
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
-import java.util.stream.Collectors;
 
 @Slf4j
 @Component
@@ -44,40 +42,12 @@ public class StoreMembershipCardJob {
     private final static String MEMBERSHIP_CARD_BYE_TYPE_HALF_YEAR = "2";
     //会员卡购买类型-年卡
     private final static String MEMBERSHIP_CARD_BYE_TYPE_YEAR = "3";
-    // 商家账号注销:0-未注销,1-已注销,2-注销冷静期
-    private final static Integer LOGOUT_FLAG_DONE = StoreUser.LOGOUT_FLAG_DONE;
-    private final static Integer LOGOUT_FLAG_COOLING = StoreUser.LOGOUT_FLAG_COOLING;
-    private static final String LOGOUT_DISPLAY_NAME = "已注销";
-    private static final int LOGOUT_COOLING_DAYS = 7;
-
     private final StoreMembershipCardMapper storeMembershipCardMapper;
 
     private final StoreMembershipCardOrderMapper storeMembershipCardOrderMapper;
 
-    private final StoreUserMapper storeUserMapper;
-
-    private final StoreInfoMapper storeInfoMapper;
-
-    private final BaseRedisService baseRedisService;
-
-    private final LifeUserMapper lifeUserMapper;
-
-    private final LifeFansMapper lifeFansMapper;
-
-    private final LawyerUserMapper lawyerUserMapper;
-
-    private final LifeNoticeMapper lifeNoticeMapper;
-
-    private final LifeNoticeUtil lifeNoticeUtil;
-
     private final AlienStoreFeign alienStoreFeign;
 
-    private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
-
-    private final LifeDiscountCouponUserMapper lifeDiscountCouponUserMapper;
-
-    private final LifeCouponMapper lifeCouponMapper;
-
     /**
      * 会员卡状态及会员卡订单状态变更任务
      */
@@ -141,222 +111,19 @@ public class StoreMembershipCardJob {
      */
     @XxlJob("cancellationOfBusinessJob")
     public void cancellationOfBusinessJob() {
-        log.info("处理已申请注销超过7天的商家、用户与律师: " + new Date());
-        int successCount = 0;
-        int failCount = 0;
-
-        // 获取处于注销冷静期的商家用户(含旧数据 logout_flag=1 且 status=-1)
-        List<StoreUser> storeUsers = storeUserMapper.selectList(new LambdaQueryWrapper<StoreUser>()
-                .and(w -> w.eq(StoreUser::getLogoutFlag, LOGOUT_FLAG_COOLING)
-                        .or(o -> o.eq(StoreUser::getLogoutFlag, LOGOUT_FLAG_DONE).eq(StoreUser::getStatus, -1))));
-        for (StoreUser storeUser : storeUsers) {
-            try {
-                if (null != storeUser.getLogoutTime()) {
-                    if (isLogoutCoolingExpired(storeUser.getLogoutTime())) {
-                        log.info("商家账号冷静期结束,更新注销标记为已注销: userId={}, phone={}, logoutTime={}",
-                                storeUser.getId(), storeUser.getPhone(), storeUser.getLogoutTime());
-                        storeUser.setLogoutFlag(LOGOUT_FLAG_DONE);
-                        storeUser.setName(LOGOUT_DISPLAY_NAME);
-                        storeUser.setNickName(LOGOUT_DISPLAY_NAME);
-                        storeUser.setHeadImg(null);
-                        storeUserMapper.updateById(storeUser);
-                        successCount++;
-                    }
-                }
-            } catch (Exception e) {
-                failCount++;
-                log.error("商家账号注销状态更新失败: userId={}, phone={}, error={}",
-                        storeUser.getId(), storeUser.getPhone(), e.getMessage(), e);
-            }
-        }
-
-        // 获取全部申请注销的店铺
-        List<StoreInfo> storeInfos = storeInfoMapper.selectList(new LambdaQueryWrapper<StoreInfo>().eq(StoreInfo::getLogoutFlag, LOGOUT_FLAG_DONE));
-        for (StoreInfo storeInfo : storeInfos) {
-            try {
-                if (null != storeInfo.getLogoutTime()) {
-                    // 获取申请注销时间
-                    Date logoutTime = storeInfo.getLogoutTime();
-                    // 获取申请注销 7 天后的时间(与通知一致)
-                    Calendar calendar = Calendar.getInstance();
-                    calendar.setTime(logoutTime);
-                    calendar.add(Calendar.DAY_OF_YEAR, 7);
-                    Date sevenDay = calendar.getTime();
-                    // 获取当前时间
-                    Date date = new Date();
-                    if (date.compareTo(sevenDay) >= 0) {
-                        log.info("开始删除已注销超过7天的店铺: storeId={}, storeName={}, logoutTime={}", 
-                                storeInfo.getId(), storeInfo.getStoreName(), logoutTime);
-
-                        invalidateStoreCoupons(storeInfo.getId());
-                        
-                        // 先清理关联的商户用户注销状态,确保可以重新入驻
-                        List<StoreUser> relatedStoreUsers = storeUserMapper.selectList(
-                                new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeInfo.getId()));
-                        int deletedUsers = 0;
-                        int deletedFans = 0;
-                        
-                        for (StoreUser storeUser : relatedStoreUsers) {
-                            try {
-                                // 清理注销标记和时间,允许重新入驻
-                                storeUser.setLogoutFlag(0);
-                                storeUser.setLogoutTime(null);
-                                storeUser.setStoreId(null);  // 解除店铺绑定
-                                storeUserMapper.updateById(storeUser);
-                                deletedUsers++;
-                                log.info("清理商户用户注销状态: userId={}, phone={}", storeUser.getId(), storeUser.getPhone());
-                            } catch (Exception e) {
-                                log.error("清理商户用户注销状态失败: userId={}, phone={}, error={}", 
-                                        storeUser.getId(), storeUser.getPhone(), e.getMessage(), e);
-                            }
-                        }
-                        
-                        // 清理粉丝关系
-                        for (StoreUser storeUser : relatedStoreUsers) {
-                            try {
-                                if (storeUser.getPhone() != null) {
-                                    LambdaQueryWrapper<LifeFans> queryWrapper = new LambdaQueryWrapper<LifeFans>()
-                                            .eq(LifeFans::getDeleteFlag, 0)
-                                            .and(w -> w
-                                                    .nested(n -> n.eq(LifeFans::getFollowedUserType, 2)
-                                                            .eq(LifeFans::getFollowedRefId, storeUser.getId()))
-                                                    .or()
-                                                    .nested(n -> n.eq(LifeFans::getFansUserType, 2)
-                                                            .eq(LifeFans::getFansRefId, storeUser.getId())));
-                                    int deleted = lifeFansMapper.delete(queryWrapper);
-                                    deletedFans += deleted;
-                                }
-                            } catch (Exception e) {
-                                log.error("清理粉丝关系失败: userId={}, phone={}, error={}", 
-                                        storeUser.getId(), storeUser.getPhone(), e.getMessage(), e);
-                            }
-                        }
-                        
-                        // 清理Redis地理位置(通过Feign调用)
-                        try {
-                            alienStoreFeign.delMer(Boolean.TRUE, storeInfo.getId().toString());
-                        } catch (Exception e) {
-                            log.warn("清理Redis地理位置失败: storeId={}, error={}", storeInfo.getId(), e.getMessage());
-                        }
-                        
-                        // 最后删除店铺记录
-                        storeInfoMapper.deleteById(storeInfo.getId());
-                        successCount++;
-                        log.info("店铺删除完成: storeId={}, deletedUsers={}, deletedFans={}", 
-                                storeInfo.getId(), deletedUsers, deletedFans);
-                    }
-                }
-            } catch (Exception e) {
-                failCount++;
-                log.error("删除店铺失败: storeId={}, storeName={}, error={}", 
-                        storeInfo.getId(), storeInfo.getStoreName(), e.getMessage(), e);
-            }
-        }
-
-        // 获取处于注销冷静期的 C 端用户
-        List<LifeUser> lifeUsers = lifeUserMapper.selectList(
-                new LambdaQueryWrapper<LifeUser>().eq(LifeUser::getLogoutFlag, LifeUser.LOGOUT_FLAG_COOLING));
-        for (LifeUser lifeUser : lifeUsers) {
-            try {
-                if (null != lifeUser.getLogoutTime()) {
-                    if (isLogoutCoolingExpired(lifeUser.getLogoutTime())) {
-                        lifeUserMapper.update(null, new LambdaUpdateWrapper<LifeUser>()
-                                .eq(LifeUser::getId, lifeUser.getId())
-                                .set(LifeUser::getLogoutFlag, LifeUser.LOGOUT_FLAG_DONE)
-                                .set(LifeUser::getDeleteFlag, 1)
-                                .set(LifeUser::getUserName, LOGOUT_DISPLAY_NAME)
-                                .set(LifeUser::getUserImage, null));
-                        successCount++;
-                    }
-                }
-            } catch (Exception e) {
-                failCount++;
-                log.error("C端用户注销状态更新失败: userId={}, phone={}, error={}",
-                        lifeUser.getId(), lifeUser.getUserPhone(), e.getMessage(), e);
+        log.info("处理已申请注销超过7天的商家、用户与律师: {}", new Date());
+        try {
+            R<AccountLogoutCompleteResultVo> result = alienStoreFeign.completeAccountLogoutCooling();
+            if (result != null && result.getData() != null) {
+                AccountLogoutCompleteResultVo data = result.getData();
+                log.info("注销冷静期到期处理完成: success={}, fail={}, skipped={}, details={}",
+                        data.getSuccessCount(), data.getFailCount(), data.getSkippedCount(), data.getDetails());
+            } else {
+                log.warn("注销冷静期到期处理返回为空: {}", result);
             }
+        } catch (Exception e) {
+            log.error("注销冷静期到期处理失败", e);
+            throw e;
         }
-
-        // 获取处于注销冷静期的律师用户
-        List<LawyerUser> lawyerUsers = lawyerUserMapper.selectList(new LambdaQueryWrapper<LawyerUser>()
-                .eq(LawyerUser::getLogoutFlag, LawyerUser.LOGOUT_FLAG_COOLING)
-                .eq(LawyerUser::getDeleteFlag, 0));
-        for (LawyerUser lawyerUser : lawyerUsers) {
-            try {
-                if (lawyerUser.getLogoutTime() != null) {
-                    if (isLogoutCoolingExpired(lawyerUser.getLogoutTime())) {
-                        log.info("律师账号冷静期结束,更新注销标记为已注销: lawyerId={}, phone={}, logoutTime={}",
-                                lawyerUser.getId(), lawyerUser.getPhone(), lawyerUser.getLogoutTime());
-                        lawyerUserMapper.update(null, new LambdaUpdateWrapper<LawyerUser>()
-                                .eq(LawyerUser::getId, lawyerUser.getId())
-                                .set(LawyerUser::getLogoutFlag, LawyerUser.LOGOUT_FLAG_DONE)
-                                .set(LawyerUser::getName, LOGOUT_DISPLAY_NAME)
-                                .set(LawyerUser::getNickName, LOGOUT_DISPLAY_NAME)
-                                .set(LawyerUser::getHeadImg, null)
-                                .set(LawyerUser::getOrderReceivingStatus, 0)
-                                .set(LawyerUser::getIsOnline, 0)
-                                .set(LawyerUser::getIsRecommended, 0)
-                                .set(LawyerUser::getLastOnlineTime, new Date()));
-                        successCount++;
-                    }
-                }
-            } catch (Exception e) {
-                failCount++;
-                log.error("律师账号注销状态更新失败: lawyerId={}, phone={}, error={}",
-                        lawyerUser.getId(), lawyerUser.getPhone(), e.getMessage(), e);
-            }
-        }
-        
-        log.info("定时任务执行完成: 成功={}, 失败={}", successCount, failCount);
-    }
-
-    private static boolean isLogoutCoolingExpired(Date logoutTime) {
-        Calendar calendar = Calendar.getInstance();
-        calendar.setTime(logoutTime);
-        calendar.add(Calendar.DAY_OF_YEAR, LOGOUT_COOLING_DAYS);
-        return new Date().compareTo(calendar.getTime()) >= 0;
-    }
-
-    /**
-     * 店铺注销冷静期结束:将该店铺赠出/发行的优惠券全部置为已失效
-     */
-    private void invalidateStoreCoupons(Integer storeId) {
-        if (storeId == null) {
-            return;
-        }
-        String storeIdStr = String.valueOf(storeId);
-        int voidedUserCouponStatus = Integer.parseInt(DiscountCouponEnum.HAVE_BEEN_VOIDED.getValue());
-
-        lifeDiscountCouponMapper.update(null, new LambdaUpdateWrapper<LifeDiscountCoupon>()
-                .eq(LifeDiscountCoupon::getStoreId, storeIdStr)
-                .set(LifeDiscountCoupon::getCouponStatus, LifeDiscountCoupon.COUPON_STATUS_INVALID)
-                .set(LifeDiscountCoupon::getGetStatus, 0));
-
-        List<LifeDiscountCoupon> storeCoupons = lifeDiscountCouponMapper.selectList(
-                new LambdaQueryWrapper<LifeDiscountCoupon>().eq(LifeDiscountCoupon::getStoreId, storeIdStr));
-        if (!storeCoupons.isEmpty()) {
-            List<Integer> couponIds = storeCoupons.stream().map(LifeDiscountCoupon::getId).collect(Collectors.toList());
-            lifeDiscountCouponUserMapper.update(null, new LambdaUpdateWrapper<LifeDiscountCouponUser>()
-                    .in(LifeDiscountCouponUser::getCouponId, couponIds)
-                    .eq(LifeDiscountCouponUser::getStatus, Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()))
-                    .set(LifeDiscountCouponUser::getStatus, voidedUserCouponStatus));
-        }
-
-        lifeCouponMapper.update(null, new LambdaUpdateWrapper<LifeCoupon>()
-                .eq(LifeCoupon::getStoreId, storeIdStr)
-                .notIn(LifeCoupon::getStatus, CouponStatusEnum.ENDED.getCode(), CouponStatusEnum.INVALID.getCode())
-                .set(LifeCoupon::getStatus, CouponStatusEnum.INVALID.getCode()));
-
-        List<LifeCoupon> storeVouchers = lifeCouponMapper.selectList(
-                new LambdaQueryWrapper<LifeCoupon>().eq(LifeCoupon::getStoreId, storeIdStr));
-        if (!storeVouchers.isEmpty()) {
-            List<String> voucherIds = storeVouchers.stream().map(LifeCoupon::getId).collect(Collectors.toList());
-            lifeDiscountCouponUserMapper.update(null, new LambdaUpdateWrapper<LifeDiscountCouponUser>()
-                    .in(LifeDiscountCouponUser::getVoucherId, voucherIds)
-                    .eq(LifeDiscountCouponUser::getStatus, Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()))
-                    .set(LifeDiscountCouponUser::getStatus, voidedUserCouponStatus));
-        }
-
-        log.info("店铺优惠券已失效处理完成: storeId={}, templateCount={}, voucherCount={}",
-                storeId, storeCoupons.size(), storeVouchers.size());
     }
 }

+ 2 - 84
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreBusinessServiceImpl.java

@@ -1881,94 +1881,12 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
 
     @Override
     public void logoutStore(StoreInfoVo storeInfo) {
-        // 通过id获取当前店铺信息
-        LambdaQueryWrapper<StoreInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
-        lambdaQueryWrapper.eq(StoreInfo::getId, storeInfo.getId());
-        StoreInfo storeIn = storeInfoMapper.selectOne(lambdaQueryWrapper);
-        if (storeIn != null) {
-            // 添加注销原因
-            storeIn.setLogoutReason(storeInfo.getLogoutReason());
-            // 添加注销code
-            storeIn.setLogoutCode(storeInfo.getLogoutCode());
-            // 注销中状态
-            storeIn.setStoreStatus(-1);
-            // 添加注销申请时间
-            storeIn.setLogoutTime(new Date());
-            // 更新logout_flag状态为1
-            storeIn.setLogoutFlag(1);
-            int num = storeInfoMapper.updateById(storeIn);
-            if (num > 0) {
-                // 发送通知
-                LifeNotice lifeMessage = getLifeNotice(storeInfo);
-                lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
-                lifeNoticeMapper.insert(lifeMessage);
-                WebSocketVo websocketVo = new WebSocketVo();
-                websocketVo.setSenderId("system");
-                websocketVo.setReceiverId("store_" + storeInfo.getStorePhone());
-                websocketVo.setCategory("notice");
-                websocketVo.setNoticeType("1");
-                websocketVo.setIsRead(0);
-                websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeMessage).toJSONString());
-            }
-        }
-    }
-
-    private static LifeNotice getLifeNotice(StoreInfoVo storeInfo) {
-        LifeNotice lifeMessage = new LifeNotice();
-        lifeMessage.setReceiverId("store_" + storeInfo.getStorePhone());
-        String text = "您提交的店铺注销申请已成功接收,系统将按流程进行处理。\n\n注销申请提交后,将进入7天的冷静期,期间您可随时在【我的设置】-【注销店铺】中撤回申请。冷静期结束后,系统将正式开始注销操作,店铺内所有数据将被永久清除,且无法恢复。\n\n如有疑问,可联系客服咨询,感谢您使用我们的服务。";
-        com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
-        jsonObject.put("message", text);
-        lifeMessage.setContext(jsonObject.toJSONString());
-        lifeMessage.setTitle("注销店铺通知");
-        lifeMessage.setSenderId("system");
-        lifeMessage.setIsRead(0);
-        lifeMessage.setNoticeType(1);
-        return lifeMessage;
+        throw new IllegalArgumentException("店铺注销已合并至商家账号注销,请通过商家账号注销接口操作");
     }
 
     @Override
     public void cancelLogoutStore(StoreInfoVo storeInfo) {
-        // 通过id获取当前商家账号信息
-        LambdaQueryWrapper<StoreInfo> storeUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        storeUserLambdaQueryWrapper.eq(StoreInfo::getId, storeInfo.getId());
-        StoreInfo storeIn = storeInfoMapper.selectOne(storeUserLambdaQueryWrapper);
-        // 修改注销标记为0
-        storeIn.setLogoutFlag(0);
-        // 注销状态变为可用
-        storeIn.setStoreStatus(1);
-        // 清空注销原因
-        storeIn.setLogoutReason("");
-        // 清空注销code
-        storeIn.setLogoutCode("");
-        // 清空注销申请时间
-        storeIn.setLogoutTime(null);
-        int num = storeInfoMapper.updateById(storeIn);
-        if (num > 0) {
-            // 发送通知
-            LifeNotice lifeMessage = new LifeNotice();
-            lifeMessage.setReceiverId("store_" + storeInfo.getStorePhone());
-            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm");
-            String storeDate = simpleDateFormat.format(new Date());
-            String text = "当前店铺状态已恢复正常。您可继续使用该店铺登录并享受各项服务,所有店铺数据均已妥善保留。\n\n若您后续仍有注销需求,可随时通过【我的设置】-【注销店铺】中重新提交申请。感谢您的理解与支持!";
-            com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
-            jsonObject.put("message", text);
-            lifeMessage.setContext(jsonObject.toJSONString());
-            lifeMessage.setTitle("处理结果: 已撤回注销申请");
-            lifeMessage.setSenderId("system");
-            lifeMessage.setIsRead(0);
-            lifeMessage.setNoticeType(1);
-            lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
-            lifeNoticeMapper.insert(lifeMessage);
-
-            WebSocketVo websocketVo = new WebSocketVo();
-            websocketVo.setSenderId("system");
-            websocketVo.setReceiverId("store_" + storeInfo.getStorePhone());
-            websocketVo.setCategory("notice");
-            websocketVo.setNoticeType("1");
-            websocketVo.setIsRead(0);
-            websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeMessage).toJSONString());
-        }
+        throw new IllegalArgumentException("店铺撤销注销已合并至商家账号注销,请通过商家账号撤销注销接口操作");
     }
 
     @Override

+ 32 - 0
alien-store/src/main/java/shop/alien/store/controller/AccountLogoutJobController.java

@@ -0,0 +1,32 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.dto.AccountLogoutCompleteRequestDTO;
+import shop.alien.entity.store.vo.AccountLogoutCompleteResultVo;
+import shop.alien.store.service.AccountLogoutCompleteService;
+
+/**
+ * 注销冷静期完成(供 XXL-JOB Feign 调用,遵守 7 天规则)
+ */
+@Slf4j
+@Api(tags = "账号注销-定时任务")
+@RestController
+@RequestMapping("/account/logout/job")
+@RequiredArgsConstructor
+public class AccountLogoutJobController {
+
+    private final AccountLogoutCompleteService accountLogoutCompleteService;
+
+    @ApiOperation("执行注销冷静期到期处理(满7天才会处理,与 cancellationOfBusinessJob 一致)")
+    @PostMapping("/completeCooling")
+    public R<AccountLogoutCompleteResultVo> completeCooling() {
+        log.info("AccountLogoutJobController.completeCooling");
+        AccountLogoutCompleteResultVo result = accountLogoutCompleteService.completeCoolingPeriod(false);
+        return R.data(result);
+    }
+}

+ 52 - 0
alien-store/src/main/java/shop/alien/store/controller/AccountLogoutTestController.java

@@ -0,0 +1,52 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.dto.AccountLogoutCompleteRequestDTO;
+import shop.alien.entity.store.vo.AccountLogoutCompleteResultVo;
+import shop.alien.store.service.AccountLogoutCompleteService;
+
+/**
+ * 注销冷静期测试接口(仅启动 alien-store 即可联调,跳过 7 天等待)
+ */
+@Slf4j
+@Api(tags = "账号注销-测试")
+@RestController
+@RequestMapping("/account/logout/test")
+@RequiredArgsConstructor
+public class AccountLogoutTestController {
+
+    private final AccountLogoutCompleteService accountLogoutCompleteService;
+
+    @ApiOperation(value = "强制完成注销冷静期", notes = "默认跳过7天校验,立即将冷静期账号变为已注销。accountType: ALL|STORE_USER|LIFE_USER|LAWYER|STORE;可传 id 或 phone 指定单条")
+    @PostMapping("/force-complete-cooling")
+    public R<AccountLogoutCompleteResultVo> forceCompleteCooling(
+            @RequestBody(required = false) AccountLogoutCompleteRequestDTO request) {
+        if (request == null) {
+            request = new AccountLogoutCompleteRequestDTO();
+        }
+        request.setForce(true);
+        log.info("AccountLogoutTestController.forceCompleteCooling request={}", request);
+        AccountLogoutCompleteResultVo result = accountLogoutCompleteService.completeCoolingPeriod(request);
+        return R.data(result);
+    }
+
+    @ApiOperation("强制完成全部冷静期账号(GET 快捷调用)")
+    @GetMapping("/force-complete-cooling")
+    public R<AccountLogoutCompleteResultVo> forceCompleteCoolingGet(
+            @RequestParam(required = false) String accountType,
+            @RequestParam(required = false) Integer id,
+            @RequestParam(required = false) String phone) {
+        AccountLogoutCompleteRequestDTO request = new AccountLogoutCompleteRequestDTO();
+        request.setAccountType(accountType);
+        request.setId(id);
+        request.setPhone(phone);
+        request.setForce(true);
+        log.info("AccountLogoutTestController.forceCompleteCoolingGet request={}", request);
+        return R.data(accountLogoutCompleteService.completeCoolingPeriod(request));
+    }
+}

+ 16 - 0
alien-store/src/main/java/shop/alien/store/service/AccountLogoutCompleteService.java

@@ -0,0 +1,16 @@
+package shop.alien.store.service;
+
+import shop.alien.entity.store.dto.AccountLogoutCompleteRequestDTO;
+import shop.alien.entity.store.vo.AccountLogoutCompleteResultVo;
+
+public interface AccountLogoutCompleteService {
+
+    /**
+     * 执行注销冷静期到期处理(与 XXL-JOB cancellationOfBusinessJob 一致)
+     *
+     * @param force 为 true 时跳过 7 天冷静期时间校验(测试用)
+     */
+    AccountLogoutCompleteResultVo completeCoolingPeriod(boolean force);
+
+    AccountLogoutCompleteResultVo completeCoolingPeriod(AccountLogoutCompleteRequestDTO request);
+}

+ 25 - 0
alien-store/src/main/java/shop/alien/store/service/StoreLogoutSyncService.java

@@ -0,0 +1,25 @@
+package shop.alien.store.service;
+
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.vo.AccountLogoutCompleteResultVo;
+
+/**
+ * 商家用户与关联店铺注销状态同步
+ */
+public interface StoreLogoutSyncService {
+
+    /**
+     * 商家用户进入冷静期时,同步关联店铺
+     */
+    void applyCoolingForStoreUser(StoreUser storeUser, String logoutReason, String logoutCode);
+
+    /**
+     * 商家用户撤销注销时,同步恢复关联店铺
+     */
+    void revokeCoolingForStoreUser(StoreUser storeUser);
+
+    /**
+     * 商家用户冷静期结束(已注销)时,同步完成关联店铺注销
+     */
+    void completeLogoutForStoreUser(StoreUser storeUser, AccountLogoutCompleteResultVo result);
+}

+ 292 - 0
alien-store/src/main/java/shop/alien/store/service/impl/AccountLogoutCompleteServiceImpl.java

@@ -0,0 +1,292 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.dto.AccountLogoutCompleteRequestDTO;
+import shop.alien.entity.store.vo.AccountLogoutCompleteResultVo;
+import shop.alien.mapper.*;
+import shop.alien.store.service.AccountLogoutCompleteService;
+import shop.alien.store.service.NearMeService;
+import shop.alien.store.service.StoreLogoutSyncService;
+import shop.alien.util.common.AccountLogoutUtil;
+import shop.alien.util.common.constant.CouponStatusEnum;
+import shop.alien.util.common.constant.DiscountCouponEnum;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AccountLogoutCompleteServiceImpl implements AccountLogoutCompleteService {
+
+    private static final String LOGOUT_DISPLAY_NAME = "已注销";
+
+    private final StoreUserMapper storeUserMapper;
+    private final StoreInfoMapper storeInfoMapper;
+    private final LifeUserMapper lifeUserMapper;
+    private final LawyerUserMapper lawyerUserMapper;
+    private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
+    private final LifeDiscountCouponUserMapper lifeDiscountCouponUserMapper;
+    private final LifeCouponMapper lifeCouponMapper;
+    private final NearMeService nearMeService;
+    private final StoreLogoutSyncService storeLogoutSyncService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AccountLogoutCompleteResultVo completeCoolingPeriod(boolean force) {
+        AccountLogoutCompleteRequestDTO request = new AccountLogoutCompleteRequestDTO();
+        request.setAccountType("ALL");
+        request.setForce(force);
+        return completeCoolingPeriod(request);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AccountLogoutCompleteResultVo completeCoolingPeriod(AccountLogoutCompleteRequestDTO request) {
+        boolean force = request == null || request.getForce() == null || Boolean.TRUE.equals(request.getForce());
+        String accountType = normalizeAccountType(request != null ? request.getAccountType() : null);
+        Integer id = request != null ? request.getId() : null;
+        String phone = request != null ? request.getPhone() : null;
+
+        AccountLogoutCompleteResultVo result = new AccountLogoutCompleteResultVo();
+        if ("ALL".equals(accountType) || "STORE_USER".equals(accountType)) {
+            completeStoreUsers(force, id, phone, result);
+        }
+        // 仅兼容历史独立店铺注销数据;新流程随商家用户一并注销
+        if ("STORE".equals(accountType)) {
+            completeLegacyStores(force, id, result);
+        }
+        if ("ALL".equals(accountType) || "LIFE_USER".equals(accountType)) {
+            completeLifeUsers(force, id, phone, result);
+        }
+        if ("ALL".equals(accountType) || "LAWYER".equals(accountType)) {
+            completeLawyerUsers(force, id, phone, result);
+        }
+        log.info("注销冷静期完成处理: force={}, type={}, success={}, fail={}, skipped={}",
+                force, accountType, result.getSuccessCount(), result.getFailCount(), result.getSkippedCount());
+        return result;
+    }
+
+    private void completeStoreUsers(boolean force, Integer id, String phone, AccountLogoutCompleteResultVo result) {
+        LambdaQueryWrapper<StoreUser> wrapper = new LambdaQueryWrapper<StoreUser>()
+                .and(w -> w.eq(StoreUser::getLogoutFlag, StoreUser.LOGOUT_FLAG_COOLING)
+                        .or(o -> o.eq(StoreUser::getLogoutFlag, StoreUser.LOGOUT_FLAG_DONE)
+                                .eq(StoreUser::getStatus, -1)));
+        if (id != null) {
+            wrapper.eq(StoreUser::getId, id);
+        }
+        if (StringUtils.hasText(phone)) {
+            wrapper.eq(StoreUser::getPhone, phone);
+        }
+        for (StoreUser storeUser : storeUserMapper.selectList(wrapper)) {
+            try {
+                if (storeUser.getLogoutTime() == null) {
+                    result.setSkippedCount(result.getSkippedCount() + 1);
+                    result.addDetail("STORE_USER#" + storeUser.getId() + " 跳过:无 logoutTime");
+                    continue;
+                }
+                if (!force && !AccountLogoutUtil.isLogoutCoolingExpired(storeUser.getLogoutTime())) {
+                    result.setSkippedCount(result.getSkippedCount() + 1);
+                    result.addDetail("STORE_USER#" + storeUser.getId() + " 跳过:未满7天冷静期");
+                    continue;
+                }
+                storeUser.setLogoutFlag(StoreUser.LOGOUT_FLAG_DONE);
+                storeUser.setName(LOGOUT_DISPLAY_NAME);
+                storeUser.setNickName(LOGOUT_DISPLAY_NAME);
+                storeUser.setHeadImg(null);
+                if (Integer.valueOf(-1).equals(storeUser.getStatus())) {
+                    storeUser.setStatus(0);
+                }
+                storeUserMapper.updateById(storeUser);
+                storeLogoutSyncService.completeLogoutForStoreUser(storeUser, result);
+                result.setSuccessCount(result.getSuccessCount() + 1);
+                result.addDetail("STORE_USER#" + storeUser.getId() + " 已注销");
+            } catch (Exception e) {
+                result.setFailCount(result.getFailCount() + 1);
+                result.addDetail("STORE_USER#" + storeUser.getId() + " 失败:" + e.getMessage());
+                log.error("商家账号注销完成失败 userId={}", storeUser.getId(), e);
+            }
+        }
+    }
+
+    /** 处理历史独立店铺注销数据(新流程已合并至商家用户注销) */
+    private void completeLegacyStores(boolean force, Integer storeId, AccountLogoutCompleteResultVo result) {
+        LambdaQueryWrapper<StoreInfo> wrapper = new LambdaQueryWrapper<StoreInfo>()
+                .and(w -> w.eq(StoreInfo::getLogoutFlag, StoreInfo.LOGOUT_FLAG_COOLING)
+                        .or(o -> o.eq(StoreInfo::getLogoutFlag, StoreInfo.LOGOUT_FLAG_DONE)
+                                .eq(StoreInfo::getStoreStatus, -1)));
+        if (storeId != null) {
+            wrapper.eq(StoreInfo::getId, storeId);
+        }
+        for (StoreInfo storeInfo : storeInfoMapper.selectList(wrapper)) {
+            try {
+                if (storeInfo.getLogoutTime() == null) {
+                    result.setSkippedCount(result.getSkippedCount() + 1);
+                    result.addDetail("STORE#" + storeInfo.getId() + " 跳过:无 logoutTime");
+                    continue;
+                }
+                if (!force && !AccountLogoutUtil.isLogoutCoolingExpired(storeInfo.getLogoutTime())) {
+                    result.setSkippedCount(result.getSkippedCount() + 1);
+                    result.addDetail("STORE#" + storeInfo.getId() + " 跳过:未满7天冷静期");
+                    continue;
+                }
+                if (StoreInfo.LOGOUT_FLAG_DONE == storeInfo.getLogoutFlag()
+                        && StoreInfo.BUSINESS_STATUS_PERMANENT_CLOSE == storeInfo.getBusinessStatus()) {
+                    result.setSkippedCount(result.getSkippedCount() + 1);
+                    result.addDetail("STORE#" + storeInfo.getId() + " 跳过:已是已注销状态");
+                    continue;
+                }
+                invalidateStoreCoupons(storeInfo.getId());
+                try {
+                    nearMeService.removeGeolocation(Boolean.TRUE, storeInfo.getId().toString());
+                } catch (Exception e) {
+                    log.warn("清理Redis地理位置失败 storeId={}", storeInfo.getId(), e);
+                }
+                storeInfoMapper.update(null, new LambdaUpdateWrapper<StoreInfo>()
+                        .eq(StoreInfo::getId, storeInfo.getId())
+                        .set(StoreInfo::getLogoutFlag, StoreInfo.LOGOUT_FLAG_DONE)
+                        .set(StoreInfo::getStoreStatus, 2)
+                        .set(StoreInfo::getBusinessStatus, StoreInfo.BUSINESS_STATUS_PERMANENT_CLOSE)
+                        .set(StoreInfo::getStoreName, LOGOUT_DISPLAY_NAME));
+                result.setSuccessCount(result.getSuccessCount() + 1);
+                result.addDetail("STORE#" + storeInfo.getId() + " 已注销");
+            } catch (Exception e) {
+                result.setFailCount(result.getFailCount() + 1);
+                result.addDetail("STORE#" + storeInfo.getId() + " 失败:" + e.getMessage());
+                log.error("店铺注销完成失败 storeId={}", storeInfo.getId(), e);
+            }
+        }
+    }
+
+    private void completeLifeUsers(boolean force, Integer id, String phone, AccountLogoutCompleteResultVo result) {
+        LambdaQueryWrapper<LifeUser> wrapper = new LambdaQueryWrapper<LifeUser>()
+                .eq(LifeUser::getLogoutFlag, LifeUser.LOGOUT_FLAG_COOLING);
+        if (id != null) {
+            wrapper.eq(LifeUser::getId, id);
+        }
+        if (StringUtils.hasText(phone)) {
+            wrapper.eq(LifeUser::getUserPhone, phone);
+        }
+        for (LifeUser lifeUser : lifeUserMapper.selectList(wrapper)) {
+            try {
+                if (lifeUser.getLogoutTime() == null) {
+                    result.setSkippedCount(result.getSkippedCount() + 1);
+                    result.addDetail("LIFE_USER#" + lifeUser.getId() + " 跳过:无 logoutTime");
+                    continue;
+                }
+                if (!force && !AccountLogoutUtil.isLogoutCoolingExpired(lifeUser.getLogoutTime())) {
+                    result.setSkippedCount(result.getSkippedCount() + 1);
+                    result.addDetail("LIFE_USER#" + lifeUser.getId() + " 跳过:未满7天冷静期");
+                    continue;
+                }
+                lifeUserMapper.update(null, new LambdaUpdateWrapper<LifeUser>()
+                        .eq(LifeUser::getId, lifeUser.getId())
+                        .set(LifeUser::getLogoutFlag, LifeUser.LOGOUT_FLAG_DONE)
+                        .set(LifeUser::getUserName, LOGOUT_DISPLAY_NAME)
+                        .set(LifeUser::getUserImage, null));
+                result.setSuccessCount(result.getSuccessCount() + 1);
+                result.addDetail("LIFE_USER#" + lifeUser.getId() + " 已注销");
+            } catch (Exception e) {
+                result.setFailCount(result.getFailCount() + 1);
+                result.addDetail("LIFE_USER#" + lifeUser.getId() + " 失败:" + e.getMessage());
+                log.error("C端用户注销完成失败 userId={}", lifeUser.getId(), e);
+            }
+        }
+    }
+
+    private void completeLawyerUsers(boolean force, Integer id, String phone, AccountLogoutCompleteResultVo result) {
+        LambdaQueryWrapper<LawyerUser> wrapper = new LambdaQueryWrapper<LawyerUser>()
+                .eq(LawyerUser::getLogoutFlag, LawyerUser.LOGOUT_FLAG_COOLING)
+                .eq(LawyerUser::getDeleteFlag, 0);
+        if (id != null) {
+            wrapper.eq(LawyerUser::getId, id);
+        }
+        if (StringUtils.hasText(phone)) {
+            wrapper.eq(LawyerUser::getPhone, phone);
+        }
+        for (LawyerUser lawyerUser : lawyerUserMapper.selectList(wrapper)) {
+            try {
+                if (lawyerUser.getLogoutTime() == null) {
+                    result.setSkippedCount(result.getSkippedCount() + 1);
+                    result.addDetail("LAWYER#" + lawyerUser.getId() + " 跳过:无 logoutTime");
+                    continue;
+                }
+                if (!force && !AccountLogoutUtil.isLogoutCoolingExpired(lawyerUser.getLogoutTime())) {
+                    result.setSkippedCount(result.getSkippedCount() + 1);
+                    result.addDetail("LAWYER#" + lawyerUser.getId() + " 跳过:未满7天冷静期");
+                    continue;
+                }
+                lawyerUserMapper.update(null, new LambdaUpdateWrapper<LawyerUser>()
+                        .eq(LawyerUser::getId, lawyerUser.getId())
+                        .set(LawyerUser::getLogoutFlag, LawyerUser.LOGOUT_FLAG_DONE)
+                        .set(LawyerUser::getName, LOGOUT_DISPLAY_NAME)
+                        .set(LawyerUser::getNickName, LOGOUT_DISPLAY_NAME)
+                        .set(LawyerUser::getHeadImg, null)
+                        .set(LawyerUser::getOrderReceivingStatus, 0)
+                        .set(LawyerUser::getIsOnline, 0)
+                        .set(LawyerUser::getIsRecommended, 0)
+                        .set(LawyerUser::getLastOnlineTime, new Date()));
+                result.setSuccessCount(result.getSuccessCount() + 1);
+                result.addDetail("LAWYER#" + lawyerUser.getId() + " 已注销");
+            } catch (Exception e) {
+                result.setFailCount(result.getFailCount() + 1);
+                result.addDetail("LAWYER#" + lawyerUser.getId() + " 失败:" + e.getMessage());
+                log.error("律师注销完成失败 lawyerId={}", lawyerUser.getId(), e);
+            }
+        }
+    }
+
+    private void invalidateStoreCoupons(Integer storeId) {
+        if (storeId == null) {
+            return;
+        }
+        String storeIdStr = String.valueOf(storeId);
+        int voidedUserCouponStatus = Integer.parseInt(DiscountCouponEnum.HAVE_BEEN_VOIDED.getValue());
+
+        lifeDiscountCouponMapper.update(null, new LambdaUpdateWrapper<LifeDiscountCoupon>()
+                .eq(LifeDiscountCoupon::getStoreId, storeIdStr)
+                .set(LifeDiscountCoupon::getCouponStatus, LifeDiscountCoupon.COUPON_STATUS_INVALID)
+                .set(LifeDiscountCoupon::getGetStatus, 0));
+
+        List<LifeDiscountCoupon> storeCoupons = lifeDiscountCouponMapper.selectList(
+                new LambdaQueryWrapper<LifeDiscountCoupon>().eq(LifeDiscountCoupon::getStoreId, storeIdStr));
+        if (!storeCoupons.isEmpty()) {
+            List<Integer> couponIds = storeCoupons.stream().map(LifeDiscountCoupon::getId).collect(Collectors.toList());
+            lifeDiscountCouponUserMapper.update(null, new LambdaUpdateWrapper<LifeDiscountCouponUser>()
+                    .in(LifeDiscountCouponUser::getCouponId, couponIds)
+                    .eq(LifeDiscountCouponUser::getStatus, Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()))
+                    .set(LifeDiscountCouponUser::getStatus, voidedUserCouponStatus));
+        }
+
+        lifeCouponMapper.update(null, new LambdaUpdateWrapper<LifeCoupon>()
+                .eq(LifeCoupon::getStoreId, storeIdStr)
+                .notIn(LifeCoupon::getStatus, CouponStatusEnum.ENDED.getCode(), CouponStatusEnum.INVALID.getCode())
+                .set(LifeCoupon::getStatus, CouponStatusEnum.INVALID.getCode()));
+
+        List<LifeCoupon> storeVouchers = lifeCouponMapper.selectList(
+                new LambdaQueryWrapper<LifeCoupon>().eq(LifeCoupon::getStoreId, storeIdStr));
+        if (!storeVouchers.isEmpty()) {
+            List<String> voucherIds = storeVouchers.stream().map(LifeCoupon::getId).collect(Collectors.toList());
+            lifeDiscountCouponUserMapper.update(null, new LambdaUpdateWrapper<LifeDiscountCouponUser>()
+                    .in(LifeDiscountCouponUser::getVoucherId, voucherIds)
+                    .eq(LifeDiscountCouponUser::getStatus, Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()))
+                    .set(LifeDiscountCouponUser::getStatus, voidedUserCouponStatus));
+        }
+    }
+
+    private static String normalizeAccountType(String accountType) {
+        if (!StringUtils.hasText(accountType)) {
+            return "ALL";
+        }
+        return accountType.trim().toUpperCase(Locale.ROOT);
+    }
+}

+ 1 - 0
alien-store/src/main/java/shop/alien/store/service/impl/PerformanceListServiceImpl.java

@@ -1307,6 +1307,7 @@ public class PerformanceListServiceImpl implements PerformanceListService {
                 PerformanceGuestVo guest = new PerformanceGuestVo();
                 guest.setStaffId(staff.getId());
                 guest.setName(staff.getName());
+                guest.setTag(staff.getTag());
                 guest.setStaffImage(staff.getStaffImage());
                 guest.setStaffPosition(staff.getStaffPosition());
                 guest.setProficientProjects(staff.getProficientProjects());

+ 2 - 113
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -2785,123 +2785,12 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
     @Override
     public void logoutStore(StoreInfoVo storeInfo) {
-        // 通过id获取当前店铺信息
-        LambdaQueryWrapper<StoreInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
-        lambdaQueryWrapper.eq(StoreInfo::getId, storeInfo.getId());
-        StoreInfo storeIn = storeInfoMapper.selectOne(lambdaQueryWrapper);
-        if (storeIn != null) {
-            // 添加注销原因
-            storeIn.setLogoutReason(storeInfo.getLogoutReason());
-            // 添加注销code
-            storeIn.setLogoutCode(storeInfo.getLogoutCode());
-            // 注销中状态
-            storeIn.setStoreStatus(-1);
-            // 添加注销申请时间
-            storeIn.setLogoutTime(new Date());
-            // 更新logout_flag状态为1
-            storeIn.setLogoutFlag(1);
-            int num = storeInfoMapper.updateById(storeIn);
-            if (num > 0) {
-                // 发送通知
-                LifeNotice lifeMessage = getLifeNotice(storeInfo);
-                lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
-                lifeNoticeMapper.insert(lifeMessage);
-                WebSocketVo websocketVo = new WebSocketVo();
-                websocketVo.setSenderId("system");
-                websocketVo.setReceiverId("store_" + storeInfo.getStorePhone());
-                websocketVo.setCategory("notice");
-                websocketVo.setNoticeType("1");
-                websocketVo.setIsRead(0);
-                websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeMessage).toJSONString());
-                try {
-                    webSocketProcess.sendMessage("store_" + storeInfo.getStorePhone(), com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
-                } catch (Exception e) {
-                    log.error("StoreInfoService webSocketProcess Stack={}", e);
-                }
-            }
-        }
-    }
-
-    private static LifeNotice getLifeNotice(StoreInfoVo storeInfo) {
-        LifeNotice lifeMessage = new LifeNotice();
-        lifeMessage.setReceiverId("store_" + storeInfo.getStorePhone());
-        String text = "您提交的店铺注销申请已成功接收,系统将按流程进行处理。\n\n注销申请提交后,将进入7天的冷静期,期间您可随时在【我的设置】-【注销店铺】中撤回申请。冷静期结束后,系统将正式开始注销操作,店铺内所有数据将被永久清除,且无法恢复。\n\n如有疑问,可联系客服咨询,感谢您使用我们的服务。";
-        com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
-        jsonObject.put("message", text);
-        lifeMessage.setContext(jsonObject.toJSONString());
-        lifeMessage.setTitle("注销店铺通知");
-        lifeMessage.setSenderId("system");
-        lifeMessage.setIsRead(0);
-        lifeMessage.setNoticeType(1);
-        return lifeMessage;
+        throw new IllegalArgumentException("店铺注销已合并至商家账号注销,请通过商家账号注销接口操作");
     }
 
     @Override
     public void cancelLogoutStore(StoreInfoVo storeInfo) {
-        // 通过id获取当前商家账号信息
-        LambdaQueryWrapper<StoreInfo> storeUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        storeUserLambdaQueryWrapper.eq(StoreInfo::getId, storeInfo.getId());
-        StoreInfo storeIn = storeInfoMapper.selectOne(storeUserLambdaQueryWrapper);
-        
-        // 检查店铺是否存在
-        if (storeIn == null) {
-            throw new IllegalArgumentException("店铺不存在或已被删除");
-        }
-        
-        // 检查是否超过7天冷静期
-        if (storeIn.getLogoutTime() != null) {
-            Date logoutTime = storeIn.getLogoutTime();
-            Calendar calendar = Calendar.getInstance();
-            calendar.setTime(logoutTime);
-            calendar.add(Calendar.DAY_OF_YEAR, 7);
-            Date sevenDaysLater = calendar.getTime();
-            Date now = new Date();
-            if (now.after(sevenDaysLater)) {
-                throw new IllegalArgumentException("已超过7天冷静期,无法取消注销");
-            }
-        }
-        
-        // 修改注销标记为0
-        storeIn.setLogoutFlag(0);
-        // 注销状态变为可用
-        storeIn.setStoreStatus(1);
-        // 清空注销原因
-        storeIn.setLogoutReason("");
-        // 清空注销code
-        storeIn.setLogoutCode("");
-        // 清空注销申请时间
-        storeIn.setLogoutTime(null);
-        int num = storeInfoMapper.updateById(storeIn);
-        if (num > 0) {
-            // 发送通知
-            LifeNotice lifeMessage = new LifeNotice();
-            lifeMessage.setReceiverId("store_" + storeInfo.getStorePhone());
-            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm");
-            String storeDate = simpleDateFormat.format(new Date());
-            String text = "当前店铺状态已恢复正常。您可继续使用该店铺登录并享受各项服务,所有店铺数据均已妥善保留。\n\n若您后续仍有注销需求,可随时通过【我的设置】-【注销店铺】中重新提交申请。感谢您的理解与支持!";
-            com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
-            jsonObject.put("message", text);
-            lifeMessage.setContext(jsonObject.toJSONString());
-            lifeMessage.setTitle("处理结果: 已撤回注销申请");
-            lifeMessage.setSenderId("system");
-            lifeMessage.setIsRead(0);
-            lifeMessage.setNoticeType(1);
-            lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
-            lifeNoticeMapper.insert(lifeMessage);
-
-            WebSocketVo websocketVo = new WebSocketVo();
-            websocketVo.setSenderId("system");
-            websocketVo.setReceiverId("store_" + storeInfo.getStorePhone());
-            websocketVo.setCategory("notice");
-            websocketVo.setNoticeType("1");
-            websocketVo.setIsRead(0);
-            websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeMessage).toJSONString());
-            try {
-                webSocketProcess.sendMessage("store_" + storeInfo.getStorePhone(), com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
-            } catch (Exception e) {
-                log.error("StoreInfoServiceImpl webSocketProcess Stack={}", e);
-            }
-        }
+        throw new IllegalArgumentException("店铺撤销注销已合并至商家账号注销,请通过商家账号撤销注销接口操作");
     }
 
     @Override

+ 162 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreLogoutSyncServiceImpl.java

@@ -0,0 +1,162 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.LifeCoupon;
+import shop.alien.entity.store.LifeDiscountCoupon;
+import shop.alien.entity.store.LifeDiscountCouponUser;
+import shop.alien.entity.store.StoreInfo;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.vo.AccountLogoutCompleteResultVo;
+import shop.alien.mapper.LifeCouponMapper;
+import shop.alien.mapper.LifeDiscountCouponMapper;
+import shop.alien.mapper.LifeDiscountCouponUserMapper;
+import shop.alien.mapper.StoreInfoMapper;
+import shop.alien.store.service.NearMeService;
+import shop.alien.store.service.StoreLogoutSyncService;
+import shop.alien.util.common.constant.CouponStatusEnum;
+import shop.alien.util.common.constant.DiscountCouponEnum;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class StoreLogoutSyncServiceImpl implements StoreLogoutSyncService {
+
+    private static final String LOGOUT_DISPLAY_NAME = "已注销";
+
+    private final StoreInfoMapper storeInfoMapper;
+    private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
+    private final LifeDiscountCouponUserMapper lifeDiscountCouponUserMapper;
+    private final LifeCouponMapper lifeCouponMapper;
+    private final NearMeService nearMeService;
+
+    @Override
+    public void applyCoolingForStoreUser(StoreUser storeUser, String logoutReason, String logoutCode) {
+        StoreInfo store = getLinkedStore(storeUser);
+        if (store == null) {
+            return;
+        }
+        storeInfoMapper.update(null, new LambdaUpdateWrapper<StoreInfo>()
+                .eq(StoreInfo::getId, store.getId())
+                .set(StoreInfo::getLogoutFlag, StoreInfo.LOGOUT_FLAG_COOLING)
+                .set(StoreInfo::getBusinessStatus, StoreInfo.BUSINESS_STATUS_PAUSE)
+                .set(StoreInfo::getStoreStatus, -1)
+                .set(StoreInfo::getLogoutTime, storeUser.getLogoutTime())
+                .set(StoreInfo::getLogoutReason, logoutReason)
+                .set(StoreInfo::getLogoutCode, logoutCode));
+        log.info("关联店铺进入注销冷静期: storeId={}, userId={}", store.getId(), storeUser.getId());
+    }
+
+    @Override
+    public void revokeCoolingForStoreUser(StoreUser storeUser) {
+        StoreInfo store = getLinkedStore(storeUser);
+        if (store == null) {
+            return;
+        }
+        storeInfoMapper.update(null, new LambdaUpdateWrapper<StoreInfo>()
+                .eq(StoreInfo::getId, store.getId())
+                .set(StoreInfo::getLogoutFlag, StoreInfo.LOGOUT_FLAG_NORMAL)
+                .set(StoreInfo::getBusinessStatus, StoreInfo.BUSINESS_STATUS_NORMAL)
+                .set(StoreInfo::getStoreStatus, 1)
+                .set(StoreInfo::getLogoutReason, "")
+                .set(StoreInfo::getLogoutCode, "")
+                .set(StoreInfo::getLogoutTime, null));
+        log.info("关联店铺撤销注销冷静期: storeId={}, userId={}", store.getId(), storeUser.getId());
+    }
+
+    @Override
+    public void completeLogoutForStoreUser(StoreUser storeUser, AccountLogoutCompleteResultVo result) {
+        StoreInfo store = getLinkedStore(storeUser);
+        if (store == null) {
+            if (result != null) {
+                result.addDetail("STORE_USER#" + storeUser.getId() + " 无关联店铺,跳过店铺同步");
+            }
+            return;
+        }
+        if (isStoreLogoutCompleted(store)) {
+            if (result != null) {
+                result.addDetail("STORE#" + store.getId() + " 跳过:已是已注销状态");
+            }
+            return;
+        }
+        try {
+            invalidateStoreCoupons(store.getId());
+            try {
+                nearMeService.removeGeolocation(Boolean.TRUE, store.getId().toString());
+            } catch (Exception e) {
+                log.warn("清理Redis地理位置失败 storeId={}", store.getId(), e);
+            }
+            storeInfoMapper.update(null, new LambdaUpdateWrapper<StoreInfo>()
+                    .eq(StoreInfo::getId, store.getId())
+                    .set(StoreInfo::getLogoutFlag, StoreInfo.LOGOUT_FLAG_DONE)
+                    .set(StoreInfo::getStoreStatus, 2)
+                    .set(StoreInfo::getBusinessStatus, StoreInfo.BUSINESS_STATUS_PERMANENT_CLOSE)
+                    .set(StoreInfo::getLogoutTime, storeUser.getLogoutTime()));
+            if (result != null) {
+                result.addDetail("STORE#" + store.getId() + " 已注销(随商家用户同步)");
+            }
+            log.info("关联店铺注销完成: storeId={}, userId={}", store.getId(), storeUser.getId());
+        } catch (Exception e) {
+            log.error("关联店铺注销完成失败 storeId={}, userId={}", store.getId(), storeUser.getId(), e);
+            throw e;
+        }
+    }
+
+    private StoreInfo getLinkedStore(StoreUser storeUser) {
+        if (storeUser == null || storeUser.getStoreId() == null) {
+            return null;
+        }
+        return storeInfoMapper.selectById(storeUser.getStoreId());
+    }
+
+    private static boolean isStoreLogoutCompleted(StoreInfo store) {
+        return store != null
+                && StoreInfo.LOGOUT_FLAG_DONE == store.getLogoutFlag()
+                && StoreInfo.BUSINESS_STATUS_PERMANENT_CLOSE == store.getBusinessStatus();
+    }
+
+    private void invalidateStoreCoupons(Integer storeId) {
+        if (storeId == null) {
+            return;
+        }
+        String storeIdStr = String.valueOf(storeId);
+        int voidedUserCouponStatus = Integer.parseInt(DiscountCouponEnum.HAVE_BEEN_VOIDED.getValue());
+
+        lifeDiscountCouponMapper.update(null, new LambdaUpdateWrapper<LifeDiscountCoupon>()
+                .eq(LifeDiscountCoupon::getStoreId, storeIdStr)
+                .set(LifeDiscountCoupon::getCouponStatus, LifeDiscountCoupon.COUPON_STATUS_INVALID)
+                .set(LifeDiscountCoupon::getGetStatus, 0));
+
+        List<LifeDiscountCoupon> storeCoupons = lifeDiscountCouponMapper.selectList(
+                new LambdaQueryWrapper<LifeDiscountCoupon>().eq(LifeDiscountCoupon::getStoreId, storeIdStr));
+        if (!storeCoupons.isEmpty()) {
+            List<Integer> couponIds = storeCoupons.stream().map(LifeDiscountCoupon::getId).collect(Collectors.toList());
+            lifeDiscountCouponUserMapper.update(null, new LambdaUpdateWrapper<LifeDiscountCouponUser>()
+                    .in(LifeDiscountCouponUser::getCouponId, couponIds)
+                    .eq(LifeDiscountCouponUser::getStatus, Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()))
+                    .set(LifeDiscountCouponUser::getStatus, voidedUserCouponStatus));
+        }
+
+        lifeCouponMapper.update(null, new LambdaUpdateWrapper<LifeCoupon>()
+                .eq(LifeCoupon::getStoreId, storeIdStr)
+                .notIn(LifeCoupon::getStatus, CouponStatusEnum.ENDED.getCode(), CouponStatusEnum.INVALID.getCode())
+                .set(LifeCoupon::getStatus, CouponStatusEnum.INVALID.getCode()));
+
+        List<LifeCoupon> storeVouchers = lifeCouponMapper.selectList(
+                new LambdaQueryWrapper<LifeCoupon>().eq(LifeCoupon::getStoreId, storeIdStr));
+        if (!storeVouchers.isEmpty()) {
+            List<String> voucherIds = storeVouchers.stream().map(LifeCoupon::getId).collect(Collectors.toList());
+            lifeDiscountCouponUserMapper.update(null, new LambdaUpdateWrapper<LifeDiscountCouponUser>()
+                    .in(LifeDiscountCouponUser::getVoucherId, voucherIds)
+                    .eq(LifeDiscountCouponUser::getStatus, Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()))
+                    .set(LifeDiscountCouponUser::getStatus, voidedUserCouponStatus));
+        }
+    }
+}

+ 6 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreUserServiceImpl.java

@@ -29,6 +29,7 @@ import shop.alien.mapper.*;
 import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.config.WebSocketProcess;
 import shop.alien.store.service.NearMeService;
+import shop.alien.store.service.StoreLogoutSyncService;
 import shop.alien.store.service.StoreUserService;
 import shop.alien.store.util.FunctionMagic;
 import shop.alien.util.ali.AliOSSUtil;
@@ -84,6 +85,8 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
 
     private final WebSocketProcess webSocketProcess;
 
+    private final StoreLogoutSyncService storeLogoutSyncService;
+
     @Value("${spring.web.resources.excel-path}")
     private String excelPath;
 
@@ -1155,6 +1158,8 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
             storeUser.setLogoutTime(new Date());
             int num = storeUserMapper.updateById(storeUser);
             if (num > 0) {
+                storeLogoutSyncService.applyCoolingForStoreUser(
+                        storeUser, storeUserVo.getLogoutReason(), storeUserVo.getLogoutCode());
                 // 发送通知
                 LifeNotice lifeMessage = new LifeNotice();
                 lifeMessage.setReceiverId("store_" + storeUser.getPhone());
@@ -1204,6 +1209,7 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
         storeUser.setLogoutTime(null);
         int num = storeUserMapper.updateById(storeUser);
         if (num > 0) {
+            storeLogoutSyncService.revokeCoolingForStoreUser(storeUser);
             // 发送通知
             LifeNotice lifeMessage = new LifeNotice();
             lifeMessage.setReceiverId("store_" + storeUser.getPhone());

+ 109 - 0
alien-util/src/main/java/shop/alien/util/common/AccountLogoutUtil.java

@@ -0,0 +1,109 @@
+package shop.alien.util.common;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * 账号注销统一时间线(用户端 / 商家端 / 律师端一致):
+ * <ul>
+ *   <li>0~7 天:冷静期,可撤销注销</li>
+ *   <li>7~14 天:已注销,不可登录/注册</li>
+ *   <li>14 天后:同手机号可重新注册</li>
+ * </ul>
+ */
+public final class AccountLogoutUtil {
+
+    public static final int COOLING_DAYS = 7;
+    public static final int RE_REGISTER_BLOCK_DAYS = 14;
+    public static final String LOGGED_OUT_MESSAGE = "当前用户已注销";
+
+    private static final DateTimeFormatter RE_REGISTER_ALLOW_TIME_FORMAT =
+            DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
+    private static final DateTimeFormatter COOLING_END_TIME_FORMAT =
+            DateTimeFormatter.ofPattern("yyyy/MM/dd");
+
+    private AccountLogoutUtil() {
+    }
+
+    /** 是否仍在 0~7 天冷静期内(可撤销注销) */
+    public static boolean isWithinCoolingPeriod(Date logoutTime) {
+        return logoutTime != null && !isLogoutCoolingExpired(logoutTime);
+    }
+
+    /** 冷静期是否已结束(满 7 天) */
+    public static boolean isLogoutCoolingExpired(Date logoutTime) {
+        if (logoutTime == null) {
+            return false;
+        }
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(logoutTime);
+        calendar.add(Calendar.DAY_OF_YEAR, COOLING_DAYS);
+        return new Date().compareTo(calendar.getTime()) >= 0;
+    }
+
+    /** 是否处于 7~14 天禁登/禁注册期(自申请注销日起算) */
+    public static boolean isWithinReRegisterBlockPeriod(Date logoutTime) {
+        if (logoutTime == null) {
+            return false;
+        }
+        ZoneId zone = ZoneId.systemDefault();
+        LocalDateTime allowFrom = logoutTime.toInstant()
+                .atZone(zone)
+                .toLocalDate()
+                .plusDays(RE_REGISTER_BLOCK_DAYS)
+                .atStartOfDay();
+        return LocalDateTime.now(zone).isBefore(allowFrom);
+    }
+
+    public static String resolveCoolingEndTime(Date logoutTime) {
+        if (logoutTime == null) {
+            return null;
+        }
+        return logoutTime.toInstant()
+                .atZone(ZoneId.systemDefault())
+                .toLocalDate()
+                .plusDays(COOLING_DAYS)
+                .format(COOLING_END_TIME_FORMAT);
+    }
+
+    public static String resolveReRegisterAllowTime(Date logoutTime) {
+        if (logoutTime == null) {
+            return null;
+        }
+        return logoutTime.toInstant()
+                .atZone(ZoneId.systemDefault())
+                .toLocalDate()
+                .plusDays(RE_REGISTER_BLOCK_DAYS)
+                .atStartOfDay()
+                .format(RE_REGISTER_ALLOW_TIME_FORMAT);
+    }
+
+    /** 登录/已注销态提示(7~14 天) */
+    public static String loggedOutBlockMessage(Date logoutTime, Integer logoutFlagDone) {
+        if (logoutFlagDone == null || logoutFlagDone != 1) {
+            return null;
+        }
+        if (isWithinReRegisterBlockPeriod(logoutTime)) {
+            return LOGGED_OUT_MESSAGE;
+        }
+        return null;
+    }
+
+    /** 注册拦截提示(7~14 天) */
+    public static String reRegisterBlockedMessage(Date logoutTime, Integer logoutFlagDone) {
+        if (logoutFlagDone == null || logoutFlagDone != 1) {
+            return null;
+        }
+        if (!isWithinReRegisterBlockPeriod(logoutTime)) {
+            return null;
+        }
+        String allowTime = resolveReRegisterAllowTime(logoutTime);
+        if (allowTime == null) {
+            return LOGGED_OUT_MESSAGE;
+        }
+        return "原手机号无法注册新账号," + allowTime + "起可重新注册";
+    }
+}