Ver código fonte

Merge remote-tracking branch 'origin/sit' into uat-20260202

dujian 15 horas atrás
pai
commit
8f2916c7d0
100 arquivos alterados com 3190 adições e 574 exclusões
  1. 1 1
      alien-dining/src/main/java/shop/alien/dining/controller/DiningCouponController.java
  2. 8 1
      alien-entity/src/main/java/shop/alien/entity/store/LawyerUser.java
  3. 9 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeCollect.java
  4. 8 1
      alien-entity/src/main/java/shop/alien/entity/store/LifeDiscountCoupon.java
  5. 16 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeFans.java
  6. 9 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeLikeRecord.java
  7. 26 2
      alien-entity/src/main/java/shop/alien/entity/store/LifeMessage.java
  8. 18 2
      alien-entity/src/main/java/shop/alien/entity/store/LifeNotice.java
  9. 14 1
      alien-entity/src/main/java/shop/alien/entity/store/LifeUser.java
  10. 8 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeUserDynamics.java
  11. 8 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreClockIn.java
  12. 8 1
      alien-entity/src/main/java/shop/alien/entity/store/StoreUser.java
  13. 30 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/LawyerLogoutRequestDto.java
  14. 48 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerUserLogoutListVo.java
  15. 4 1
      alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerUserVo.java
  16. 2 2
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeDiscountCouponVo.java
  17. 35 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeUserCancelAccountResultVo.java
  18. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeUserVo.java
  19. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreClockInVo.java
  20. 51 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreUserLogoutListVo.java
  21. 23 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreUserWithStoreVo.java
  22. 2 2
      alien-entity/src/main/java/shop/alien/mapper/CommonCommentMapper.java
  23. 9 0
      alien-entity/src/main/java/shop/alien/mapper/LawyerUserMapper.java
  24. 8 4
      alien-entity/src/main/java/shop/alien/mapper/LifeBrowseRecordMapper.java
  25. 7 4
      alien-entity/src/main/java/shop/alien/mapper/LifeCollectMapper.java
  26. 173 139
      alien-entity/src/main/java/shop/alien/mapper/LifeFansMapper.java
  27. 43 16
      alien-entity/src/main/java/shop/alien/mapper/LifeMessageMapper.java
  28. 6 3
      alien-entity/src/main/java/shop/alien/mapper/LifeNoticeMapper.java
  29. 85 52
      alien-entity/src/main/java/shop/alien/mapper/LifeUserDynamicsMapper.java
  30. 18 18
      alien-entity/src/main/java/shop/alien/mapper/LifeUserExpertMapper.java
  31. 7 2
      alien-entity/src/main/java/shop/alien/mapper/StoreClockInMapper.java
  32. 4 4
      alien-entity/src/main/java/shop/alien/mapper/second/SecondRecommendMapper.java
  33. 133 0
      alien-entity/src/main/java/shop/alien/util/type/LifeCollectIdentityQuery.java
  34. 147 0
      alien-entity/src/main/java/shop/alien/util/type/LifeFansIdentityQuery.java
  35. 215 0
      alien-entity/src/main/java/shop/alien/util/type/LifeMessageUtil.java
  36. 129 0
      alien-entity/src/main/java/shop/alien/util/type/LifeNoticeUtil.java
  37. 29 0
      alien-entity/src/main/java/shop/alien/util/type/PhoneTypeIdResult.java
  38. 126 0
      alien-entity/src/main/java/shop/alien/util/type/TypeUtil.java
  39. 4 0
      alien-entity/src/main/resources/db/migration/life_discount_coupon_status_invalid.sql
  40. 75 0
      alien-entity/src/main/resources/db/migration/life_fans_type_ref_backfill.sql
  41. 210 0
      alien-entity/src/main/resources/db/migration/phone_identity_user_type_ref_id.sql
  42. 12 0
      alien-entity/src/main/resources/mapper/LawyerUserMapper.xml
  43. 4 3
      alien-entity/src/main/resources/mapper/LifeDiscountCouponUserMapper.xml
  44. 59 36
      alien-entity/src/main/resources/mapper/LifeUserDynamicsMapper.xml
  45. 4 4
      alien-entity/src/main/resources/mapper/OrderReviewMapper.xml
  46. 2 2
      alien-entity/src/main/resources/mapper/PlatformLifeUserMapper.xml
  47. 2 2
      alien-entity/src/main/resources/mapper/ReviewCommentMapper.xml
  48. 2 2
      alien-entity/src/main/resources/mapper/StoreStaffCommentMapper.xml
  49. 3 3
      alien-entity/src/main/resources/mapper/StoreStaffReviewMapper.xml
  50. 11 11
      alien-entity/src/main/resources/mapper/second/SecondGoodsInfoMapper.xml
  51. 9 3
      alien-gateway/src/main/java/shop/alien/gateway/config/JwtTokenFilter.java
  52. 4 0
      alien-gateway/src/main/java/shop/alien/gateway/controller/LifeUserController.java
  53. 4 0
      alien-gateway/src/main/java/shop/alien/gateway/controller/StoreUserController.java
  54. 8 0
      alien-gateway/src/main/java/shop/alien/gateway/mapper/LifeUserGatewayMapper.java
  55. 2 0
      alien-gateway/src/main/java/shop/alien/gateway/service/LifeUserPasswordService.java
  56. 79 0
      alien-gateway/src/main/java/shop/alien/gateway/service/LifeUserService.java
  57. 5 0
      alien-gateway/src/main/java/shop/alien/gateway/service/impl/StoreUserServiceImpl.java
  58. 4 0
      alien-job/src/main/java/shop/alien/job/second/AiCheckXxlJob.java
  59. 4 0
      alien-job/src/main/java/shop/alien/job/second/AiProductComplaintJob.java
  60. 4 0
      alien-job/src/main/java/shop/alien/job/second/AiUserViolationJob.java
  61. 12 0
      alien-job/src/main/java/shop/alien/job/second/SecondGoodsTradeXxlJob.java
  62. 4 0
      alien-job/src/main/java/shop/alien/job/store/BadReviewAppealJob.java
  63. 4 0
      alien-job/src/main/java/shop/alien/job/store/LifeCouponJob.java
  64. 4 0
      alien-job/src/main/java/shop/alien/job/store/LifeReverseGroupBuyingJob.java
  65. 4 0
      alien-job/src/main/java/shop/alien/job/store/LifeUserOrderJob.java
  66. 140 48
      alien-job/src/main/java/shop/alien/job/store/StoreMembershipCardJob.java
  67. 5 0
      alien-lawyer/src/main/java/shop/alien/lawyer/config/WebSocketProcess.java
  68. 58 8
      alien-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserController.java
  69. 15 1
      alien-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserLogInController.java
  70. 19 12
      alien-lawyer/src/main/java/shop/alien/lawyer/service/LawyerUserService.java
  71. 7 0
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/AiUserAuditTaskServiceImpl.java
  72. 6 0
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/CommentAppealServiceImpl.java
  73. 14 5
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerClientConsultationOrderServiceImpl.java
  74. 8 2
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java
  75. 7 4
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerNoticeServiceImpl.java
  76. 31 1
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerUserLogInServiceImpl.java
  77. 218 96
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerUserServiceImpl.java
  78. 8 0
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerUserViolationServiceImpl.java
  79. 7 0
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderExpirationServiceImpl.java
  80. 12 2
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderReviewServiceImpl.java
  81. 11 3
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/ReviewCommentServiceImpl.java
  82. 31 9
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/StoreCommentServiceImpl.java
  83. 204 0
      alien-lawyer/src/main/java/shop/alien/lawyer/util/LawyerLikeRecordIdentityHelper.java
  84. 4 0
      alien-second/src/main/java/shop/alien/second/service/impl/RiskControlServiceImpl.java
  85. 6 0
      alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsNotificationServiceImpl.java
  86. 4 0
      alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsReportingServiceImpl.java
  87. 30 14
      alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsServiceImpl.java
  88. 35 11
      alien-second/src/main/java/shop/alien/second/service/impl/SecondRecommendServiceImpl.java
  89. 13 1
      alien-second/src/main/java/shop/alien/second/service/impl/SecondTradeRecordServiceImpl.java
  90. 12 0
      alien-second/src/main/java/shop/alien/second/task/Task.java
  91. 7 15
      alien-second/src/main/java/shop/alien/second/util/AiUserViolationUtils.java
  92. 204 0
      alien-second/src/main/java/shop/alien/second/util/SecondLikeRecordIdentityHelper.java
  93. 1 1
      alien-second/src/main/resources/bootstrap.yml
  94. 4 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformLoginController.java
  95. 8 1
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/LifeGroupBuyPlatformServiceImpl.java
  96. 10 5
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/MerchantUserServiceImpl.java
  97. 6 3
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/NoticeServiceImpl.java
  98. 4 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivityServiceImpl.java
  99. 4 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivitySignupServiceImpl.java
  100. 25 10
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreBusinessServiceImpl.java

+ 1 - 1
alien-dining/src/main/java/shop/alien/dining/controller/DiningCouponController.java

@@ -47,7 +47,7 @@ public class DiningCouponController {
     @ApiImplicitParams({
             @ApiImplicitParam(name = "page", value = "分页页数", dataType = "Integer", paramType = "query", required = false),
             @ApiImplicitParam(name = "size", value = "分页条数", dataType = "Integer", paramType = "query", required = false),
-            @ApiImplicitParam(name = "tabType", value = "分页类型(0:全部(未使用),1:即将过期,2:已使用,3:已过期)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "tabType", value = "分页类型(0:全部(未使用),1:即将过期,2:已使用,3:已过期/已失效)", dataType = "String", paramType = "query", required = true),
             @ApiImplicitParam(name = "type", value = "券类型(不传或1:优惠券;代金券 type=4 已下线)", dataType = "Integer", paramType = "query", required = false),
             @ApiImplicitParam(name = "couponType", value = "优惠券类型:1=仅满减券,2=仅折扣券,不传=全部优惠券(可选)", dataType = "Integer", paramType = "query", required = false),
             @ApiImplicitParam(name = "storeId", value = "商铺ID,可为空,传则仅返回该商铺的优惠券", dataType = "String", paramType = "query", required = false),

+ 8 - 1
alien-entity/src/main/java/shop/alien/entity/store/LawyerUser.java

@@ -26,6 +26,13 @@ import java.util.List;
 @ApiModel(value = "LawyerUser对象", description = "律师用户")
 public class LawyerUser extends Model<LawyerUser> {
 
+    /** logout_flag:未注销 */
+    public static final int LOGOUT_FLAG_NORMAL = 0;
+    /** logout_flag:已注销(冷静期结束后) */
+    public static final int LOGOUT_FLAG_DONE = 1;
+    /** logout_flag:注销冷静期 */
+    public static final int LOGOUT_FLAG_COOLING = 2;
+
     public LawyerUser() {
     }
 
@@ -101,7 +108,7 @@ public class LawyerUser extends Model<LawyerUser> {
     @TableField("updated_user_id")
     private Integer updatedUserId;
 
-    @ApiModelProperty(value = "注销标记, 0:未注销, 1:已注销")
+    @ApiModelProperty(value = "注销标记, 0:未注销, 1:已注销, 2:注销冷静期")
     @TableField("logout_flag")
     private Integer logoutFlag;
 

+ 9 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeCollect.java

@@ -21,8 +21,17 @@ public class LifeCollect {
 
     private String storeId;
 
+    @ApiModelProperty(value = "收藏人身份键(user_{phone} 或历史数字 userId 字符串)")
     private String userId;
 
+    @TableField("user_id_user_type")
+    @ApiModelProperty(value = "收藏人用户分类:1-用户(life_user) 2-商户(store_user) 3-律师(lawyer_user)")
+    private Integer userIdUserType;
+
+    @TableField("user_id_ref_id")
+    @ApiModelProperty(value = "收藏人对应表主键id(与 user_id_user_type 对应)")
+    private Integer userIdRefId;
+
     @ApiModelProperty(value = "团购id")
     @TableField("coupon_id")
     private String couponId;

+ 8 - 1
alien-entity/src/main/java/shop/alien/entity/store/LifeDiscountCoupon.java

@@ -29,6 +29,13 @@ public class LifeDiscountCoupon extends Model<LifeDiscountCoupon> {
 
     private static final long serialVersionUID = 1L;
 
+    /** 优惠券状态:草稿 */
+    public static final int COUPON_STATUS_DRAFT = 0;
+    /** 优惠券状态:正式 */
+    public static final int COUPON_STATUS_FORMAL = 1;
+    /** 优惠券状态:已失效(如店铺注销冷静期结束) */
+    public static final int COUPON_STATUS_INVALID = 2;
+
     @ApiModelProperty(value = "主键")
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
@@ -146,7 +153,7 @@ public class LifeDiscountCoupon extends Model<LifeDiscountCoupon> {
     @TableField("claim_rule_customize_value")
     private Integer claimRuleCustomizeValue;
 
-    @ApiModelProperty(value = "优惠券状态:0:草稿,1:正式")
+    @ApiModelProperty(value = "优惠券状态:0-草稿,1-正式,2-已失效")
     @TableField("coupon_status")
     private Integer couponStatus;
 

+ 16 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeFans.java

@@ -28,6 +28,22 @@ public class LifeFans {
     @ApiModelProperty(value = "粉丝id ('store'或'user' + '_' + 手机号)")
     private String fansId;
 
+    @TableField("fans_user_type")
+    @ApiModelProperty(value = "粉丝用户分类:1-用户(life_user) 2-商户(store_user) 3-律师(lawyer_user)")
+    private Integer fansUserType;
+
+    @TableField("fans_ref_id")
+    @ApiModelProperty(value = "粉丝对应表主键id(与 fans_user_type 对应)")
+    private Integer fansRefId;
+
+    @TableField("followed_user_type")
+    @ApiModelProperty(value = "被关注方用户分类:1-用户 2-商户 3-律师")
+    private Integer followedUserType;
+
+    @TableField("followed_ref_id")
+    @ApiModelProperty(value = "被关注方对应表主键id(与 followed_user_type 对应)")
+    private Integer followedRefId;
+
     @TableField("fans_type")
     private String fansType;
 

+ 9 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeLikeRecord.java

@@ -19,8 +19,17 @@ public class LifeLikeRecord {
     @TableId(value = "id", type = IdType.AUTO)
     private String id;
 
+    @ApiModelProperty(value = "点赞人身份键('user_'/'store_' + 手机号等)")
     private String dianzanId;
 
+    @TableField("dianzan_user_type")
+    @ApiModelProperty(value = "点赞人用户分类:1-用户 2-商户 3-律师")
+    private Integer dianzanUserType;
+
+    @TableField("dianzan_ref_id")
+    @ApiModelProperty(value = "点赞人对应表主键id(与 dianzan_user_type 对应)")
+    private Integer dianzanRefId;
+
     private String huifuId;
 
     @ApiModelProperty(value = "1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工 9-员工评价点赞 10-员工评论点赞")

+ 26 - 2
alien-entity/src/main/java/shop/alien/entity/store/LifeMessage.java

@@ -19,15 +19,31 @@ public class LifeMessage {
     @TableId(type = IdType.AUTO)
     private Integer id;
 
-    @ApiModelProperty(value = "发送人id ('store_' 或 'user_' + 手机号)")
+    @ApiModelProperty(value = "发送人id ('user_'/'store_'/'lawyer_' + 手机号)")
     private String senderId;
 
+    @TableField("sender_user_type")
+    @ApiModelProperty(value = "发送人用户分类:1-用户 2-商户 3-律师")
+    private Integer senderUserType;
+
+    @TableField("sender_ref_id")
+    @ApiModelProperty(value = "发送人对应表主键id(与 sender_user_type 对应)")
+    private Integer senderRefId;
+
     @ApiModelProperty(value = "发送人姓名")
     private String senderName;
 
-    @ApiModelProperty(value = "接收人id('store_' 或 'user_' + 手机号)")
+    @ApiModelProperty(value = "接收人id('user_'/'store_'/'lawyer_' + 手机号)")
     private String receiverId;
 
+    @TableField("receiver_user_type")
+    @ApiModelProperty(value = "接收人用户分类:1-用户 2-商户 3-律师")
+    private Integer receiverUserType;
+
+    @TableField("receiver_ref_id")
+    @ApiModelProperty(value = "接收人对应表主键id(与 receiver_user_type 对应)")
+    private Integer receiverRefId;
+
     @ApiModelProperty(value = "接收人姓名")
     private String receiverName;
 
@@ -46,6 +62,14 @@ public class LifeMessage {
     @TableField("delete_phone_id")
     private String deletePhoneId;
 
+    @TableField("delete_phone_user_type")
+    @ApiModelProperty(value = "删除方用户分类:1-用户 2-商户 3-律师")
+    private Integer deletePhoneUserType;
+
+    @TableField("delete_phone_ref_id")
+    @ApiModelProperty(value = "删除方对应表主键id(与 delete_phone_user_type 对应)")
+    private Integer deletePhoneRefId;
+
     @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
     @TableField("delete_flag")
     @TableLogic

+ 18 - 2
alien-entity/src/main/java/shop/alien/entity/store/LifeNotice.java

@@ -19,12 +19,28 @@ public class LifeNotice {
     @TableId(value = "id", type = IdType.AUTO)
     private String id;
 
-    @ApiModelProperty(value = "发表公告人id")
+    @ApiModelProperty(value = "发表公告人id('user_'/'store_'/'lawyer_' + 手机号,或 system)")
     private String senderId;
 
-    @ApiModelProperty(value = "接收人id")
+    @TableField("sender_user_type")
+    @ApiModelProperty(value = "发送人用户分类:1-用户 2-商户 3-律师")
+    private Integer senderUserType;
+
+    @TableField("sender_ref_id")
+    @ApiModelProperty(value = "发送人对应表主键id(与 sender_user_type 对应)")
+    private Integer senderRefId;
+
+    @ApiModelProperty(value = "接收人id('user_'/'store_'/'lawyer_' + 手机号)")
     private String receiverId;
 
+    @TableField("receiver_user_type")
+    @ApiModelProperty(value = "接收人用户分类:1-用户 2-商户 3-律师")
+    private Integer receiverUserType;
+
+    @TableField("receiver_ref_id")
+    @ApiModelProperty(value = "接收人对应表主键id(与 receiver_user_type 对应)")
+    private Integer receiverRefId;
+
     @ApiModelProperty(value = "业务id  关注通知-粉丝id;动态通知-动态id;")
     @TableField("business_id")
     private Integer businessId;

+ 14 - 1
alien-entity/src/main/java/shop/alien/entity/store/LifeUser.java

@@ -25,6 +25,15 @@ import java.util.Date;
 public class LifeUser implements Serializable {
     private static final long serialVersionUID = 1L;
 
+    /** logout_flag:未注销 */
+    public static final int LOGOUT_FLAG_NORMAL = 0;
+    /** logout_flag:已注销 */
+    public static final int LOGOUT_FLAG_DONE = 1;
+    /** logout_flag:注销冷静期 */
+    public static final int LOGOUT_FLAG_COOLING = 2;
+    /** 注销完成后禁止重新注册的天数(自 logoutTime 日起,T+14 00:00 起可注册) */
+    public static final int RE_REGISTER_BLOCK_DAYS = 14;
+
     @ApiModelProperty(value = "主键")
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
@@ -126,7 +135,7 @@ public class LifeUser implements Serializable {
     @TableField("invited_num")
     private String invitedNum;
 
-    @ApiModelProperty(value = "注销标记, 0:未注销, 1:已注销")
+    @ApiModelProperty(value = "注销标记, 0:未注销, 1:已注销, 2:注销冷静期")
     @TableField("logout_flag")
     private Integer logoutFlag;
 
@@ -192,6 +201,10 @@ public class LifeUser implements Serializable {
     @TableField("is_popup")
     private Integer isPopup;
 
+    @ApiModelProperty(value = "注销其他原因")
+    @TableField("logout_context")
+    private String logoutContext;
+
     @ApiModelProperty(value = "设备唯一标识ID")
     @TableField("device_id")
     private String deviceId;

+ 8 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeUserDynamics.java

@@ -25,6 +25,14 @@ public class LifeUserDynamics {
      */
     private String phoneId;
 
+    @TableField("phone_user_type")
+    @ApiModelProperty(value = "发布人用户分类:1-用户(life_user) 2-商户(store_user) 3-律师(lawyer_user)")
+    private Integer phoneUserType;
+
+    @TableField("phone_ref_id")
+    @ApiModelProperty(value = "发布人对应表主键id(与 phone_user_type 对应)")
+    private Integer phoneRefId;
+
     /**
      * 标题
      */

+ 8 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreClockIn.java

@@ -43,6 +43,14 @@ public class StoreClockIn extends Model<StoreClockIn> {
     @TableField("phone_id")
     private String phoneId;
 
+    @ApiModelProperty(value = "打卡人用户分类:1-用户(life_user) 2-商户(store_user) 3-律师(lawyer_user)")
+    @TableField("phone_user_type")
+    private Integer phoneUserType;
+
+    @ApiModelProperty(value = "打卡人对应表主键id(与 phone_user_type 对应)")
+    @TableField("phone_ref_id")
+    private Integer phoneRefId;
+
     @ApiModelProperty(value = "打卡内容")
     @TableField("content")
     private String content;

+ 8 - 1
alien-entity/src/main/java/shop/alien/entity/store/StoreUser.java

@@ -24,6 +24,13 @@ import java.util.List;
 @ApiModel(value = "StoreUser对象", description = "门店用户")
 public class StoreUser extends Model<StoreUser> {
 
+    /** logout_flag:未注销 */
+    public static final int LOGOUT_FLAG_NORMAL = 0;
+    /** logout_flag:已注销(冷静期结束后) */
+    public static final int LOGOUT_FLAG_DONE = 1;
+    /** logout_flag:注销冷静期 */
+    public static final int LOGOUT_FLAG_COOLING = 2;
+
     public StoreUser() {
     }
 
@@ -97,7 +104,7 @@ public class StoreUser extends Model<StoreUser> {
     @TableField("updated_user_id")
     private Integer updatedUserId;
 
-    @ApiModelProperty(value = "注销标记, 0:未注销, 1:已注销")
+    @ApiModelProperty(value = "注销标记, 0:未注销, 1:已注销, 2:注销冷静期")
     @TableField("logout_flag")
     private Integer logoutFlag;
 

+ 30 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/LawyerLogoutRequestDto.java

@@ -0,0 +1,30 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 律师账号注销请求
+ */
+@Data
+@ApiModel(value = "LawyerLogoutRequestDto", description = "律师账号注销请求")
+public class LawyerLogoutRequestDto implements Serializable {
+
+    @ApiModelProperty(value = "律师ID", required = true)
+    private Integer id;
+
+    @ApiModelProperty(value = "手机号", required = true)
+    private String phone;
+
+    @ApiModelProperty(value = "短信验证码", required = true)
+    private String verificationCode;
+
+    @ApiModelProperty(value = "注销原因(如:多余账号、使用过程中遇到困难、安全/隐私顾虑、不再从事该行业、其他)", required = true)
+    private String logoutReason;
+
+    @ApiModelProperty(value = "注销内容,原因为「其他」时必填,最多300字")
+    private String logoutContent;
+}

+ 48 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerUserLogoutListVo.java

@@ -0,0 +1,48 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 中台-律师注销列表
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "LawyerUserLogoutListVo", description = "律师注销列表")
+public class LawyerUserLogoutListVo {
+
+    @ApiModelProperty(value = "律师用户ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "律师姓名(lawyer_user.name)")
+    private String name;
+
+    @ApiModelProperty(value = "电话")
+    private String phone;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "申请注销时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date applyLogoutTime;
+
+    @ApiModelProperty(value = "注销时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date logoutFinishTime;
+
+    @ApiModelProperty(value = "注销标记:0正常,1已注销,2注销冷静期")
+    private Integer logoutFlag;
+
+    @ApiModelProperty(value = "状态文案:注销中/已注销")
+    private String statusName;
+
+    @ApiModelProperty(value = "注销原因")
+    private String logoutReason;
+}

+ 4 - 1
alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerUserVo.java

@@ -88,7 +88,7 @@ public class LawyerUserVo implements Serializable {
     @TableField("updated_user_id")
     private Integer updatedUserId;
 
-    @ApiModelProperty(value = "注销标记, 0:未注销, 1:已注销")
+    @ApiModelProperty(value = "注销标记, 0:未注销, 1:已注销, 2:注销冷静期")
     @TableField("logout_flag")
     private Integer logoutFlag;
 
@@ -304,6 +304,9 @@ public class LawyerUserVo implements Serializable {
     @ApiModelProperty(value = "登录Token")
     private String token;
 
+    @ApiModelProperty(value = "申请注销结束时间(logoutTime+7天),格式yyyy/mm/dd,未申请注销时为null")
+    private String logoutEndTime;
+
     @ApiModelProperty(value = "一级法律场景id")
     private String firstLevelScenarioId;
 

+ 2 - 2
alien-entity/src/main/java/shop/alien/entity/store/vo/LifeDiscountCouponVo.java

@@ -96,7 +96,7 @@ public class LifeDiscountCouponVo {
     @ApiModelProperty(value = "类型   1-优惠券  2-红包")
     private Integer type;
 
-    @ApiModelProperty(value = "状态(0:进行中,1:已结束,2:未开始,3:已暂停,4:已售罄,5:草稿)")
+    @ApiModelProperty(value = "状态:商家券列表为(0进行中/1已结束/…);getUserCouponList 为用户券状态(0待使用/1已使用/2已作废)")
     private Integer status;
 
     @ApiModelProperty(value = "优惠券状态文案,用于前端展示:进行中/已结束/未开始/已暂停/已售罄/草稿")
@@ -168,7 +168,7 @@ public class LifeDiscountCouponVo {
     @ApiModelProperty(value = "结束领取时间")
     private LocalDate endGetDate;
 
-    @ApiModelProperty(value = "优惠券状态:0:草稿,1:正式")
+    @ApiModelProperty(value = "优惠券状态:0-草稿,1-正式,2-已失效")
     private Integer couponStatus;
 
     @ApiModelProperty(value = "有效期")

+ 35 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LifeUserCancelAccountResultVo.java

@@ -0,0 +1,35 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * C 端用户申请注销接口返回体
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "LifeUserCancelAccountResultVo", description = "用户端申请注销返回")
+public class LifeUserCancelAccountResultVo {
+
+    /** 0-注销成功 */
+    public static final int STATUS_SUCCESS = 0;
+    /** 1-验证码错误 */
+    public static final int STATUS_SMS_ERROR = 1;
+    /** 2-业务校验信息 */
+    public static final int STATUS_BUSINESS = 2;
+
+    @ApiModelProperty(value = "状态:0-注销成功,1-验证码错误,2-业务校验信息")
+    private Integer status;
+
+    @ApiModelProperty(value = "具体提示:注销成功 / 验证码错误文案 / 业务校验拦截文案")
+    private String message;
+
+    public static LifeUserCancelAccountResultVo of(int status, String message) {
+        LifeUserCancelAccountResultVo vo = new LifeUserCancelAccountResultVo();
+        vo.setStatus(status);
+        vo.setMessage(message);
+        return vo;
+    }
+}

+ 3 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LifeUserVo.java

@@ -72,4 +72,7 @@ public class LifeUserVo extends LifeUser {
 
     @ApiModelProperty(value = "是否已设置登录密码")
     private Boolean hasLoginPassword;
+
+    @ApiModelProperty(value = "申请注销结束时间(logoutTime+7天),格式yyyy/mm/dd,未申请注销时为null")
+    private String logoutEndTime;
 }

+ 3 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreClockInVo.java

@@ -80,4 +80,7 @@ public class StoreClockInVo extends StoreClockIn {
 
     @ApiModelProperty(value = "经营板块名称")
     private String businessSectionName;
+
+    @ApiModelProperty(value = "注销账号(0:正常,1:已注销)")
+    private Integer logoutFlag;
 }

+ 51 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreUserLogoutListVo.java

@@ -0,0 +1,51 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 中台-商家注销列表
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreUserLogoutListVo", description = "商家注销列表")
+public class StoreUserLogoutListVo {
+
+    @ApiModelProperty(value = "商家用户ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "用户名(store_user.name)")
+    private String name;
+
+    @ApiModelProperty(value = "真实姓名")
+    private String realName;
+
+    @ApiModelProperty(value = "电话")
+    private String phone;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "申请注销时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date applyLogoutTime;
+
+    @ApiModelProperty(value = "注销时间(预计或完成时间)")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date logoutFinishTime;
+
+    @ApiModelProperty(value = "注销标记:0正常,1已注销,2注销冷静期")
+    private Integer logoutFlag;
+
+    @ApiModelProperty(value = "状态文案:注销中/已注销")
+    private String statusName;
+
+    @ApiModelProperty(value = "注销原因")
+    private String logoutReason;
+}

+ 23 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreUserWithStoreVo.java

@@ -0,0 +1,23 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import shop.alien.entity.store.StoreInfo;
+import shop.alien.entity.store.StoreUser;
+
+/**
+ * 店铺用户及其绑定店铺信息
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreUserWithStoreVo", description = "店铺用户及绑定店铺信息")
+public class StoreUserWithStoreVo {
+
+    @ApiModelProperty(value = "店铺用户信息")
+    private StoreUser storeUser;
+
+    @ApiModelProperty(value = "绑定的店铺信息")
+    private StoreInfo storeInfo;
+}

+ 2 - 2
alien-entity/src/main/java/shop/alien/mapper/CommonCommentMapper.java

@@ -37,7 +37,7 @@ public interface CommonCommentMapper extends BaseMapper<CommonComment> {
             "IF(cc.comment_type = 1, lu.user_name, su.nick_name) AS headName, " +
             "IF(cc.comment_type = 1, lu.user_phone, su.phone) AS headPhone, " +
             "IF(cc.comment_type = 2, si.store_name, NULL) AS storeName, " +
-            "IF(llr.dianzan_id IS NULL, '0', '1') AS isLike " +
+            "IF(llr.id IS NULL, '0', '1') AS isLike " +
             "FROM common_comment cc " +
             "LEFT JOIN life_user lu ON cc.user_id = lu.id AND lu.delete_flag = 0 " +
             "LEFT JOIN store_info si ON cc.merchant_id = si.id AND si.delete_flag = 0 " +
@@ -45,7 +45,7 @@ public interface CommonCommentMapper extends BaseMapper<CommonComment> {
             "AND su.id = (SELECT MIN(su2.id) FROM store_user su2 WHERE su2.store_id = cc.merchant_id AND su2.delete_flag = 0) " +
             "LEFT JOIN life_like_record llr ON llr.huifu_id = cc.id " +
             "AND llr.`type` = #{type} " +
-            "AND llr.dianzan_id = #{dianzanId} " +
+            "AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{dianzanId} " +
             "AND llr.delete_flag = 0 " +
             "${ew.customSqlSegment}")
     List<CommonCommentVo> selectALlComment(@Param("page") Page<CommonCommentVo> page,

+ 9 - 0
alien-entity/src/main/java/shop/alien/mapper/LawyerUserMapper.java

@@ -1,6 +1,7 @@
 package shop.alien.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -142,5 +143,13 @@ Integer updateLawyerUser(LawyerUser user);
                                   @Param("firmId") Integer firmId,
                                   @Param("createdTimeStart") String createdTimeStart);
 
+    /**
+     * 中台-律师注销列表(含已逻辑删除记录,不走 TableLogic)
+     */
+    IPage<LawyerUser> selectLogoutPage(IPage<LawyerUser> page,
+                                       @Param("name") String name,
+                                       @Param("phone") String phone,
+                                       @Param("logoutFlag") Integer logoutFlag);
+
 }
 

+ 8 - 4
alien-entity/src/main/java/shop/alien/mapper/LifeBrowseRecordMapper.java

@@ -35,7 +35,12 @@ public interface LifeBrowseRecordMapper extends BaseMapper<LifeBrowseRecord> {
             "lud.id dynamicsId, " +
             "IFNULL(lud.cover_image, '') coverImage, " +
             "lbr.liulan_date liulanDate, " +
-            "lud.phone_id phoneId, " +
+            "CASE WHEN lud.phone_user_type = 1 THEN CONCAT('user_', (SELECT user_phone FROM life_user WHERE id = lud.phone_ref_id AND delete_flag = 0 LIMIT 1)) " +
+            "WHEN lud.phone_user_type = 2 THEN CONCAT('store_', (SELECT phone FROM store_user WHERE id = lud.phone_ref_id AND delete_flag = 0 LIMIT 1)) " +
+            "WHEN lud.phone_user_type = 3 THEN CONCAT('lawyer_', (SELECT phone FROM lawyer_user WHERE id = lud.phone_ref_id AND delete_flag = 0 LIMIT 1)) " +
+            "END phoneId, " +
+            "lud.phone_user_type phoneUserType, " +
+            "lud.phone_ref_id phoneRefId, " +
             "lud.title, " +
             "lud.context, " +
             "lud.image_path imagePath, " +
@@ -74,9 +79,8 @@ public interface LifeBrowseRecordMapper extends BaseMapper<LifeBrowseRecord> {
             "INNER JOIN life_user_dynamics lud ON lud.id = lbr.dynamics_id " +
             "AND lud.delete_flag = 0 " +
             "AND lbr.delete_flag = 0 " +
-            "LEFT JOIN store_user su ON SUBSTRING_INDEX(lud.phone_id, '_', -1) = su.phone " +
-            "AND su.delete_flag = 0 " +
-            "AND SUBSTRING_INDEX(lud.phone_id, '_', 1) = 'store' " +
+            "LEFT JOIN store_user su ON su.delete_flag = 0 " +
+            "AND lud.phone_user_type = 2 AND su.id = lud.phone_ref_id " +
             "LEFT JOIN store_info si1 ON si1.id = su.store_id " +
             "AND si1.delete_flag = 0 " +
             "LEFT JOIN ( " +

+ 7 - 4
alien-entity/src/main/java/shop/alien/mapper/LifeCollectMapper.java

@@ -3,6 +3,7 @@ package shop.alien.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import shop.alien.entity.second.vo.SecondGoodsRecommendVo;
 import shop.alien.entity.store.LifeCollect;
@@ -19,8 +20,9 @@ public interface LifeCollectMapper extends BaseMapper<LifeCollect> {
      * 获取收藏的二级商品
      * @param page
      * @param userId
-     * @param s
-     * @param s1
+     * @param collectUserType 收藏人 user_id_user_type
+     * @param collectRefId 收藏人 user_id_ref_id
+     * @param position 经纬度
      * @return
      */
     @Select("<script>" +
@@ -42,7 +44,8 @@ public interface LifeCollectMapper extends BaseMapper<LifeCollect> {
             "  ON lc.business_id = sg.id " +
             "  AND lc.business_type = 1 " +
             "  AND lc.delete_flag = 0 " +
-            "  AND lc.user_id = #{s} " +
+            "  AND lc.user_id_user_type = #{collectUserType} " +
+            "  AND lc.user_id_ref_id = #{collectRefId} " +
             "WHERE sg.delete_flag = 0 " +
             // 动态排除屏蔽商品(空集合不拼接,避免SQL报错)
             "<if test=\"shieldedGoodsIds != null and !shieldedGoodsIds.isEmpty()\">" +
@@ -55,5 +58,5 @@ public interface LifeCollectMapper extends BaseMapper<LifeCollect> {
             // 按收藏时间倒序排序
             "ORDER BY lc.created_time DESC" +
             "</script>")
-    IPage<SecondGoodsRecommendVo> collectSecondGoodsByPage(IPage<SecondGoodsRecommendVo> page, Integer userId, String s, String position, List<Integer> shieldedGoodsIds);
+    IPage<SecondGoodsRecommendVo> collectSecondGoodsByPage(IPage<SecondGoodsRecommendVo> page, @Param("userId") Integer userId, @Param("collectUserType") Integer collectUserType, @Param("collectRefId") Integer collectRefId, @Param("position") String position, @Param("shieldedGoodsIds") List<Integer> shieldedGoodsIds);
 }

+ 173 - 139
alien-entity/src/main/java/shop/alien/mapper/LifeFansMapper.java

@@ -18,117 +18,117 @@ public interface LifeFansMapper extends BaseMapper<LifeFans> {
     @Select("<script>" +
             "select MAX(foll.id) id, MAX(foll.name) name, MAX(foll.image) image, foll.phoneId, MAX(foll.blurb) blurb, MAX(foll.blockedType) blockedType, MAX(foll.blockedId) blockedId, MAX(foll.username) username, MAX(foll.accountBlurb) accountBlurb, MAX(foll.isMerchant) isMerchant, MAX(foll.created_time) created_time, " +
             "  MAX(lb.id) blackListid, MAX(if(isnull(fans.id), 0, 1)) isFollowMe, MAX(if(isnull(fans_this.id), 0, 1)) isFollowThis, MAX(if(isnull(lb.id), '0', '1')) isBlocked,  " +
-            "  (select count(1) from life_fans where fans_id= foll.phoneId and delete_flag =0) followNum, " +
-            "  (select count(1) from life_fans where followed_id= foll.phoneId and delete_flag =0) fansNum from ( " +
+            "  (select count(1) from life_fans lf_cnt where lf_cnt.fans_user_type = foll.followedUserType and lf_cnt.fans_ref_id = foll.followedRefId and lf_cnt.delete_flag = 0) followNum, " +
+            "  (select count(1) from life_fans lf_cnt2 where lf_cnt2.followed_user_type = foll.followedUserType and lf_cnt2.followed_ref_id = foll.followedRefId and lf_cnt2.delete_flag = 0) fansNum from ( " +
             "    with follow as (   " +
-            "        select substring_index(followed_id, '_', 1) as flag, substring_index(followed_id, '_', -1) as phone, created_time   " +
+            "        select followed_user_type as followedUserType, followed_ref_id as followedRefId, created_time   " +
             "        from life_fans   " +
-            "        where delete_flag = 0 and fans_id = #{fansId} " +
+            "        where delete_flag = 0 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "        and fans_user_type = #{identityUserType} and fans_ref_id = #{identityRefId} " +
+            "</if>" +
             "    )   " +
             "    select info.id, IF(info.store_application_status = 0, user.nick_name, info.store_name) AS NAME," +
-            "    user.head_img image, concat('store_', user.phone) phoneId, IF(info.store_application_status = 0, user.account_blurb, info.store_blurb) AS blurb, 1 blockedType,user.id blockedId ,IFNULL(user.nick_name, user.name) username, user.account_blurb accountBlurb, IF(info.store_application_status = 1, '1', '0') AS isMerchant, foll.created_time " +
+            "    user.head_img image, concat('store_', user.phone) phoneId, IF(info.store_application_status = 0, user.account_blurb, info.store_blurb) AS blurb, 1 blockedType,user.id blockedId ,IFNULL(user.nick_name, user.name) username, user.account_blurb accountBlurb, IF(info.store_application_status = 1, '1', '0') AS isMerchant, foll.created_time, 2 as followedUserType, user.id as followedRefId " +
             "    from follow foll " +
-            "    join store_user user on foll.phone = user.phone " +
+            "    join store_user user on foll.followedUserType = 2 and user.id = foll.followedRefId " +
             "    join store_info info on info.id = user.store_id " +
             "    left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
-            "    where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0 " +
+            "    where user.delete_flag = 0 and info.delete_flag = 0 " +
             "<if test=\"onlyStoreFollowed == false\">" +
             "    union " +
-            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 2 blockedType,user.id blockedId,'' username, '' accountBlurb, '0' AS isMerchant, foll.created_time " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 2 blockedType,user.id blockedId,'' username, '' accountBlurb, '0' AS isMerchant, foll.created_time, 1 as followedUserType, user.id as followedRefId " +
             "    from follow foll " +
-            "    join life_user user on foll.phone = user.user_phone   " +
-            "    where foll.flag = 'user' and user.delete_flag = 0   " +
+            "    join life_user user on foll.followedUserType = 1 and user.id = foll.followedRefId   " +
+            "    where user.delete_flag = 0   " +
             "</if>" +
             ") foll   " +
-            "left join life_fans fans on fans.fans_id = foll.phoneId and fans.followed_id = #{relationFansId} and fans.delete_flag = 0 " +
-            "left join life_fans fans_this on fans_this.fans_id = #{relationFansId} and fans_this.followed_id = foll.phoneId and fans_this.delete_flag = 0 " +
+            "left join life_fans fans on fans.fans_user_type = foll.followedUserType and fans.fans_ref_id = foll.followedRefId and fans.delete_flag = 0 " +
+            "<if test=\"relationIdentityUserType != null and relationIdentityRefId != null\">" +
+            " and fans.followed_user_type = #{relationIdentityUserType} and fans.followed_ref_id = #{relationIdentityRefId} " +
+            "</if>" +
+            "left join life_fans fans_this on fans_this.followed_user_type = foll.followedUserType and fans_this.followed_ref_id = foll.followedRefId and fans_this.delete_flag = 0 " +
+            "<if test=\"relationIdentityUserType != null and relationIdentityRefId != null\">" +
+            " and fans_this.fans_user_type = #{relationIdentityUserType} and fans_this.fans_ref_id = #{relationIdentityRefId} " +
+            "</if>" +
             "left join life_blacklist lb on lb.blocked_type = foll.blockedType and lb.blocked_id = foll.blockedId and lb.blocker_type = #{blockerType} and lb.blocker_id = #{blockerId} and lb.delete_flag = 0 " +
             "${ew.customSqlSegment} " +
             "</script>")
-    IPage<LifeFansVo> getMyFollowed(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("relationFansId") String relationFansId, @Param("blockerType") String blockerType, @Param("blockerId") String blockerId, @Param("onlyStoreFollowed") boolean onlyStoreFollowed, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+    IPage<LifeFansVo> getMyFollowed(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("identityUserType") Integer identityUserType, @Param("identityRefId") Integer identityRefId, @Param("relationFansId") String relationFansId, @Param("relationIdentityUserType") Integer relationIdentityUserType, @Param("relationIdentityRefId") Integer relationIdentityRefId, @Param("blockerType") String blockerType, @Param("blockerId") String blockerId, @Param("onlyStoreFollowed") boolean onlyStoreFollowed, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
 
 
-    @Select("select foll.*, if(isnull(fans.id), 0, 1) isFollowMe, 1 as isFollowThis from ( " +
+    @Select("<script>" +
+            "select foll.*, if(isnull(fans.id), 0, 1) isFollowMe, 1 as isFollowThis from ( " +
             "    with follow as (   " +
-            "        select substring_index(followed_id, '_', 1) as flag, substring_index(followed_id, '_', -1) as phone   " +
+            "        select followed_user_type as followedUserType, followed_ref_id as followedRefId   " +
             "        from life_fans   " +
-            "        where delete_flag = 0 and fans_id = #{fansId} " +
+            "        where delete_flag = 0 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "        and fans_user_type = #{identityUserType} and fans_ref_id = #{identityRefId} " +
+            "</if>" +
             "    )   " +
-            "    select info.id, info.store_name name, img.img_url image, concat('store_', user.phone) phoneId " +
+            "    select info.id, info.store_name name, img.img_url image, concat('store_', user.phone) phoneId, 2 as followedUserType, user.id as followedRefId " +
             "    from follow foll " +
-            "    join store_user user on foll.phone = user.phone " +
+            "    join store_user user on foll.followedUserType = 2 and user.id = foll.followedRefId " +
             "    join store_info info on info.id = user.store_id " +
             "    left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
-            "    where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0 " +
+            "    where user.delete_flag = 0 and info.delete_flag = 0 " +
             "    union " +
-            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, 1 as followedUserType, user.id as followedRefId " +
             "    from follow foll " +
-            "    join life_user user on foll.phone = user.user_phone   " +
-            "    where foll.flag = 'user' and user.delete_flag = 0   " +
+            "    join life_user user on foll.followedUserType = 1 and user.id = foll.followedRefId   " +
+            "    where user.delete_flag = 0   " +
             ") foll " +
-            "left join life_fans fans on fans.fans_id = foll.phoneId and fans.followed_id = #{fansId} and fans.delete_flag = 0 ")
-    List<LifeFansVo> getMyFollowedAll(@Param("fansId") String fansId);
+            "left join life_fans fans on fans.fans_user_type = foll.followedUserType and fans.fans_ref_id = foll.followedRefId and fans.delete_flag = 0 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            " and fans.followed_user_type = #{identityUserType} and fans.followed_ref_id = #{identityRefId}" +
+            "</if>" +
+            "</script>")
+    List<LifeFansVo> getMyFollowedAll(@Param("fansId") String fansId, @Param("identityUserType") Integer identityUserType, @Param("identityRefId") Integer identityRefId);
 
     @Select("<script>" +
             "select MAX(foll.id) id, MAX(foll.name) name, MAX(foll.image) image, foll.phoneId, MAX(foll.blurb) blurb, MAX(foll.blockedType) blockedType, MAX(foll.blockedId) blockedId, MAX(foll.isMerchant) isMerchant, MAX(foll.created_time) created_time, " +
             "  MAX(lb.id) blackListid, MAX(if(isnull(fans.id), 0, 1)) isFollowThis, MAX(if(isnull(fans_me.id), 0, 1)) isFollowMe, MAX(if(isnull(lb.id), '0', '1')) isBlocked, " +
-            "    (select count(1) from life_fans fans2 where fans2.followed_id = foll.phoneId and fans2.delete_flag = 0) fansNum, " +
-            "    (select count(1) from life_fans fans3 where fans3.fans_id = foll.phoneId and fans3.delete_flag = 0) followNum " +
+            "    (select count(1) from life_fans fans2 where fans2.followed_user_type = foll.fansUserType and fans2.followed_ref_id = foll.fansRefId and fans2.delete_flag = 0) fansNum, " +
+            "    (select count(1) from life_fans fans3 where fans3.fans_user_type = foll.fansUserType and fans3.fans_ref_id = foll.fansRefId and fans3.delete_flag = 0) followNum " +
             "from ( " +
             "    with follow as ( " +
-            "        select substring_index(fans_id, '_', 1) as flag, substring_index(fans_id, '_', -1) as phone, created_time " +
+            "        select fans_user_type as fansUserType, fans_ref_id as fansRefId, created_time " +
             "        from life_fans " +
-            "        where delete_flag = 0 and followed_id = #{fansId} " +
+            "        where delete_flag = 0 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "        and followed_user_type = #{identityUserType} and followed_ref_id = #{identityRefId} " +
+            "</if>" +
             "    ) " +
-            "    select user.id, IF(info.store_application_status = 0, user.nick_name, info.store_name) AS name, user.head_img image, concat('store_', user.phone) phoneId, IF(info.store_application_status = 0, user.account_blurb, info.store_blurb) AS blurb, 1 blockedType,user.id blockedId, IF(info.store_application_status = 1, '1', '0') AS isMerchant, foll.created_time " +
+            "    select user.id, IF(info.store_application_status = 0, user.nick_name, info.store_name) AS name, user.head_img image, concat('store_', user.phone) phoneId, IF(info.store_application_status = 0, user.account_blurb, info.store_blurb) AS blurb, 1 blockedType,user.id blockedId, IF(info.store_application_status = 1, '1', '0') AS isMerchant, foll.created_time, 2 as fansUserType, user.id as fansRefId " +
             "    from follow foll " +
-            "    join store_user user on foll.phone = user.phone " +
+            "    join store_user user on foll.fansUserType = 2 and user.id = foll.fansRefId " +
             "    join store_info info on info.id = user.store_id " +
             "    left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
-            "    where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0" +
+            "    where user.delete_flag = 0 and info.delete_flag = 0" +
             "<if test=\"onlyStoreFans == false\">" +
             "    union " +
-            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 2 blockedType,user.id blockedId, '0' AS isMerchant, foll.created_time " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 2 blockedType,user.id blockedId, '0' AS isMerchant, foll.created_time, 1 as fansUserType, user.id as fansRefId " +
             "    from follow foll " +
-            "    join life_user user on foll.phone = user.user_phone " +
-            "    where foll.flag = 'user' and user.delete_flag = 0 " +
+            "    join life_user user on foll.fansUserType = 1 and user.id = foll.fansRefId " +
+            "    where user.delete_flag = 0 " +
             "</if>" +
             ") foll " +
-            "left join life_fans fans on fans.followed_id = foll.phoneId and fans.fans_id = #{relationFansId} and fans.delete_flag = 0 " +
-            "left join life_fans fans_me on fans_me.fans_id = foll.phoneId and fans_me.followed_id = #{relationFansId} and fans_me.delete_flag = 0 " +
+            "left join life_fans fans on fans.followed_user_type = foll.fansUserType and fans.followed_ref_id = foll.fansRefId and fans.delete_flag = 0 " +
+            "<if test=\"relationIdentityUserType != null and relationIdentityRefId != null\">" +
+            " and fans.fans_user_type = #{relationIdentityUserType} and fans.fans_ref_id = #{relationIdentityRefId}" +
+            "</if>" +
+            "left join life_fans fans_me on fans_me.fans_user_type = foll.fansUserType and fans_me.fans_ref_id = foll.fansRefId and fans_me.delete_flag = 0 " +
+            "<if test=\"relationIdentityUserType != null and relationIdentityRefId != null\">" +
+            " and fans_me.followed_user_type = #{relationIdentityUserType} and fans_me.followed_ref_id = #{relationIdentityRefId}" +
+            "</if>" +
             "left join life_blacklist lb on lb.blocked_type = foll.blockedType and lb.blocked_id = foll.blockedId and lb.blocker_type = #{blockerType} and lb.blocker_id = #{blockerId} and lb.delete_flag = 0 " +
             "${ew.customSqlSegment} " +
             "</script>")
-    IPage<LifeFansVo> getMyFans(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("relationFansId") String relationFansId, @Param("blockerType") String blockerType, @Param("blockerId") String blockerId, @Param("onlyStoreFans") boolean onlyStoreFans, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+    IPage<LifeFansVo> getMyFans(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("identityUserType") Integer identityUserType, @Param("identityRefId") Integer identityRefId, @Param("relationFansId") String relationFansId, @Param("relationIdentityUserType") Integer relationIdentityUserType, @Param("relationIdentityRefId") Integer relationIdentityRefId, @Param("blockerType") String blockerType, @Param("blockerId") String blockerId, @Param("onlyStoreFans") boolean onlyStoreFans, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
 
-//    @Select("select foll.*, if(isnull(fans.id), 0, 1) isFollowThis, 1 as isFollowMe, count(fans2.id) fansNum, count(fans3.id) followNum from ( " +
-//            "    with follow as ( " +
-//            "    select substring_index(fans_id, '_', 1) as flag, substring_index(fans_id, '_', -1) as phone " +
-//            "    from life_fans " +
-//            "    where delete_flag = 0 and followed_id = #{fansId} " +
-//            "    ) " +
-//            "    select info.id," +
-//            "    \"CASE \" +\n" +
-//            "        \"WHEN user.store_id IS NULL OR info.store_application_status = 0 THEN user.nick_name \" +\n" +
-//            "        \"ELSE info.store_name \" +\n" +
-//            "        \"END AS name, \" +\n" +
-//            "        \"CASE \" +\n" +
-//            "        \"WHEN user.store_id IS NULL OR info.store_application_status IN (0, 2) THEN user.account_blurb \" +\n" +
-//            "        \"ELSE info.store_blurb \" +\n" +
-//            "        \"END AS store_blurb, \" + " +
-//            "    user.head_img image, concat('store_', user.phone) phoneId" +
-//            "    from follow foll " +
-//            "    join store_user user on foll.phone = user.phone " +
-//            "    LEFT JOIN store_info info ON user.store_id IS NOT NULL AND info.id = user.store_id " +
-//            "    and info.delete_flag = 0" +
-//            "    left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
-//            "    where foll.flag = 'store' and user.delete_flag = 0 " +
-//            ") foll " +
-//            "left join life_fans fans on fans.followed_id = foll.phoneId and fans.fans_id = #{fansId} and fans.delete_flag = 0 " +
-//            "left join life_fans fans2 on fans2.followed_id = foll.phoneId and fans2.delete_flag = 0 " +
-//            "left join life_fans fans3 on fans3.fans_id = foll.phoneId and fans3.delete_flag = 0 " +
-//            "${ew.customSqlSegment} ")
-    @Select("SELECT " +
+    @Select("<script>" +
+            "SELECT " +
             "MAX(foll.id) id, " +
             "MAX(foll.name) name, " +
             "MAX(foll.blurb) blurb, " +
@@ -142,14 +142,12 @@ public interface LifeFansMapper extends BaseMapper<LifeFans> {
             "FROM " +
             "( " +
             "WITH follow AS ( " +
-            "SELECT " +
-            "substring_index(fans_id, '_', 1) AS flag, " +
-            "substring_index(fans_id, '_', -1) AS phone " +
-            "FROM " +
-            "life_fans " +
-            "WHERE " +
-            "delete_flag = 0 " +
-            "AND followed_id = #{fansId} " +
+            "SELECT fans_user_type AS fansUserType, fans_ref_id AS fansRefId " +
+            "FROM life_fans " +
+            "WHERE delete_flag = 0 AND fans_user_type = 2 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "AND followed_user_type = #{identityUserType} AND followed_ref_id = #{identityRefId} " +
+            "</if>" +
             ") " +
             "SELECT " +
             "info.id, " +
@@ -166,126 +164,162 @@ public interface LifeFansMapper extends BaseMapper<LifeFans> {
             "CASE " +
             "WHEN user.store_id IS NOT NULL AND info.store_application_status = 1 THEN '1' " +
             "ELSE '0' " +
-            "END AS isMerchant " +
-            "FROM " +
-            "follow foll " +
-            "JOIN store_user user ON foll.phone = user.phone " +
+            "END AS isMerchant, " +
+            "2 AS fansUserType, user.id AS fansRefId " +
+            "FROM follow foll " +
+            "JOIN store_user user ON foll.fansUserType = 2 AND user.id = foll.fansRefId " +
             "LEFT JOIN store_info info ON user.store_id IS NOT NULL " +
             "AND info.id = user.store_id " +
             "AND info.delete_flag = 0 " +
             "LEFT JOIN store_img img ON img.store_id = user.store_id " +
             "AND img.img_type = '10' " +
             "AND img.delete_flag = 0 " +
-            "WHERE " +
-            "foll.flag = 'store' " +
-            "AND user.delete_flag = 0 " +
+            "WHERE user.delete_flag = 0 " +
             ") foll " +
-            "LEFT JOIN life_fans fans ON fans.followed_id = foll.phoneId " +
-            "AND fans.fans_id = #{fansId} " +
+            "LEFT JOIN life_fans fans ON fans.followed_user_type = foll.fansUserType AND fans.followed_ref_id = foll.fansRefId " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "AND fans.fans_user_type = #{identityUserType} AND fans.fans_ref_id = #{identityRefId} " +
+            "</if>" +
             "AND fans.delete_flag = 0 " +
-            "LEFT JOIN life_fans fans2 ON fans2.followed_id = foll.phoneId " +
+            "LEFT JOIN life_fans fans2 ON fans2.followed_user_type = foll.fansUserType AND fans2.followed_ref_id = foll.fansRefId " +
             "AND fans2.delete_flag = 0 " +
-            "LEFT JOIN life_fans fans3 ON fans3.fans_id = foll.phoneId " +
+            "LEFT JOIN life_fans fans3 ON fans3.fans_user_type = foll.fansUserType AND fans3.fans_ref_id = foll.fansRefId " +
             "AND fans3.delete_flag = 0 " +
-            "${ew.customSqlSegment}")
-    IPage<LifeFansVo> getMyStoreFans(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+            "${ew.customSqlSegment}" +
+            "</script>")
+    IPage<LifeFansVo> getMyStoreFans(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("identityUserType") Integer identityUserType, @Param("identityRefId") Integer identityRefId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
 
-    @Select("select MAX(foll.id) id, MAX(foll.name) name, MAX(foll.image) image, foll.phoneId, MAX(foll.blurb) blurb, " +
+    @Select("<script>" +
+            "select MAX(foll.id) id, MAX(foll.name) name, MAX(foll.image) image, foll.phoneId, MAX(foll.blurb) blurb, " +
             "  MAX(if(isnull(fans.id), 0, 1)) isFollowThis, 1 as isFollowMe, " +
-            "       (select count(1) from life_fans fans2 where fans2.followed_id = foll.phoneId and fans2.delete_flag = 0) fansNum, " +
-            "       (select count(1) from life_fans fans3 where fans3.fans_id = foll.phoneId and fans3.delete_flag = 0) followNum " +
+            "       (select count(1) from life_fans fans2 where fans2.followed_user_type = foll.fansUserType and fans2.followed_ref_id = foll.fansRefId and fans2.delete_flag = 0) fansNum, " +
+            "       (select count(1) from life_fans fans3 where fans3.fans_user_type = foll.fansUserType and fans3.fans_ref_id = foll.fansRefId and fans3.delete_flag = 0) followNum " +
             " from ( " +
             "    with follow as ( " +
-            "    select substring_index(fans_id, '_', 1) as flag, substring_index(fans_id, '_', -1) as phone " +
+            "    select fans_user_type as fansUserType, fans_ref_id as fansRefId " +
             "    from life_fans " +
-            "    where delete_flag = 0 and followed_id = #{fansId} " +
+            "    where delete_flag = 0 and fans_user_type = 1 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "    and followed_user_type = #{identityUserType} and followed_ref_id = #{identityRefId} " +
+            "</if>" +
             "    ) " +
-            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 1 as fansUserType, user.id as fansRefId " +
             "    from follow foll " +
-            "    join life_user user on foll.phone = user.user_phone " +
-            "    where foll.flag = 'user' and user.delete_flag = 0 " +
+            "    join life_user user on foll.fansUserType = 1 and user.id = foll.fansRefId " +
+            "    where user.delete_flag = 0 " +
             ") foll " +
-            "left join life_fans fans on fans.followed_id = foll.phoneId and fans.fans_id = #{fansId} and fans.delete_flag = 0 " +
-            "${ew.customSqlSegment} ")
-    IPage<LifeFansVo> getMyUserFans(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+            "left join life_fans fans on fans.followed_user_type = foll.fansUserType and fans.followed_ref_id = foll.fansRefId and fans.delete_flag = 0 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            " and fans.fans_user_type = #{identityUserType} and fans.fans_ref_id = #{identityRefId}" +
+            "</if>" +
+            "${ew.customSqlSegment} " +
+            "</script>")
+    IPage<LifeFansVo> getMyUserFans(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("identityUserType") Integer identityUserType, @Param("identityRefId") Integer identityRefId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
 
-    @Select("select MAX(foll.id) id, MAX(foll.name) name, MAX(foll.image) image, foll.phoneId, MAX(foll.blurb) blurb, MAX(foll.blockedType) blockedType, MAX(foll.blockedId) blockedId, MAX(foll.username) username, MAX(foll.accountBlurb) accountBlurb, MAX(foll.isMerchant) isMerchant, MAX(foll.created_time) created_time, " +
+    @Select("<script>" +
+            "select MAX(foll.id) id, MAX(foll.name) name, MAX(foll.image) image, foll.phoneId, MAX(foll.blurb) blurb, MAX(foll.blockedType) blockedType, MAX(foll.blockedId) blockedId, MAX(foll.username) username, MAX(foll.accountBlurb) accountBlurb, MAX(foll.isMerchant) isMerchant, MAX(foll.created_time) created_time, " +
             "  MAX(lb.id) blackListid, 1 as isFollowThis, 1 as isFollowMe, " +
-            "       (select count(1) from life_fans fans2 where fans2.followed_id = foll.phoneId and fans2.delete_flag = 0) fansNum, " +
-            "       (select count(1) from life_fans fans3 where fans3.fans_id = foll.phoneId and fans3.delete_flag = 0) followNum " +
+            "       (select count(1) from life_fans fans2 where fans2.followed_user_type = foll.followedUserType and fans2.followed_ref_id = foll.followedRefId and fans2.delete_flag = 0) fansNum, " +
+            "       (select count(1) from life_fans fans3 where fans3.fans_user_type = foll.followedUserType and fans3.fans_ref_id = foll.followedRefId and fans3.delete_flag = 0) followNum " +
             "from ( " +
             "    with follow as ( " +
-            "        select substring_index(fans1.followed_id, '_', 1) as flag, substring_index(fans1.followed_id, '_', -1) as phone, " +
+            "        select fans1.followed_user_type as followedUserType, fans1.followed_ref_id as followedRefId, " +
             "               GREATEST(fans1.created_time, fans2.created_time) as created_time " +
             "        from life_fans fans1 " +
-            "        join life_fans fans2 on fans1.followed_id = fans2.fans_id and fans1.fans_id = fans2.followed_id " +
-            "        where fans1.delete_flag = 0 and fans2.delete_flag = 0 and fans1.fans_id = #{fansId} " +
+            "        join life_fans fans2 on fans1.followed_user_type = fans2.fans_user_type and fans1.followed_ref_id = fans2.fans_ref_id " +
+            "            and fans1.fans_user_type = fans2.followed_user_type and fans1.fans_ref_id = fans2.followed_ref_id " +
+            "        where fans1.delete_flag = 0 and fans2.delete_flag = 0 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "        and fans1.fans_user_type = #{identityUserType} and fans1.fans_ref_id = #{identityRefId} " +
+            "</if>" +
             "    ) " +
-            "    select info.id, IF(info.store_application_status = 0, user.nick_name, info.store_name) AS name, user.head_img image, concat('store_', user.phone) phoneId, IF(info.store_application_status = 0, user.account_blurb, info.store_blurb) AS blurb, 1 blockedType,user.id blockedId ,IFNULL(user.nick_name, user.name) username, user.account_blurb accountBlurb, IF(info.store_application_status = 1, '1', '0') AS isMerchant, foll.created_time " +
+            "    select info.id, IF(info.store_application_status = 0, user.nick_name, info.store_name) AS name, user.head_img image, concat('store_', user.phone) phoneId, IF(info.store_application_status = 0, user.account_blurb, info.store_blurb) AS blurb, 1 blockedType,user.id blockedId ,IFNULL(user.nick_name, user.name) username, user.account_blurb accountBlurb, IF(info.store_application_status = 1, '1', '0') AS isMerchant, foll.created_time, 2 as followedUserType, user.id as followedRefId " +
             "    from follow foll " +
-            "    join store_user user on foll.phone = user.phone " +
+            "    join store_user user on foll.followedUserType = 2 and user.id = foll.followedRefId " +
             "    join store_info info on info.id = user.store_id " +
             "    left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
-            "    where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0 " +
+            "    where user.delete_flag = 0 and info.delete_flag = 0 " +
             "    union " +
-            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 2 blockedType,user.id blockedId,'' username, '' accountBlurb, '0' AS isMerchant, foll.created_time " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 2 blockedType,user.id blockedId,'' username, '' accountBlurb, '0' AS isMerchant, foll.created_time, 1 as followedUserType, user.id as followedRefId " +
             "    from follow foll " +
-            "    join life_user user on foll.phone = user.user_phone " +
-            "    where foll.flag = 'user' and user.delete_flag = 0 " +
+            "    join life_user user on foll.followedUserType = 1 and user.id = foll.followedRefId " +
+            "    where user.delete_flag = 0 " +
             ") foll " +
             "left join life_blacklist lb on lb.blocked_type = foll.blockedType and lb.blocked_id = foll.blockedId and lb.blocker_type = #{blockerType} and lb.blocker_id = #{blockerId} and lb.delete_flag = 0 " +
-            "${ew.customSqlSegment} ")
-    IPage<LifeFansVo> getMutualAttention(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("blockerType") String blockerType, @Param("blockerId") String blockerId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+            "${ew.customSqlSegment} " +
+            "</script>")
+    IPage<LifeFansVo> getMutualAttention(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("identityUserType") Integer identityUserType, @Param("identityRefId") Integer identityRefId, @Param("blockerType") String blockerType, @Param("blockerId") String blockerId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
 
-    @Select("select MAX(foll.id) id, MAX(foll.name) name, MAX(foll.image) image, foll.phoneId, MAX(foll.blurb) blurb, 1 as isFollowThis, 1 as isFollowMe, " +
-            "       (select count(1) from life_fans fans2 where fans2.followed_id = foll.phoneId and fans2.delete_flag = 0) fansNum, " +
-            "       (select count(1) from life_fans fans3 where fans3.fans_id = foll.phoneId and fans3.delete_flag = 0) followNum " +
+    @Select("<script>" +
+            "select MAX(foll.id) id, MAX(foll.name) name, MAX(foll.image) image, foll.phoneId, MAX(foll.blurb) blurb, 1 as isFollowThis, 1 as isFollowMe, " +
+            "       (select count(1) from life_fans fans2 where fans2.followed_user_type = foll.followedUserType and fans2.followed_ref_id = foll.followedRefId and fans2.delete_flag = 0) fansNum, " +
+            "       (select count(1) from life_fans fans3 where fans3.fans_user_type = foll.followedUserType and fans3.fans_ref_id = foll.followedRefId and fans3.delete_flag = 0) followNum " +
             "from ( " +
             "    with follow as ( " +
-            "        select substring_index(fans1.followed_id, '_', 1) as flag, substring_index(fans1.followed_id, '_', -1) as phone " +
+            "        select fans1.followed_user_type as followedUserType, fans1.followed_ref_id as followedRefId " +
             "        from life_fans fans1 " +
-            "        join life_fans fans2 on fans1.followed_id = fans2.fans_id and fans1.fans_id = fans2.followed_id " +
-            "        where fans1.delete_flag = 0 and fans2.delete_flag = 0 and fans1.fans_id = #{fansId} " +
+            "        join life_fans fans2 on fans1.followed_user_type = fans2.fans_user_type and fans1.followed_ref_id = fans2.fans_ref_id " +
+            "            and fans1.fans_user_type = fans2.followed_user_type and fans1.fans_ref_id = fans2.followed_ref_id " +
+            "        where fans1.delete_flag = 0 and fans2.delete_flag = 0 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "        and fans1.fans_user_type = #{identityUserType} and fans1.fans_ref_id = #{identityRefId} " +
+            "</if>" +
             "    ) " +
-            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 1 as followedUserType, user.id as followedRefId " +
             "    from follow foll " +
-            "    join life_user user on foll.phone = user.user_phone " +
-            "    where foll.flag = 'user' and user.delete_flag = 0 " +
+            "    join life_user user on foll.followedUserType = 1 and user.id = foll.followedRefId " +
+            "    where user.delete_flag = 0 " +
             ") foll " +
-            "${ew.customSqlSegment} ")
-    IPage<LifeFansVo> getMutualAttentionUser(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+            "${ew.customSqlSegment} " +
+            "</script>")
+    IPage<LifeFansVo> getMutualAttentionUser(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("identityUserType") Integer identityUserType, @Param("identityRefId") Integer identityRefId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
 
-    @Select("select (select count(1) from life_fans where fans_id = #{phoneId} and delete_flag = 0 and followed_id not REGEXP '_$') followNum, " +
-            "(select count(1) from life_fans where followed_id= #{phoneId} and delete_flag = 0 and fans_id not REGEXP '_$') fansNum, " +
+    @Select("<script>" +
+            "select " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "(select count(1) from life_fans where delete_flag = 0 and fans_user_type = #{identityUserType} and fans_ref_id = #{identityRefId}) followNum, " +
+            "(select count(1) from life_fans where delete_flag = 0 and followed_user_type = #{identityUserType} and followed_ref_id = #{identityRefId}) fansNum, " +
+            "</if>" +
+            "<if test=\"identityUserType == null or identityRefId == null\">" +
+            "0 followNum, 0 fansNum, " +
+            "</if>" +
             "(select count(1) from ( " +
             "        select foll.*, 1 as isFollowThis, 1 as isFollowMe, " +
-            "               (select count(1) from life_fans fans2 where fans2.followed_id = foll.phoneId and fans2.delete_flag = 0) fansNum, " +
-            "               (select count(1) from life_fans fans3 where fans3.fans_id = foll.phoneId and fans3.delete_flag = 0) followNum " +
+            "               (select count(1) from life_fans fans2 where fans2.followed_user_type = foll.followedUserType and fans2.followed_ref_id = foll.followedRefId and fans2.delete_flag = 0) fansNum, " +
+            "               (select count(1) from life_fans fans3 where fans3.fans_user_type = foll.followedUserType and fans3.fans_ref_id = foll.followedRefId and fans3.delete_flag = 0) followNum " +
             "        from ( " +
             "            with follow as ( " +
-            "                select substring_index(fans1.followed_id, '_', 1) as flag, substring_index(fans1.followed_id, '_', -1) as phone " +
+            "                select fans1.followed_user_type as followedUserType, fans1.followed_ref_id as followedRefId " +
             "                from life_fans fans1 " +
-            "                join life_fans fans2 on fans1.followed_id = fans2.fans_id and fans1.fans_id = fans2.followed_id " +
-            "                where fans1.delete_flag = 0 and fans2.delete_flag = 0 and fans1.fans_id = #{phoneId} " +
+            "                join life_fans fans2 on fans1.followed_user_type = fans2.fans_user_type and fans1.followed_ref_id = fans2.fans_ref_id " +
+            "                    and fans1.fans_user_type = fans2.followed_user_type and fans1.fans_ref_id = fans2.followed_ref_id " +
+            "                where fans1.delete_flag = 0 and fans2.delete_flag = 0 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "                and fans1.fans_user_type = #{identityUserType} and fans1.fans_ref_id = #{identityRefId} " +
+            "</if>" +
             "            ) " +
-            "            select info.id, info.store_name name, img.img_url image, concat('store_', user.phone) phoneId " +
+            "            select info.id, info.store_name name, img.img_url image, concat('store_', user.phone) phoneId, 2 as followedUserType, user.id as followedRefId " +
             "            from follow foll " +
-            "            join store_user user on foll.phone = user.phone " +
+            "            join store_user user on foll.followedUserType = 2 and user.id = foll.followedRefId " +
             "            join store_info info on info.id = user.store_id " +
             "            left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
-            "            where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0 " +
+            "            where user.delete_flag = 0 and info.delete_flag = 0 " +
             "            union " +
-            "            select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId " +
+            "            select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, 1 as followedUserType, user.id as followedRefId " +
             "            from follow foll " +
-            "            join life_user user on foll.phone = user.user_phone " +
-            "            where foll.flag = 'user' and user.delete_flag = 0 " +
+            "            join life_user user on foll.followedUserType = 1 and user.id = foll.followedRefId " +
+            "            where user.delete_flag = 0 " +
             "        ) foll " +
             "    ) a " +
             ") friendNum, " +
             "( " +
             "    select count(id) " +
             "    from life_user_dynamics " +
-            "    where delete_flag = 0 and phone_id = #{phoneId} and draft = 0 and enable_status != 1 " +
-            ") dynamicsNum")
-    LifeFansVo getHomePageInfo(@Param("phoneId") String phoneId);
-}
+            "    where delete_flag = 0 and draft = 0 and enable_status != 1 " +
+            "<if test=\"identityUserType != null and identityRefId != null\">" +
+            "    and phone_user_type = #{identityUserType} and phone_ref_id = #{identityRefId} " +
+            "</if>" +
+            ") dynamicsNum" +
+            "</script>")
+    LifeFansVo getHomePageInfo(@Param("phoneId") String phoneId, @Param("identityUserType") Integer identityUserType, @Param("identityRefId") Integer identityRefId);
+}

+ 43 - 16
alien-entity/src/main/java/shop/alien/mapper/LifeMessageMapper.java

@@ -32,10 +32,14 @@ public interface LifeMessageMapper extends BaseMapper<LifeMessage> {
 
     @Select("with message_num as ( " +
             "    with message as ( " +
-            "        select id, type, content, created_time, if(sender_id = #{phoneId}, receiver_id, sender_id) phoneId, sender_id, receiver_id, is_read " +
+            "        select id, type, content, created_time, " +
+            "               if(sender_user_type = #{participantUserType} and sender_ref_id = #{participantRefId}, receiver_id, sender_id) phoneId, " +
+            "               sender_id, receiver_id, is_read " +
             "        from life_message " +
-            "        where delete_flag = 0 and (sender_id = #{phoneId} or receiver_id = #{phoneId}) " +
-            "               and (instr(delete_phone_id, #{phoneId}) is null or instr(delete_phone_id, #{phoneId}) = 0)" +
+            "        where delete_flag = 0 " +
+            "          and ((sender_user_type = #{participantUserType} and sender_ref_id = #{participantRefId}) " +
+            "               or (receiver_user_type = #{participantUserType} and receiver_ref_id = #{participantRefId})) " +
+            "          and (instr(delete_phone_id, #{phoneId}) is null or instr(delete_phone_id, #{phoneId}) = 0) " +
             "    ) " +
             "    select *, row_number() over (partition by phoneId order by created_time desc ) num, " +
             "           substring_index(phoneId, '_', 1) flag, substring_index(phoneId, '_', -1) phone " +
@@ -67,37 +71,60 @@ public interface LifeMessageMapper extends BaseMapper<LifeMessage> {
             "left join lawyer_user luser on message.flag = 'lawyer' and message.phone = luser.phone and luser.delete_flag = 0 "+
             "left join store_img img on img.store_id = suser.store_id and img.img_type = '10' and img.delete_flag = 0 " +
             "${ew.customSqlSegment}")
-    List<LifeMessageVo> getLifeMessagePageByPhoneId(@Param("phoneId") String phoneId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> dynamicsWrapper);
+    List<LifeMessageVo> getLifeMessagePageByPhoneId(@Param("phoneId") String phoneId,
+                                                    @Param("participantUserType") Integer participantUserType,
+                                                    @Param("participantRefId") Integer participantRefId,
+                                                    @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> dynamicsWrapper);
 
     @Update("update life_message set delete_phone_id = (if(delete_phone_id is null, #{receiverId}, concat(delete_phone_id, ',', #{receiverId}))) " +
-            "where ((receiver_id = #{senderId} and sender_id = #{receiverId}) or (sender_id = #{senderId} and receiver_id = #{receiverId})) " +
+            "where ((receiver_user_type = #{receiverUserType} and receiver_ref_id = #{receiverRefId} " +
+            "        and sender_user_type = #{senderUserType} and sender_ref_id = #{senderRefId}) " +
+            "    or (sender_user_type = #{receiverUserType} and sender_ref_id = #{receiverRefId} " +
+            "        and receiver_user_type = #{senderUserType} and receiver_ref_id = #{senderRefId})) " +
             "   and (instr(delete_phone_id, #{receiverId}) is null or instr(delete_phone_id, #{receiverId}) = 0)")
-    int deleteMessageByPhoneId(@Param("senderId") String senderId, @Param("receiverId") String receiverId);
+    int deleteMessageByPhoneId(@Param("senderUserType") Integer senderUserType,
+                               @Param("senderRefId") Integer senderRefId,
+                               @Param("receiverUserType") Integer receiverUserType,
+                               @Param("receiverRefId") Integer receiverRefId,
+                               @Param("senderId") String senderId,
+                               @Param("receiverId") String receiverId);
 
     @Select("select lm.id,lm.updated_time,lm.created_user_id,lm.receiver_name,lm.is_read,lm.type,lm.content,lm.current_time,lm.delete_flag," +
             "       lm.sender_id,lm.sender_name,lm.receiver_id,lm.delete_phone_id,lm.created_time,lm.updated_user_id,lu.user_image " +
             "from life_message lm " +
             "left join life_user lu on SUBSTRING(lm.sender_id, INSTR(lm.sender_id, '_') + 1) = lu.user_phone " +
             "where lm.delete_flag = 0 " +
-            "   and (((lm.receiver_id = #{senderId} and lm.sender_id = #{receiverId}) or (lm.sender_id = #{senderId} and lm.receiver_id = #{receiverId}))) " +
-            "   and (instr(delete_phone_id, #{receiverId}) is null or instr(delete_phone_id, #{receiverId}) = 0) " +
+            "   and (((lm.receiver_user_type = #{receiverUserType} and lm.receiver_ref_id = #{receiverRefId} " +
+            "         and lm.sender_user_type = #{senderUserType} and lm.sender_ref_id = #{senderRefId}) " +
+            "      or (lm.sender_user_type = #{receiverUserType} and lm.sender_ref_id = #{receiverRefId} " +
+            "         and lm.receiver_user_type = #{senderUserType} and lm.receiver_ref_id = #{senderRefId}))) " +
+            "   and (instr(delete_phone_id, #{receiverPhoneId}) is null or instr(delete_phone_id, #{receiverPhoneId}) = 0) " +
             "order by lm.created_time asc ")
-    List<LifeMessageVo> getMessageListByReceiverId(@Param("receiverId") String receiverId, @Param("senderId") String senderId);
+    List<LifeMessageVo> getMessageListByReceiverId(@Param("receiverPhoneId") String receiverPhoneId,
+                                                   @Param("senderUserType") Integer senderUserType,
+                                                   @Param("senderRefId") Integer senderRefId,
+                                                   @Param("receiverUserType") Integer receiverUserType,
+                                                   @Param("receiverRefId") Integer receiverRefId);
 
     @Select("select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb " +
             "from life_user user " +
             "where user_phone in ( " +
             "    select substring_index(sender_id, '_', -1) phone from life_message " +
-            "    where sender_id in " +
-            "          (select receiver_id from life_message " +
-            "            where sender_id = #{senderId} and receiver_id != #{senderId} and delete_flag = 0 " +
-            "            and left(receiver_id, 5) = 'user_' and receiver_id not in ( " +
+            "    where sender_id in ( " +
+            "          select receiver_id from life_message " +
+            "            where sender_user_type = #{senderUserType} and sender_ref_id = #{senderRefId} " +
+            "            and delete_flag = 0 " +
+            "            and not (receiver_user_type = #{senderUserType} and receiver_ref_id = #{senderRefId}) " +
+            "            and receiver_user_type = 1 " +
+            "            and receiver_id not in ( " +
             "                    select blocked_phone_id from life_blacklist " +
             "                    where blocker_phone_id = #{senderId} and delete_flag = 0 " +
             "                ) " +
             "            group by receiver_id) " +
-            "    and receiver_id = #{senderId} and delete_flag = 0 " +
+            "    and receiver_user_type = #{senderUserType} and receiver_ref_id = #{senderRefId} and delete_flag = 0 " +
             "    group by sender_id) " +
             "and delete_flag = 0 ")
-    List<LifeFansVo> getTalkedUserList(@Param("senderId") String senderId);
-}
+    List<LifeFansVo> getTalkedUserList(@Param("senderId") String senderId,
+                                       @Param("senderUserType") Integer senderUserType,
+                                       @Param("senderRefId") Integer senderRefId);
+}

+ 6 - 3
alien-entity/src/main/java/shop/alien/mapper/LifeNoticeMapper.java

@@ -10,7 +10,10 @@ import shop.alien.entity.store.LifeNotice;
 public interface LifeNoticeMapper extends BaseMapper<LifeNotice> {
 
     @Select("select count(id) noReadNum from life_notice " +
-            "where delete_flag = 0 and is_read = 0 and receiver_id = #{receiverId} ")
-    int selectNoReadCount(@Param("receiverId") String receiverId);
+            "where delete_flag = 0 and is_read = 0 " +
+            "and receiver_user_type = #{receiverUserType} " +
+            "and receiver_ref_id = #{receiverRefId} ")
+    int selectNoReadCount(@Param("receiverUserType") Integer receiverUserType,
+                          @Param("receiverRefId") Integer receiverRefId);
 
-}
+}

+ 85 - 52
alien-entity/src/main/java/shop/alien/mapper/LifeUserDynamicsMapper.java

@@ -16,88 +16,121 @@ import java.util.List;
 public interface LifeUserDynamicsMapper extends BaseMapper<LifeUserDynamics> {
 
     @Select("select dyna1.* from (with dynamice as( " +
-            "select lud.id, lud.top_status, lud.top_time, lud.title, lud.phone_id phoneId, " +
+            "select lud.id, lud.top_status, lud.top_time, lud.title, " +
+            "lud.phone_user_type phoneUserType, lud.phone_ref_id phoneRefId, " +
+            "case when lud.phone_user_type = 1 then concat('user_', (select user_phone from life_user where id = lud.phone_ref_id and delete_flag = 0 limit 1)) " +
+            "when lud.phone_user_type = 2 then concat('store_', (select phone from store_user where id = lud.phone_ref_id and delete_flag = 0 limit 1)) " +
+            "when lud.phone_user_type = 3 then concat('lawyer_', (select phone from lawyer_user where id = lud.phone_ref_id and delete_flag = 0 limit 1)) " +
+            "end phoneId, " +
             "lud.context, lud.image_path, lud.address, lud.address_name, lud.address_context, " +
-            "lud.liulan_count, lud.dianzan_count, lud.type, lud.created_time, substring_index(lud.phone_id, '_', 1) flag, " +
-            "substring_index(lud.phone_id, '_', -1) phone, lud.draft , lud.address_province, lud.transfer_count, lud.cover_image " +
+            "lud.liulan_count, lud.dianzan_count, lud.type, lud.created_time, " +
+            "case when lud.phone_user_type = 1 then 'user' when lud.phone_user_type = 2 then 'store' when lud.phone_user_type = 3 then 'lawyer' end flag, " +
+            "case when lud.phone_user_type = 1 then (select user_phone from life_user where id = lud.phone_ref_id and delete_flag = 0 limit 1) " +
+            "when lud.phone_user_type = 2 then (select phone from store_user where id = lud.phone_ref_id and delete_flag = 0 limit 1) " +
+            "when lud.phone_user_type = 3 then (select phone from lawyer_user where id = lud.phone_ref_id and delete_flag = 0 limit 1) end phone, " +
+            "lud.draft , lud.address_province, lud.transfer_count, lud.cover_image " +
             "from life_user_dynamics lud " +
-            "where lud.delete_flag = 0 and lud.enable_status = 0 and lud.draft = 0 and " +
+            "where lud.delete_flag = 0 and lud.enable_status = 0 and lud.draft = 0 " +
+            "and lud.phone_user_type is not null and lud.phone_ref_id is not null and " +
             "not exists (select 1 from life_user_violation luv where luv.delete_flag = 0 and luv.processing_status = 1 " +
             "AND luv.dynamics_id = lud.id) order by lud.created_time desc) " +
             "select dynamice.*, info.store_name userName, user.head_img userImage, info.id storeUserId, user.id storeOrUserId, 0 isExpert, info.score_avg scoreAvg, info.business_section businessSection, info.business_type_name businessTypeName " +
             "from dynamice " +
-            "join store_user user on dynamice.phone = user.phone and user.delete_flag = 0 " +
+            "join store_user user on dynamice.phoneUserType = 2 and user.id = dynamice.phoneRefId and user.delete_flag = 0 " +
             "and user.status = 0 and user.logout_flag = 0 " +
             "join store_info info on info.id = user.store_id and info.delete_flag = 0 " +
             "and info.store_status = 1 " +
             "left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0  " +
-            "where dynamice.flag = 'store' " +
+            "where dynamice.phoneUserType = 2 " +
             "union " +
             "select dynamice.*, user.user_name userName, user.user_image userImage, user.id storeUserId, user.id storeOrUserId, " +
             "IF(lue.expert_code IS NOT NULL , 1, 0) AS isExpert, 0 scoreAvg, 0 businessSection, 0 businessTypeName " +
             "from dynamice " +
-            "join life_user user on dynamice.phone = user.user_phone and user.delete_flag = 0 " +
+            "join life_user user on dynamice.phoneUserType = 1 and user.id = dynamice.phoneRefId and user.delete_flag = 0 " +
             "and user.logout_flag = 0 " +
             "left join life_user_expert  lue on lue.user_id = user.id and lue.delete_flag = 0 " +
-            "where dynamice.flag = 'user') dyna1 order by dyna1.top_status desc, dyna1.top_time desc, created_time desc")
+            "where dynamice.phoneUserType = 1) dyna1 order by dyna1.top_status desc, dyna1.top_time desc, created_time desc")
     List<LifeUserDynamicsVo> getLifeUserDynamicsList();
 
-    @Select("select lud.id ,lud.image_path,lud.context,lu.user_image,lu.user_name,IF(llr.huifu_id IS NOT NULL, 1, 0) AS isLike\n" +
+    @Select("select lud.id ,lud.image_path,lud.context, " +
+            "case when lud.phone_user_type = 2 then su.head_img else lu.user_image end user_image, " +
+            "case when lud.phone_user_type = 2 then su.nick_name else lu.user_name end user_name, " +
+            "IF(llr.huifu_id IS NOT NULL, 1, 0) AS isLike\n" +
             "from life_user_dynamics lud\n" +
-            "left join life_user lu \n" +
-            "on lud.phone_id = CONCAT('store_', lu.user_phone )\n" +
+            "left join store_user su on lud.phone_user_type = 2 and su.id = lud.phone_ref_id and su.delete_flag = 0\n" +
+            "left join life_user lu on lud.phone_user_type = 1 and lu.id = lud.phone_ref_id and lu.delete_flag = 0\n" +
             "left join life_like_record llr\n" +
             "on llr.huifu_id = lud.id\n" +
-            "and lu.user_phone = (select CONCAT('user_',lu1.user_phone) from life_user lu1 where lu1.id = #{userId})" +
+            "and llr.delete_flag = 0\n" +
+            "and llr.dianzan_user_type = 1 and llr.dianzan_ref_id = #{userId}" +
             "${ew.customSqlSegment}")
     List<LifeUserDynamicsVo> getStoreDynamicslistWithWrapper(@Param("userId") String userId, @Param(Constants.WRAPPER) QueryWrapper<LifeUserDynamics> dynamicsWrapper);
 
-    @Select("with middle_lud as (\n" +
-            "  select \n" +
-            "    lud.*,\n" +
-            "    '1' as isLike,\n" +
-            "    llr_sub.like_created_time\n" +
-            "  from life_user_dynamics lud\n" +
-            "  inner join (\n" +
-            "    select huifu_id, max(created_time) as like_created_time\n" +
-            "    from life_like_record llr\n" +
-            "    where llr.dianzan_id = #{phoneId}\n" +
-            "      and llr.delete_flag = 0\n" +
-            "    group by huifu_id\n" +
-            "  ) llr_sub on lud.id = llr_sub.huifu_id\n" +
-            "  where lud.delete_flag = 0\n" +
-            "    and not exists (  -- 替代NOT IN,避免NULL问题\n" +
-            "      select 1 from life_blacklist lb\n" +
-            "      where lb.blocker_phone_id = #{phoneId}\n" +
-            "        and lb.delete_flag = 0\n" +
-            "        and lb.blocked_phone_id = lud.phone_id\n" +
-            "    )\n" +
+    @Select("WITH middle_lud AS (\n" +
+            "    SELECT \n" +
+            "        lud.*,\n" +
+            "        '1' AS isLike,\n" +
+            "        llr_sub.like_created_time\n" +
+            "    FROM life_user_dynamics lud\n" +
+            "    INNER JOIN (\n" +
+            "        SELECT \n" +
+            "            huifu_id, \n" +
+            "            MAX(created_time) AS like_created_time\n" +
+            "        FROM life_like_record llr\n" +
+            "        WHERE \n" +
+            "        llr.dianzan_user_type = #{viewerUserType}\n" +
+            "        and llr.dianzan_ref_id = #{viewerRefId}\n" +
+            "            AND llr.delete_flag = 0\n" +
+            "        GROUP BY huifu_id\n" +
+            "    ) llr_sub ON lud.id = llr_sub.huifu_id\n" +
+            "    WHERE lud.delete_flag = 0\n" +
+            "        AND NOT EXISTS (\n" +
+            "            SELECT 1 \n" +
+            "            FROM life_blacklist lb\n" +
+            "            WHERE lb.blocker_id = #{viewerRefId}\n" +
+            "            and lb.blocked_type = #{viewerUserType}\n" +
+            "                AND lb.delete_flag = 0\n" +
+            "                AND  lb.blocker_id = lud.phone_ref_id \n" +
+            "            and lb.blocked_type = lud.phone_user_type \n" +
+            "        )\n" +
             ")\n" +
-            "select middle_lud.*,\n" +
-            "  CASE \n" +
-            "    WHEN lf.id is not null THEN 1\n" +
-            "    ELSE 0                        \n" +
-            "  END AS isFollowThis,  \n" +
-            "  CASE \n" +
-            "    WHEN lf1.id is not null THEN 1\n" +
-            "    ELSE 0                        \n" +
-            "  END AS isFollowMe  \n" +
-            "from middle_lud\n" +
-            "left join life_fans lf on lf.fans_id = #{phoneId} \n" +
-            "                      and lf.followed_id = middle_lud.phone_id\n" +
-            "                      and lf.delete_flag = '0'\n" +
-            "left join life_fans lf1 on lf1.fans_id = middle_lud.phone_id \n" +
-            "                      and lf1.followed_id = #{phoneId}\n" +
-            "                      and lf1.delete_flag = '0'\n" +
-            "order by middle_lud.like_created_time desc")
-    List<LifeUserDynamicsVo> selectDianZanList(String phoneId);
+            "SELECT \n" +
+            "    middle_lud.*,\n" +
+            "    CASE \n" +
+            "        WHEN lf.id IS NOT NULL THEN 1\n" +
+            "        ELSE 0\n" +
+            "    END AS isFollowThis,\n" +
+            "    CASE \n" +
+            "        WHEN lf1.id IS NOT NULL THEN 1\n" +
+            "        ELSE 0\n" +
+            "    END AS isFollowMe\n" +
+            "FROM middle_lud\n" +
+            "LEFT JOIN life_fans lf \n" +
+            "    ON \n" +
+            "    lf.fans_ref_id = #{viewerRefId} and\n" +
+            "    lf.fans_user_type = #{viewerUserType} and\n" +
+            "    lf.followed_ref_id = middle_lud.phone_ref_id and \n" +
+            "    lf.followed_user_type = middle_lud.phone_user_type \n" +
+            "    AND lf.delete_flag = '0'\n" +
+            "LEFT JOIN life_fans lf1 \n" +
+            "    ON \n" +
+            "     lf1.fans_ref_id = middle_lud.phone_ref_id and\n" +
+            "    lf1.fans_user_type = middle_lud.phone_user_type  and\n" +
+            "     lf1.followed_ref_id = #{viewerRefId} and \n" +
+            "    lf1.followed_user_type = #{viewerUserType}\n" +
+            "    AND lf1.delete_flag = '0'\n" +
+            "ORDER BY middle_lud.like_created_time DESC")
+    List<LifeUserDynamicsVo> selectDianZanList(@Param("viewerUserType") Integer viewerUserType,
+                                               @Param("viewerRefId") Integer viewerRefId);
 
     List<LifeUserDynamicsVo> getDynamicsList(@Param("nickName") String nickName, @Param("userType") String userType, @Param("dynamicsType") Integer dynamicsType, @Param("releaseStartTime") String releaseStartTime, @Param("releaseEndTime") String releaseEndTime, @Param("storeName") String storeName);
 
     List<LifeUserDynamicsVo> getDynamicsDetail(@Param("id") Integer id);
 
-    List<LifeUserDynamicsVo> getStoreDynamicslist(@Param("userId") String userId, @Param("phoneId") String phoneId);
+    List<LifeUserDynamicsVo> getStoreDynamicslist(@Param("userId") String userId,
+                                                  @Param("phoneUserType") Integer phoneUserType,
+                                                  @Param("phoneRefId") Integer phoneRefId);
 
     @Select("select * from life_user_dynamics ${ew.customSqlSegment}")
     LifeUserDynamics selectAll(@Param(Constants.WRAPPER) LambdaQueryWrapper<LifeUserDynamics> and);
-}
+}

+ 18 - 18
alien-entity/src/main/java/shop/alien/mapper/LifeUserExpertMapper.java

@@ -25,10 +25,10 @@ import java.util.List;
 public interface LifeUserExpertMapper extends BaseMapper<LifeUserExpert> {
 
     @Select("with fans as ( " +
-            "    select followed_id, count(id) num " +
+            "    select followed_user_type, followed_ref_id, count(id) num " +
             "    from life_fans " +
-            "    where delete_flag = 0 " +
-            "    group by followed_id " +
+            "    where delete_flag = 0 and followed_user_type = 1 " +
+            "    group by followed_user_type, followed_ref_id " +
             "), " +
             "works as ( " +
             "    select expert_id, count(id) worksNum, sum(like_count) likeNum, sum(play_count) playNum " +
@@ -46,7 +46,7 @@ public interface LifeUserExpertMapper extends BaseMapper<LifeUserExpert> {
             "       ifnull(orders.orderMoney, 0) orderMoney, ifnull(orders.accountMoney, 0) accountMoney, (ifnull(orders.orderMoney, 0) - ifnull(orders.accountMoney, 0)) pendMoney " +
             "from life_user_expert expert " +
             "join life_user user on user.id = expert.user_id " +
-            "left join fans on fans.followed_id = concat('user_', user.user_phone)  " +
+            "left join fans on fans.followed_user_type = 1 and fans.followed_ref_id = user.id  " +
             "left join works on works.expert_id = expert.id " +
             "left join orders on orders.expert_id = expert.id " +
             "${ew.customSqlSegment}")
@@ -112,24 +112,24 @@ public interface LifeUserExpertMapper extends BaseMapper<LifeUserExpert> {
 
 
     @Select("with fans as ( " +
-            "    select followed_id, count(id) num " +
+            "    select followed_user_type, followed_ref_id, count(id) num " +
             "    from life_fans " +
-            "    where delete_flag = 0 " +
-            "    group by followed_id " +
+            "    where delete_flag = 0 and followed_user_type = 1 " +
+            "    group by followed_user_type, followed_ref_id " +
             ") " +
             " select expert.id, expert.expert_code, expert.expert_type, user.id AS user_id, user.user_image, user.user_name, user.user_phone, ifnull(fans.num, 0) fansNum " +
             " from life_user_expert expert " +
             " left join life_user user on user.id = expert.user_id " +
-            " left join fans on fans.followed_id = concat('user_', user.user_phone)  " +
+            " left join fans on fans.followed_user_type = 1 and fans.followed_ref_id = user.id  " +
             " where expert.delete_flag = 0 and user.delete_flag = 0 " +
             " ORDER BY fansNum DESC")
     List<LifeUserExpertVo> getRankList();
 
     @Select("with fans as ( " +
-            "    select followed_id, count(id) num " +
+            "    select followed_user_type, followed_ref_id, count(id) num " +
             "    from life_fans " +
-            "    where delete_flag = 0 " +
-            "    group by followed_id " +
+            "    where delete_flag = 0 and followed_user_type = 1 " +
+            "    group by followed_user_type, followed_ref_id " +
             "), " +  // 这里添加逗号分隔不同的 CTE
             "works as ( " +
             "    select expert_id, count(id) worksNum, sum(like_count) likeNum, sum(play_count) playNum " +
@@ -141,7 +141,7 @@ public interface LifeUserExpertMapper extends BaseMapper<LifeUserExpert> {
             "ifnull(fans.num, 0) fansNum " +
             " from life_user_expert expert " +
             " left join life_user user on user.id = expert.user_id " +
-            " left join fans on fans.followed_id = concat('user_', user.user_phone)  " +
+            " left join fans on fans.followed_user_type = 1 and fans.followed_ref_id = user.id  " +
             " left join works on works.expert_id = expert.id " + // 添加与 works 表的连接条件
             " where expert.id = #{expertId}")
     List<LifeUserExpertVo> getExpertDetails(Integer expertId);
@@ -149,7 +149,7 @@ public interface LifeUserExpertMapper extends BaseMapper<LifeUserExpert> {
     @Select("select expert.id, expert.expert_code, user.user_name, user.province, user.city, user.district, user.user_brithday, user.user_sex " +
             " from life_user_expert expert " +
             " left join life_user user on user.id = expert.user_id " +
-            " left join life_fans fans on fans.followed_id = concat('user_', user.user_phone)  " +
+            " left join life_fans fans on fans.followed_user_type = 1 and fans.followed_ref_id = user.id and fans.delete_flag = 0 " +
             "where expert.delete_flag = 0 and user.delete_flag = 0 and fans.delete_flag = 0" +
             " and expert.id = #{expertId}")
     List<LifeUserExpertVo> getFanPortrait(Integer expertId);
@@ -159,17 +159,17 @@ public interface LifeUserExpertMapper extends BaseMapper<LifeUserExpert> {
             "user.user_name, user.province, user.city, user.district, user.user_brithday, user.user_sex " +
             " from life_user_expert expert " +
             " left join life_user user on user.id = expert.user_id " +
-            " left join life_fans fans on fans.followed_id = concat('user_', user.user_phone)  " +
+            " left join life_fans fans on fans.followed_user_type = 1 and fans.followed_ref_id = user.id and fans.delete_flag = 0 " +
             "where expert.delete_flag = 0 and user.delete_flag = 0" +
             " and user.id = #{userId}")
     List<LifeUserExpertVo> ifExpert(String userId);
 
 
     @Select("with fans as ( " +
-            "    select followed_id, count(id) num " +
+            "    select followed_user_type, followed_ref_id, count(id) num " +
             "    from life_fans " +
-            "    where delete_flag = 0 " +
-            "    group by followed_id " +
+            "    where delete_flag = 0 and followed_user_type = 1 " +
+            "    group by followed_user_type, followed_ref_id " +
             "), " +  // 这里添加逗号分隔不同的 CTE
             "works as ( " +
             "    select expert_id, count(id) worksNum, sum(like_count) likeNum, sum(play_count) playNum " +
@@ -182,7 +182,7 @@ public interface LifeUserExpertMapper extends BaseMapper<LifeUserExpert> {
             "ifnull(fans.num, 0) fansNum " +
             " from life_user_expert expert " +
             " left join life_user user on user.id = expert.user_id " +
-            " left join fans on fans.followed_id = concat('user_', user.user_phone)  " +
+            " left join fans on fans.followed_user_type = 1 and fans.followed_ref_id = user.id  " +
             " left join works on works.expert_id = expert.id " + // 添加与 works 表的连接条件
             " where expert.id = #{expertId}")
     LifeUserExpertVo getTalentIdentityInformation(Integer expertId);

+ 7 - 2
alien-entity/src/main/java/shop/alien/mapper/StoreClockInMapper.java

@@ -23,8 +23,12 @@ import java.util.Map;
  */
 public interface StoreClockInMapper extends BaseMapper<StoreClockIn> {
 
-    @Select("select clock.id, clock.user_id, clock.store_id, clock.permission, user.user_name, store.store_name, store.store_blurb storeBlurb, store.score_avg score, clock.like_count, store.store_position, clock.content, clock.created_time, clock.img_url clockImg, img.img_url storeImg, user.user_image userImg,clock.maybe_ai_content," +
-            "store.administrative_region_district_name region, dict.dict_detail storeType, concat('user_', user.user_phone) phoneId, store.business_section,store.business_section_name,business_types_name,img1.img_url entranceImage," +
+    @Select("select storeUser.logout_flag,clock.id, clock.user_id, clock.store_id, clock.permission, user.user_name, store.store_name, store.store_blurb storeBlurb, store.score_avg score, clock.like_count, store.store_position, clock.content, clock.created_time, clock.img_url clockImg, img.img_url storeImg, user.user_image userImg,clock.maybe_ai_content," +
+            "store.administrative_region_district_name region, dict.dict_detail storeType, clock.phone_user_type phoneUserType, clock.phone_ref_id phoneRefId, " +
+            "CASE WHEN clock.phone_user_type = 1 THEN CONCAT('user_', (SELECT user_phone FROM life_user WHERE id = clock.phone_ref_id AND delete_flag = 0 LIMIT 1)) " +
+            "WHEN clock.phone_user_type = 2 THEN CONCAT('store_', (SELECT phone FROM store_user WHERE id = clock.phone_ref_id AND delete_flag = 0 LIMIT 1)) " +
+            "WHEN clock.phone_user_type = 3 THEN CONCAT('lawyer_', (SELECT phone FROM lawyer_user WHERE id = clock.phone_ref_id AND delete_flag = 0 LIMIT 1)) " +
+            "END phoneId, store.business_section,store.business_section_name,business_types_name,img1.img_url entranceImage," +
             "( " +
             " select ifnull(round(sum(uorder.price) / count(1), 0), 0) " +
             " from life_user_order uorder " +
@@ -45,6 +49,7 @@ public interface StoreClockInMapper extends BaseMapper<StoreClockIn> {
             "from store_clock_in clock " +
             "join life_user user on user.id = clock.user_id " +
             "join store_info store on store.id = clock.store_id " +
+            "join store_user storeUser on storeUser.store_id = store.id " +
             "left join store_img img on img.store_id = clock.store_id and img.img_type = '10' and img.delete_flag = 0 " +
             "left join store_img img1 on img1.store_id = clock.store_id and img1.img_type = '1' and img1.delete_flag = 0 " +
             "left join store_dictionary dict on dict.type_name = 'storeType' and store.store_type = dict.dict_id and dict.delete_flag = 0 " +

+ 4 - 4
alien-entity/src/main/java/shop/alien/mapper/second/SecondRecommendMapper.java

@@ -19,13 +19,13 @@ public interface SecondRecommendMapper extends BaseMapper<SecondGoodsRecommendVo
      */
     IPage<SecondGoodsRecommendVo> getSecondRecommendByPage(IPage<SecondGoodsRecommendVo> page, @Param("userId") Integer userId, @Param("position") String position, @Param("typeId") Integer typeId, @Param("phoneId") String phoneId, @Param("radiusKm") String radiusKm);
 
-    IPage<SecondGoodsRecommendVo> querySecondConcernByPage(IPage<SecondGoodsRecommendVo> page, @Param("userId") Integer userId, @Param("phoneId") String phoneId, @Param("position") String position);
+    IPage<SecondGoodsRecommendVo> querySecondConcernByPage(IPage<SecondGoodsRecommendVo> page, @Param("userId") Integer userId, @Param("phoneId") String phoneId, @Param("position") String position, @Param("collectUserType") Integer collectUserType, @Param("collectRefId") Integer collectRefId, @Param("fansUserType") Integer fansUserType, @Param("fansRefId") Integer fansRefId);
 
-    IPage<SecondGoodsRecommendVo> querySecondNewGoodsByPage(IPage<SecondGoodsRecommendVo> page,@Param("userId") String userId, @Param("phoneId") String phoneId, @Param("position") String position, @Param("radiusKm") String radiusKm, @Param("timeRange") Integer timeRange);
+    IPage<SecondGoodsRecommendVo> querySecondNewGoodsByPage(IPage<SecondGoodsRecommendVo> page,@Param("userId") String userId, @Param("phoneId") String phoneId, @Param("position") String position, @Param("radiusKm") String radiusKm, @Param("timeRange") Integer timeRange, @Param("collectUserType") Integer collectUserType, @Param("collectRefId") Integer collectRefId);
 
     List<SecondCommentVo> querySecondCommentInfo(@Param("ids") List<Integer> ids);
 
-    SecondGoodsRecommendVo querySecondGoodsDetail(@Param("goodsId") Integer goodsId, @Param("phoneId") String phoneId, @Param("position") String position);
+    SecondGoodsRecommendVo querySecondGoodsDetail(@Param("goodsId") Integer goodsId, @Param("phoneId") String phoneId, @Param("position") String position, @Param("collectUserType") Integer collectUserType, @Param("collectRefId") Integer collectRefId);
 
-    SecondGoodsRecommendVo querySecondGoodsDetailWithoutPosition(@Param("goodsId") Integer goodsId, @Param("phoneId") String phoneId);
+    SecondGoodsRecommendVo querySecondGoodsDetailWithoutPosition(@Param("goodsId") Integer goodsId, @Param("phoneId") String phoneId, @Param("collectUserType") Integer collectUserType, @Param("collectRefId") Integer collectRefId);
 }

+ 133 - 0
alien-entity/src/main/java/shop/alien/util/type/LifeCollectIdentityQuery.java

@@ -0,0 +1,133 @@
+package shop.alien.util.type;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.LifeCollect;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.mapper.LifeUserMapper;
+
+/**
+ * life_collect 查询条件:将前端 user_{phone}/store_{phone}/lawyer_{phone} 或数字用户 id 解析为 type + refId 后查询。
+ */
+public final class LifeCollectIdentityQuery {
+
+    private static final int TYPE_USER = 1;
+
+    private LifeCollectIdentityQuery() {
+    }
+
+    public static final class Scope {
+        private final String legacyUserId;
+        private final Integer userType;
+        private final Integer refId;
+
+        public Scope(String legacyUserId, Integer userType, Integer refId) {
+            this.legacyUserId = legacyUserId;
+            this.userType = userType;
+            this.refId = refId;
+        }
+
+        public String getLegacyUserId() {
+            return legacyUserId;
+        }
+
+        public Integer getUserType() {
+            return userType;
+        }
+
+        public Integer getRefId() {
+            return refId;
+        }
+
+        public boolean hasTypeRef() {
+            return userType != null && refId != null;
+        }
+    }
+
+    /**
+     * 解析收藏人标识:支持 user_/store_/lawyer_ 前缀、裸手机号、life_user 数字主键。
+     */
+    public static Scope resolve(String userIdKey, TypeUtil typeUtil) {
+        if (!StringUtils.hasText(userIdKey)) {
+            return new Scope(userIdKey, null, null);
+        }
+        String key = userIdKey.trim();
+        if (typeUtil != null && !typeUtil.containsUnderscore(key)) {
+            try {
+                int refId = Integer.parseInt(key);
+                return new Scope(key, TYPE_USER, refId);
+            } catch (NumberFormatException ignored) {
+                key = "user_" + key;
+            }
+        }
+        if (typeUtil != null && typeUtil.containsUnderscore(key)) {
+            PhoneTypeIdResult resolved = typeUtil.resolveTypeAndId(key);
+            if (resolved != null) {
+                return new Scope(key, resolved.getType(), resolved.getId());
+            }
+        }
+        return new Scope(key, null, null);
+    }
+
+    /**
+     * 已知 life_user 主键时直接构造收藏人 scope(type=1)。
+     */
+    public static Scope fromLifeUserId(Integer lifeUserId) {
+        if (lifeUserId == null) {
+            return new Scope(null, null, null);
+        }
+        return new Scope(String.valueOf(lifeUserId), TYPE_USER, lifeUserId);
+    }
+
+    public static void applyUserSide(LambdaQueryWrapper<LifeCollect> wrapper, Scope scope) {
+        if (scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq(LifeCollect::getUserIdUserType, scope.getUserType())
+                .eq(LifeCollect::getUserIdRefId, scope.getRefId());
+    }
+
+    public static void applyUserSide(LambdaUpdateWrapper<LifeCollect> wrapper, Scope scope) {
+        if (scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq(LifeCollect::getUserIdUserType, scope.getUserType())
+                .eq(LifeCollect::getUserIdRefId, scope.getRefId());
+    }
+
+    public static void applyUserSide(QueryWrapper<LifeCollect> wrapper, Scope scope) {
+        if (scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq("user_id_user_type", scope.getUserType())
+                .eq("user_id_ref_id", scope.getRefId());
+    }
+
+    /**
+     * 写入收藏记录时解析 user_id 并填充 user_id_user_type、user_id_ref_id(不改变前端入参 user_id 字符串)。
+     */
+    public static void fillIdentityOnCollect(LifeCollect lifeCollect, TypeUtil typeUtil, LifeUserMapper lifeUserMapper) {
+        if (lifeCollect == null || !StringUtils.hasText(lifeCollect.getUserId())) {
+            return;
+        }
+        String userId = lifeCollect.getUserId().trim();
+        if (typeUtil != null && !typeUtil.containsUnderscore(userId)) {
+            try {
+                LifeUser lifeUser = lifeUserMapper.selectById(Integer.parseInt(userId));
+                if (lifeUser != null && StringUtils.hasText(lifeUser.getUserPhone())) {
+                    userId = "user_" + lifeUser.getUserPhone();
+                    lifeCollect.setUserId(userId);
+                }
+            } catch (NumberFormatException ignored) {
+                // 裸手机号,保持原样由 resolve 处理
+            }
+        }
+        Scope scope = resolve(userId, typeUtil);
+        if (scope.hasTypeRef()) {
+            lifeCollect.setUserIdUserType(scope.getUserType());
+            lifeCollect.setUserIdRefId(scope.getRefId());
+        }
+    }
+}

+ 147 - 0
alien-entity/src/main/java/shop/alien/util/type/LifeFansIdentityQuery.java

@@ -0,0 +1,147 @@
+package shop.alien.util.type;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.LifeFans;
+
+import java.util.Optional;
+
+/**
+ * life_fans 身份解析与查询:将 user_/store_/lawyer_ 前缀 phoneId 解析为 type + refId,
+ * 写入 fans_user_type / fans_ref_id / followed_user_type / followed_ref_id;
+ * 查询条件仅使用 type + refId(不依赖 fans_id / followed_id 字符串匹配)。
+ */
+public final class LifeFansIdentityQuery {
+
+    private LifeFansIdentityQuery() {
+    }
+
+    public static final class Scope {
+        private final String legacyPhoneId;
+        private final Integer userType;
+        private final Integer refId;
+
+        public Scope(String legacyPhoneId, Integer userType, Integer refId) {
+            this.legacyPhoneId = legacyPhoneId;
+            this.userType = userType;
+            this.refId = refId;
+        }
+
+        public String getLegacyPhoneId() {
+            return legacyPhoneId;
+        }
+
+        public Integer getUserType() {
+            return userType;
+        }
+
+        public Integer getRefId() {
+            return refId;
+        }
+
+        public boolean hasTypeRef() {
+            return userType != null && refId != null;
+        }
+    }
+
+    public static Scope resolve(String phoneId, TypeUtil typeUtil) {
+        if (!StringUtils.hasText(phoneId)) {
+            return new Scope(phoneId, null, null);
+        }
+        String key = phoneId.trim();
+        if (typeUtil != null && typeUtil.containsUnderscore(key)) {
+            PhoneTypeIdResult resolved = typeUtil.resolveTypeAndId(key);
+            if (resolved != null) {
+                return new Scope(key, resolved.getType(), resolved.getId());
+            }
+        }
+        return new Scope(key, null, null);
+    }
+
+    /**
+     * 写入关注记录时解析 fans_id / followed_id 并填充 type + ref 字段(不改变前端入参字符串)。
+     */
+    public static void fillIdentityOnFans(LifeFans lifeFans, TypeUtil typeUtil) {
+        if (lifeFans == null) {
+            return;
+        }
+        Scope fansScope = resolve(lifeFans.getFansId(), typeUtil);
+        if (fansScope.hasTypeRef()) {
+            lifeFans.setFansUserType(fansScope.getUserType());
+            lifeFans.setFansRefId(fansScope.getRefId());
+        }
+        Scope followedScope = resolve(lifeFans.getFollowedId(), typeUtil);
+        if (followedScope.hasTypeRef()) {
+            lifeFans.setFollowedUserType(followedScope.getUserType());
+            lifeFans.setFollowedRefId(followedScope.getRefId());
+        }
+    }
+
+    public static boolean hasCompletePairIdentity(LifeFans lifeFans) {
+        return lifeFans != null
+                && lifeFans.getFansUserType() != null
+                && lifeFans.getFansRefId() != null
+                && lifeFans.getFollowedUserType() != null
+                && lifeFans.getFollowedRefId() != null;
+    }
+
+    /**
+     * 回填后校验双方 type + ref 是否齐全;失败时返回错误文案。
+     */
+    public static Optional<String> validatePairIdentity(LifeFans lifeFans) {
+        if (lifeFans == null) {
+            return Optional.of("请求参数不能为空:请传入被关注方与粉丝标识");
+        }
+        if (lifeFans.getFansUserType() == null || lifeFans.getFansRefId() == null) {
+            return Optional.of("粉丝方标识(fansId)无法解析为有效用户,请检查格式或用户是否存在");
+        }
+        if (lifeFans.getFollowedUserType() == null || lifeFans.getFollowedRefId() == null) {
+            return Optional.of("被关注方标识(followedId)无法解析为有效用户,请检查格式或用户是否存在");
+        }
+        return Optional.empty();
+    }
+
+    public static void applyFansSide(LambdaQueryWrapper<LifeFans> wrapper, Scope scope) {
+        if (scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq(LifeFans::getFansUserType, scope.getUserType())
+                .eq(LifeFans::getFansRefId, scope.getRefId());
+    }
+
+    public static void applyFollowedSide(LambdaQueryWrapper<LifeFans> wrapper, Scope scope) {
+        if (scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq(LifeFans::getFollowedUserType, scope.getUserType())
+                .eq(LifeFans::getFollowedRefId, scope.getRefId());
+    }
+
+    public static void applyFansSide(LambdaUpdateWrapper<LifeFans> wrapper, Scope scope) {
+        if (scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq(LifeFans::getFansUserType, scope.getUserType())
+                .eq(LifeFans::getFansRefId, scope.getRefId());
+    }
+
+    public static void applyFollowedSide(LambdaUpdateWrapper<LifeFans> wrapper, Scope scope) {
+        if (scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq(LifeFans::getFollowedUserType, scope.getUserType())
+                .eq(LifeFans::getFollowedRefId, scope.getRefId());
+    }
+
+    /** 粉丝方 + 被关注方四维匹配(查重、互关等)。 */
+    public static void applyPair(LambdaQueryWrapper<LifeFans> wrapper, Scope fansScope, Scope followedScope) {
+        applyFansSide(wrapper, fansScope);
+        applyFollowedSide(wrapper, followedScope);
+    }
+
+    public static void applyPairWithDeleteFlag(LambdaQueryWrapper<LifeFans> wrapper, Scope fansScope, Scope followedScope) {
+        wrapper.eq(LifeFans::getDeleteFlag, 0);
+        applyPair(wrapper, fansScope, followedScope);
+    }
+}

+ 215 - 0
alien-entity/src/main/java/shop/alien/util/type/LifeMessageUtil.java

@@ -0,0 +1,215 @@
+package shop.alien.util.type;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.LifeMessage;
+
+import java.util.List;
+
+/**
+ * LifeMessage 发送人/接收人 userType、refId 回填与查询工具。
+ */
+@Component
+@RequiredArgsConstructor
+public class LifeMessageUtil {
+
+    private final TypeUtil typeUtil;
+
+    public void fillUserTypeAndRefId(LifeMessage message) {
+        if (message == null) {
+            return;
+        }
+        fillSender(message);
+        fillReceiver(message);
+        fillDeletePhone(message);
+    }
+
+    private void fillSender(LifeMessage message) {
+        String senderId = message.getSenderId();
+        if (!StringUtils.hasText(senderId)) {
+            return;
+        }
+        if (message.getSenderUserType() != null && message.getSenderRefId() != null) {
+            return;
+        }
+        if (typeUtil.containsUnderscore(senderId)) {
+            applyTypeId(message, typeUtil.resolveTypeAndId(senderId), true);
+        }
+    }
+
+    private void fillReceiver(LifeMessage message) {
+        String receiverId = message.getReceiverId();
+        if (!StringUtils.hasText(receiverId)) {
+            return;
+        }
+        if (message.getReceiverUserType() != null && message.getReceiverRefId() != null) {
+            return;
+        }
+        if (typeUtil.containsUnderscore(receiverId)) {
+            applyTypeId(message, typeUtil.resolveTypeAndId(receiverId), false);
+        }
+    }
+
+    private void fillDeletePhone(LifeMessage message) {
+        String deletePhoneId = message.getDeletePhoneId();
+        if (!StringUtils.hasText(deletePhoneId)) {
+            return;
+        }
+        if (message.getDeletePhoneUserType() != null && message.getDeletePhoneRefId() != null) {
+            return;
+        }
+        PhoneTypeIdResult result = typeUtil.resolveTypeAndId(deletePhoneId);
+        if (result != null) {
+            message.setDeletePhoneUserType(result.getType());
+            message.setDeletePhoneRefId(result.getId());
+        }
+    }
+
+    private void applyTypeId(LifeMessage message, PhoneTypeIdResult result, boolean sender) {
+        if (result == null) {
+            return;
+        }
+        if (sender) {
+            message.setSenderUserType(result.getType());
+            message.setSenderRefId(result.getId());
+        } else {
+            message.setReceiverUserType(result.getType());
+            message.setReceiverRefId(result.getId());
+        }
+    }
+
+    public PhoneTypeIdResult resolveIdentity(String phoneId) {
+        if (!StringUtils.hasText(phoneId)) {
+            return null;
+        }
+        return typeUtil.resolveTypeAndId(phoneId);
+    }
+
+    public void applyReceiverQuery(LambdaQueryWrapper<LifeMessage> wrapper, String receiverId) {
+        applyReceiverIdentity(wrapper, receiverId);
+    }
+
+    public void applyReceiverUpdate(LambdaUpdateWrapper<LifeMessage> wrapper, String receiverId) {
+        applyReceiverIdentity(wrapper, receiverId);
+    }
+
+    public void applySenderQuery(LambdaQueryWrapper<LifeMessage> wrapper, String senderId) {
+        applySenderIdentity(wrapper, senderId);
+    }
+
+    public void applySenderReceiverPairQuery(LambdaQueryWrapper<LifeMessage> wrapper, String senderId, String receiverId) {
+        PhoneTypeIdResult sender = resolveIdentity(senderId);
+        PhoneTypeIdResult receiver = resolveIdentity(receiverId);
+        if (sender == null || receiver == null) {
+            wrapper.apply("1 = 0");
+            return;
+        }
+        wrapper.eq(LifeMessage::getSenderUserType, sender.getType())
+                .eq(LifeMessage::getSenderRefId, sender.getId())
+                .eq(LifeMessage::getReceiverUserType, receiver.getType())
+                .eq(LifeMessage::getReceiverRefId, receiver.getId());
+    }
+
+    public void applySenderReceiverPairUpdate(LambdaUpdateWrapper<LifeMessage> wrapper, String senderId, String receiverId) {
+        PhoneTypeIdResult sender = resolveIdentity(senderId);
+        PhoneTypeIdResult receiver = resolveIdentity(receiverId);
+        if (sender == null || receiver == null) {
+            wrapper.apply("1 = 0");
+            return;
+        }
+        wrapper.eq(LifeMessage::getSenderUserType, sender.getType())
+                .eq(LifeMessage::getSenderRefId, sender.getId())
+                .eq(LifeMessage::getReceiverUserType, receiver.getType())
+                .eq(LifeMessage::getReceiverRefId, receiver.getId());
+    }
+
+    public void applyConversationQuery(LambdaQueryWrapper<LifeMessage> wrapper, String phoneId1, String phoneId2) {
+        PhoneTypeIdResult first = resolveIdentity(phoneId1);
+        PhoneTypeIdResult second = resolveIdentity(phoneId2);
+        if (first == null || second == null) {
+            wrapper.apply("1 = 0");
+            return;
+        }
+        wrapper.and(w -> w.nested(n -> n.eq(LifeMessage::getSenderUserType, first.getType())
+                        .eq(LifeMessage::getSenderRefId, first.getId())
+                        .eq(LifeMessage::getReceiverUserType, second.getType())
+                        .eq(LifeMessage::getReceiverRefId, second.getId()))
+                .or()
+                .nested(n -> n.eq(LifeMessage::getSenderUserType, second.getType())
+                        .eq(LifeMessage::getSenderRefId, second.getId())
+                        .eq(LifeMessage::getReceiverUserType, first.getType())
+                        .eq(LifeMessage::getReceiverRefId, first.getId())));
+    }
+
+    public void applySenderInQuery(LambdaQueryWrapper<LifeMessage> wrapper, List<String> senderPhoneIds) {
+        if (senderPhoneIds == null || senderPhoneIds.isEmpty()) {
+            wrapper.apply("1 = 0");
+            return;
+        }
+        wrapper.and(w -> {
+            boolean added = false;
+            for (String senderPhoneId : senderPhoneIds) {
+                PhoneTypeIdResult sender = resolveIdentity(senderPhoneId);
+                if (sender == null) {
+                    continue;
+                }
+                if (added) {
+                    w.or();
+                }
+                w.nested(n -> n.eq(LifeMessage::getSenderUserType, sender.getType())
+                        .eq(LifeMessage::getSenderRefId, sender.getId()));
+                added = true;
+            }
+            if (!added) {
+                w.apply("1 = 0");
+            }
+        });
+    }
+
+    /**
+     * 子查询:当前用户作为发送方时,聊过的对方 receiver_id 列表。
+     */
+    public String sqlDistinctReceiverIdsBySender(String senderId) {
+        PhoneTypeIdResult sender = resolveIdentity(senderId);
+        if (sender == null) {
+            return "select null where 1 = 0";
+        }
+        return "select receiver_id from life_message where delete_flag = 0 "
+                + "and sender_user_type = " + sender.getType()
+                + " and sender_ref_id = " + sender.getId()
+                + " group by receiver_id";
+    }
+
+    private void applyReceiverIdentity(LambdaQueryWrapper<LifeMessage> wrapper, String receiverId) {
+        PhoneTypeIdResult resolved = resolveIdentity(receiverId);
+        if (resolved != null && resolved.getType() != null && resolved.getId() != null) {
+            wrapper.eq(LifeMessage::getReceiverUserType, resolved.getType())
+                    .eq(LifeMessage::getReceiverRefId, resolved.getId());
+        } else {
+            wrapper.apply("1 = 0");
+        }
+    }
+
+    private void applySenderIdentity(LambdaQueryWrapper<LifeMessage> wrapper, String senderId) {
+        PhoneTypeIdResult resolved = resolveIdentity(senderId);
+        if (resolved != null && resolved.getType() != null && resolved.getId() != null) {
+            wrapper.eq(LifeMessage::getSenderUserType, resolved.getType())
+                    .eq(LifeMessage::getSenderRefId, resolved.getId());
+        } else {
+            wrapper.apply("1 = 0");
+        }
+    }
+
+    private void applyReceiverIdentity(LambdaUpdateWrapper<LifeMessage> wrapper, String receiverId) {
+        PhoneTypeIdResult resolved = resolveIdentity(receiverId);
+        if (resolved != null && resolved.getType() != null && resolved.getId() != null) {
+            wrapper.eq(LifeMessage::getReceiverUserType, resolved.getType())
+                    .eq(LifeMessage::getReceiverRefId, resolved.getId());
+        } else {
+            wrapper.apply("1 = 0");
+        }
+    }
+}

+ 129 - 0
alien-entity/src/main/java/shop/alien/util/type/LifeNoticeUtil.java

@@ -0,0 +1,129 @@
+package shop.alien.util.type;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.LifeNotice;
+
+/**
+ * LifeNotice 发送人/接收人 userType、refId 回填工具。
+ * <p>
+ * senderId 为 system 时仅补接收人;为 user_/store_/lawyer_ 前缀时补发送人与接收人。
+ * </p>
+ */
+@Component
+@RequiredArgsConstructor
+public class LifeNoticeUtil {
+
+    private static final String SYSTEM_SENDER_ID = "system";
+
+    private final TypeUtil typeUtil;
+
+    public void fillUserTypeAndRefId(LifeNotice notice) {
+        if (notice == null) {
+            return;
+        }
+        fillSender(notice);
+        fillReceiver(notice);
+    }
+
+    private void fillSender(LifeNotice notice) {
+        String senderId = notice.getSenderId();
+        if (!StringUtils.hasText(senderId) || SYSTEM_SENDER_ID.equals(senderId)) {
+            return;
+        }
+        if (notice.getSenderUserType() != null && notice.getSenderRefId() != null) {
+            return;
+        }
+        if (typeUtil.containsUnderscore(senderId)) {
+            applyTypeId(notice, typeUtil.resolveTypeAndId(senderId), true);
+        }
+    }
+
+    private void fillReceiver(LifeNotice notice) {
+        String receiverId = notice.getReceiverId();
+        if (!StringUtils.hasText(receiverId)) {
+            return;
+        }
+        if (notice.getReceiverUserType() != null && notice.getReceiverRefId() != null) {
+            return;
+        }
+        if (typeUtil.containsUnderscore(receiverId)) {
+            applyTypeId(notice, typeUtil.resolveTypeAndId(receiverId), false);
+        }
+    }
+
+    private void applyTypeId(LifeNotice notice, PhoneTypeIdResult result, boolean sender) {
+        if (result == null) {
+            return;
+        }
+        if (sender) {
+            notice.setSenderUserType(result.getType());
+            notice.setSenderRefId(result.getId());
+        } else {
+            notice.setReceiverUserType(result.getType());
+            notice.setReceiverRefId(result.getId());
+        }
+    }
+
+    /**
+     * 按接收人 receiver_user_type + receiver_ref_id 查询。
+     */
+    public void applyReceiverQuery(LambdaQueryWrapper<LifeNotice> wrapper, String receiverId) {
+        applyReceiverIdentity(wrapper, receiverId);
+    }
+
+    /**
+     * 按接收人 receiver_user_type + receiver_ref_id 更新条件。
+     */
+    public void applyReceiverUpdate(LambdaUpdateWrapper<LifeNotice> wrapper, String receiverId) {
+        applyReceiverIdentity(wrapper, receiverId);
+    }
+
+    /**
+     * 按发送人 sender_user_type + sender_ref_id 查询。
+     */
+    public void applySenderQuery(LambdaQueryWrapper<LifeNotice> wrapper, String senderId) {
+        if (!StringUtils.hasText(senderId) || SYSTEM_SENDER_ID.equals(senderId)) {
+            wrapper.apply("1 = 0");
+            return;
+        }
+        PhoneTypeIdResult resolved = typeUtil.resolveTypeAndId(senderId);
+        if (resolved != null && resolved.getType() != null && resolved.getId() != null) {
+            wrapper.eq(LifeNotice::getSenderUserType, resolved.getType())
+                    .eq(LifeNotice::getSenderRefId, resolved.getId());
+        } else {
+            wrapper.apply("1 = 0");
+        }
+    }
+
+    private void applyReceiverIdentity(LambdaQueryWrapper<LifeNotice> wrapper, String receiverId) {
+        if (!StringUtils.hasText(receiverId)) {
+            wrapper.apply("1 = 0");
+            return;
+        }
+        PhoneTypeIdResult resolved = typeUtil.resolveTypeAndId(receiverId);
+        if (resolved != null && resolved.getType() != null && resolved.getId() != null) {
+            wrapper.eq(LifeNotice::getReceiverUserType, resolved.getType())
+                    .eq(LifeNotice::getReceiverRefId, resolved.getId());
+        } else {
+            wrapper.apply("1 = 0");
+        }
+    }
+
+    private void applyReceiverIdentity(LambdaUpdateWrapper<LifeNotice> wrapper, String receiverId) {
+        if (!StringUtils.hasText(receiverId)) {
+            wrapper.apply("1 = 0");
+            return;
+        }
+        PhoneTypeIdResult resolved = typeUtil.resolveTypeAndId(receiverId);
+        if (resolved != null && resolved.getType() != null && resolved.getId() != null) {
+            wrapper.eq(LifeNotice::getReceiverUserType, resolved.getType())
+                    .eq(LifeNotice::getReceiverRefId, resolved.getId());
+        } else {
+            wrapper.apply("1 = 0");
+        }
+    }
+}

+ 29 - 0
alien-entity/src/main/java/shop/alien/util/type/PhoneTypeIdResult.java

@@ -0,0 +1,29 @@
+package shop.alien.util.type;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 根据 phoneId(type_phone 格式)解析出的用户类型与主键
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel(value = "PhoneTypeIdResult", description = "phoneId 解析结果:type + id")
+public class PhoneTypeIdResult {
+
+    /**
+     * 用户类型:1-普通用户(life_user),2-商户(store_user),3-律师(lawyer_user)
+     */
+    @ApiModelProperty("用户类型:1-普通用户,2-商户,3-律师")
+    private Integer type;
+
+    /**
+     * 对应表主键 id
+     */
+    @ApiModelProperty("用户主键 id")
+    private Integer id;
+}

+ 126 - 0
alien-entity/src/main/java/shop/alien/util/type/TypeUtil.java

@@ -0,0 +1,126 @@
+package shop.alien.util.type;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.mapper.LawyerUserMapper;
+import shop.alien.mapper.LifeUserMapper;
+import shop.alien.mapper.StoreUserMapper;
+
+/**
+ * phoneId(type_phone)解析工具:根据前缀查询对应用户表并返回 type + id
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class TypeUtil {
+
+    /** 普通用户 life_user */
+    private static final int TYPE_USER = 1;
+    /** 商户 store_user */
+    private static final int TYPE_STORE = 2;
+    /** 律师 lawyer_user */
+    private static final int TYPE_LAWYER = 3;
+
+    private final LifeUserMapper lifeUserMapper;
+    private final StoreUserMapper storeUserMapper;
+    private final LawyerUserMapper lawyerUserMapper;
+
+    /**
+     * 根据 type_phone 格式的 phoneId 查询对应用户 id
+     * <p>
+     * 示例:user_13800138000 → life_user.user_phone = 13800138000 → {type:1, id:?}<br>
+     * store_13800138000 → store_user.phone = 13800138000 → {type:2, id:?}<br>
+     * lawyer_13800138000 → lawyer_user.phone = 13800138000 → {type:3, id:?}
+     * </p>
+     *
+     * @param typePhone phoneId,格式为 type + "_" + 手机号
+     * @return 解析成功返回 type 与 id;格式非法或未查到用户时返回 null
+     */
+    public PhoneTypeIdResult resolveTypeAndId(String typePhone) {
+        if (!StringUtils.hasText(typePhone) || !typePhone.contains("_")) {
+            log.warn("resolveTypeAndId 入参非法: typePhone={}", typePhone);
+            return null;
+        }
+
+        String[] parts = typePhone.split("_", 2);
+        if (parts.length < 2 || !StringUtils.hasText(parts[1])) {
+            log.warn("resolveTypeAndId 无法解析手机号: typePhone={}", typePhone);
+            return null;
+        }
+
+        String typePrefix = parts[0];
+        String phone = parts[1];
+
+        try {
+            switch (typePrefix) {
+                case "user":
+                    return queryLifeUserId(phone);
+                case "store":
+                    return queryStoreUserId(phone);
+                case "lawyer":
+                    return queryLawyerUserId(phone);
+                default:
+                    log.warn("resolveTypeAndId 不支持的用户类型前缀: typePrefix={}, typePhone={}", typePrefix, typePhone);
+                    return null;
+            }
+        } catch (Exception e) {
+            log.error("resolveTypeAndId 查询失败: typePhone={}", typePhone, e);
+            return null;
+        }
+    }
+
+    private PhoneTypeIdResult queryLifeUserId(String phone) {
+        LambdaQueryWrapper<LifeUser> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(LifeUser::getUserPhone, phone)
+                .eq(LifeUser::getDeleteFlag, 0)
+                .last("LIMIT 1");
+        LifeUser lifeUser = lifeUserMapper.selectOne(wrapper);
+        if (lifeUser == null) {
+            log.warn("未找到普通用户: phone={}", phone);
+            return null;
+        }
+        log.debug("解析普通用户成功: phone={}, id={}", phone, lifeUser.getId());
+        return new PhoneTypeIdResult(TYPE_USER, lifeUser.getId());
+    }
+
+    private PhoneTypeIdResult queryStoreUserId(String phone) {
+        LambdaQueryWrapper<StoreUser> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(StoreUser::getPhone, phone)
+                .eq(StoreUser::getDeleteFlag, 0)
+                .last("LIMIT 1");
+        StoreUser storeUser = storeUserMapper.selectOne(wrapper);
+        if (storeUser == null) {
+            log.warn("未找到商户用户: phone={}", phone);
+            return null;
+        }
+        log.debug("解析商户用户成功: phone={}, id={}", phone, storeUser.getId());
+        return new PhoneTypeIdResult(TYPE_STORE, storeUser.getId());
+    }
+
+    private PhoneTypeIdResult queryLawyerUserId(String phone) {
+        LambdaQueryWrapper<LawyerUser> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(LawyerUser::getPhone, phone)
+                .eq(LawyerUser::getDeleteFlag, 0)
+                .last("LIMIT 1");
+        LawyerUser lawyerUser = lawyerUserMapper.selectOne(wrapper);
+        if (lawyerUser == null) {
+            log.warn("未找到律师用户: phone={}", phone);
+            return null;
+        }
+        log.debug("解析律师用户成功: phone={}, id={}", phone, lawyerUser.getId());
+        return new PhoneTypeIdResult(TYPE_LAWYER, lawyerUser.getId());
+    }
+
+    public boolean containsUnderscore(String input) {
+        if (input == null) {
+            return false;
+        }
+        return input.contains("_");
+    }
+}

+ 4 - 0
alien-entity/src/main/resources/db/migration/life_discount_coupon_status_invalid.sql

@@ -0,0 +1,4 @@
+-- coupon_status 扩展:2-已失效(如店铺注销冷静期结束)
+-- 若已执行 life_discount_coupon_invalid_flag.sql,请先 DROP COLUMN invalid_flag 后再执行本脚本注释更新
+ALTER TABLE `life_discount_coupon`
+    MODIFY COLUMN `coupon_status` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '优惠券状态:0-草稿,1-正式,2-已失效';

+ 75 - 0
alien-entity/src/main/resources/db/migration/life_fans_type_ref_backfill.sql

@@ -0,0 +1,75 @@
+-- life_fans:历史数据 type + ref_id 回填(PR-1)
+-- 执行前请备份;建议在从库验证后再上生产。
+-- 前缀规则:user_ -> 1 + life_user.id;store_ -> 2 + store_user.id;lawyer_ -> 3 + lawyer_user.id
+
+-- ---------------------------------------------------------------------------
+-- 1. fans 侧 fans_id
+-- ---------------------------------------------------------------------------
+UPDATE life_fans lf
+    INNER JOIN life_user lu ON lf.fans_id LIKE 'user\_%'
+        AND lu.user_phone = SUBSTRING(lf.fans_id, 6) AND lu.delete_flag = 0
+SET lf.fans_user_type = 1, lf.fans_ref_id = lu.id
+WHERE lf.fans_user_type IS NULL AND lf.fans_id LIKE 'user\_%';
+
+UPDATE life_fans lf
+    INNER JOIN store_user su ON lf.fans_id LIKE 'store\_%'
+        AND su.phone = SUBSTRING(lf.fans_id, 7) AND su.delete_flag = 0
+SET lf.fans_user_type = 2, lf.fans_ref_id = su.id
+WHERE lf.fans_user_type IS NULL AND lf.fans_id LIKE 'store\_%';
+
+UPDATE life_fans lf
+    INNER JOIN lawyer_user lwu ON lf.fans_id LIKE 'lawyer\_%'
+        AND lwu.phone = SUBSTRING(lf.fans_id, 8) AND lwu.delete_flag = 0
+SET lf.fans_user_type = 3, lf.fans_ref_id = lwu.id
+WHERE lf.fans_user_type IS NULL AND lf.fans_id LIKE 'lawyer\_%';
+
+-- ---------------------------------------------------------------------------
+-- 2. 被关注方 followed_id
+-- ---------------------------------------------------------------------------
+UPDATE life_fans lf
+    INNER JOIN life_user lu ON lf.followed_id LIKE 'user\_%'
+        AND lu.user_phone = SUBSTRING(lf.followed_id, 6) AND lu.delete_flag = 0
+SET lf.followed_user_type = 1, lf.followed_ref_id = lu.id
+WHERE lf.followed_user_type IS NULL AND lf.followed_id LIKE 'user\_%';
+
+UPDATE life_fans lf
+    INNER JOIN store_user su ON lf.followed_id LIKE 'store\_%'
+        AND su.phone = SUBSTRING(lf.followed_id, 7) AND su.delete_flag = 0
+SET lf.followed_user_type = 2, lf.followed_ref_id = su.id
+WHERE lf.followed_user_type IS NULL AND lf.followed_id LIKE 'store\_%';
+
+UPDATE life_fans lf
+    INNER JOIN lawyer_user lwu ON lf.followed_id LIKE 'lawyer\_%'
+        AND lwu.phone = SUBSTRING(lf.followed_id, 8) AND lwu.delete_flag = 0
+SET lf.followed_user_type = 3, lf.followed_ref_id = lwu.id
+WHERE lf.followed_user_type IS NULL AND lf.followed_id LIKE 'lawyer\_%';
+
+-- ---------------------------------------------------------------------------
+-- 3. 校验:未回填的有效关注记录(delete_flag=0 且四方 type/ref 任一为 NULL)
+-- ---------------------------------------------------------------------------
+-- SELECT id, fans_id, followed_id, fans_user_type, fans_ref_id, followed_user_type, followed_ref_id
+-- FROM life_fans
+-- WHERE delete_flag = 0
+--   AND (fans_user_type IS NULL OR fans_ref_id IS NULL
+--     OR followed_user_type IS NULL OR followed_ref_id IS NULL);
+
+-- ---------------------------------------------------------------------------
+-- 4. 可选:type+ref 维度唯一约束(确认无重复后再执行)
+-- ---------------------------------------------------------------------------
+-- DELETE FROM life_fans
+-- WHERE id IN (
+--     SELECT id FROM (
+--         SELECT lf.id AS id,
+--                ROW_NUMBER() OVER (
+--                    PARTITION BY lf.followed_user_type, lf.followed_ref_id, lf.fans_user_type, lf.fans_ref_id
+--                    ORDER BY CASE WHEN lf.delete_flag = 0 THEN 0 ELSE 1 END, lf.id ASC
+--                ) AS rn
+--         FROM life_fans lf
+--         WHERE lf.followed_user_type IS NOT NULL AND lf.followed_ref_id IS NOT NULL
+--           AND lf.fans_user_type IS NOT NULL AND lf.fans_ref_id IS NOT NULL
+--     ) ranked
+--     WHERE ranked.rn > 1
+-- );
+--
+-- ALTER TABLE life_fans
+--     ADD UNIQUE KEY uk_life_fans_type_ref_pair (followed_user_type, followed_ref_id, fans_user_type, fans_ref_id);

+ 210 - 0
alien-entity/src/main/resources/db/migration/phone_identity_user_type_ref_id.sql

@@ -0,0 +1,210 @@
+-- 手机号身份键配套字段:user_type + ref_id
+-- 分类口径(除 life_blacklist 外):1-用户(life_user) 2-商户(store_user) 3-律师(lawyer_user)
+-- life_blacklist 仍使用原有 blocker_type/blocked_type(1-商户 2-用户),本脚本不涉及
+-- 执行前请备份;若列已存在请跳过对应 ADD COLUMN
+
+-- ---------------------------------------------------------------------------
+-- 1. life_fans(fans_id / followed_id)
+-- ---------------------------------------------------------------------------
+ALTER TABLE life_fans
+    ADD COLUMN fans_user_type     TINYINT NULL COMMENT '粉丝用户分类:1-用户 2-商户 3-律师' AFTER fans_id,
+    ADD COLUMN fans_ref_id        INT     NULL COMMENT '粉丝对应表主键id(与 fans_user_type 对应)' AFTER fans_user_type,
+    ADD COLUMN followed_user_type TINYINT NULL COMMENT '被关注方用户分类:1-用户 2-商户 3-律师' AFTER followed_id,
+    ADD COLUMN followed_ref_id    INT     NULL COMMENT '被关注方对应表主键id(与 followed_user_type 对应)' AFTER followed_user_type;
+
+-- ---------------------------------------------------------------------------
+-- 2. life_user_dynamics(phone_id)
+-- ---------------------------------------------------------------------------
+ALTER TABLE life_user_dynamics
+    ADD COLUMN phone_user_type TINYINT NULL COMMENT '发布人用户分类:1-用户 2-商户 3-律师' AFTER phone_id,
+    ADD COLUMN phone_ref_id    INT     NULL COMMENT '发布人对应表主键id(与 phone_user_type 对应)' AFTER phone_user_type;
+
+-- ---------------------------------------------------------------------------
+-- 3. life_message(sender_id / receiver_id / delete_phone_id)
+-- ---------------------------------------------------------------------------
+ALTER TABLE life_message
+    ADD COLUMN sender_user_type       TINYINT NULL COMMENT '发送人用户分类:1-用户 2-商户 3-律师' AFTER sender_id,
+    ADD COLUMN sender_ref_id          INT     NULL COMMENT '发送人对应表主键id' AFTER sender_user_type,
+    ADD COLUMN receiver_user_type     TINYINT NULL COMMENT '接收人用户分类:1-用户 2-商户 3-律师' AFTER receiver_id,
+    ADD COLUMN receiver_ref_id        INT     NULL COMMENT '接收人对应表主键id' AFTER receiver_user_type,
+    ADD COLUMN delete_phone_user_type TINYINT NULL COMMENT '删除方用户分类:1-用户 2-商户 3-律师' AFTER delete_phone_id,
+    ADD COLUMN delete_phone_ref_id    INT     NULL COMMENT '删除方对应表主键id' AFTER delete_phone_user_type;
+
+-- ---------------------------------------------------------------------------
+-- 4. life_notice(sender_id / receiver_id)
+-- ---------------------------------------------------------------------------
+ALTER TABLE life_notice
+    ADD COLUMN sender_user_type   TINYINT NULL COMMENT '发送人用户分类:1-用户 2-商户 3-律师' AFTER sender_id,
+    ADD COLUMN sender_ref_id      INT     NULL COMMENT '发送人对应表主键id' AFTER sender_user_type,
+    ADD COLUMN receiver_user_type TINYINT NULL COMMENT '接收人用户分类:1-用户 2-商户 3-律师' AFTER receiver_id,
+    ADD COLUMN receiver_ref_id    INT     NULL COMMENT '接收人对应表主键id' AFTER receiver_user_type;
+
+-- ---------------------------------------------------------------------------
+-- 5. life_like_record(dianzan_id)
+-- ---------------------------------------------------------------------------
+ALTER TABLE life_like_record
+    ADD COLUMN dianzan_user_type TINYINT NULL COMMENT '点赞人用户分类:1-用户 2-商户 3-律师' AFTER dianzan_id,
+    ADD COLUMN dianzan_ref_id    INT     NULL COMMENT '点赞人对应表主键id' AFTER dianzan_user_type;
+
+-- ---------------------------------------------------------------------------
+-- 6. store_clock_in(phone_id)
+-- ---------------------------------------------------------------------------
+ALTER TABLE store_clock_in
+    ADD COLUMN phone_user_type TINYINT NULL COMMENT '打卡人用户分类:1-用户 2-商户 3-律师' AFTER phone_id,
+    ADD COLUMN phone_ref_id    INT     NULL COMMENT '打卡人对应表主键id' AFTER phone_user_type;
+
+-- ---------------------------------------------------------------------------
+-- 7. life_collect(user_id 身份键字段)
+-- ---------------------------------------------------------------------------
+ALTER TABLE life_collect
+    ADD COLUMN user_id_user_type TINYINT NULL COMMENT '收藏人用户分类:1-用户 2-商户 3-律师' AFTER user_id,
+    ADD COLUMN user_id_ref_id    INT     NULL COMMENT '收藏人对应表主键id' AFTER user_id_user_type;
+
+
+-- ===========================================================================
+-- 可选:历史数据回填(按需执行,建议先在从库/备份环境验证)
+-- 前缀规则:user_ -> 1 + life_user.id;store_ -> 2 + store_user.id;lawyer_ -> 3 + lawyer_user.id
+-- ===========================================================================
+
+-- life_fans.fans_id
+UPDATE life_fans lf
+    INNER JOIN life_user lu ON lf.fans_id LIKE 'user\_%' AND lu.user_phone = SUBSTRING(lf.fans_id, 6) AND lu.delete_flag = 0
+SET lf.fans_user_type = 1, lf.fans_ref_id = lu.id
+WHERE lf.fans_user_type IS NULL AND lf.fans_id LIKE 'user\_%';
+
+UPDATE life_fans lf
+    INNER JOIN store_user su ON lf.fans_id LIKE 'store\_%' AND su.phone = SUBSTRING(lf.fans_id, 7) AND su.delete_flag = 0
+SET lf.fans_user_type = 2, lf.fans_ref_id = su.id
+WHERE lf.fans_user_type IS NULL AND lf.fans_id LIKE 'store\_%';
+
+UPDATE life_fans lf
+    INNER JOIN lawyer_user lwu ON lf.fans_id LIKE 'lawyer\_%' AND lwu.phone = SUBSTRING(lf.fans_id, 8) AND lwu.delete_flag = 0
+SET lf.fans_user_type = 3, lf.fans_ref_id = lwu.id
+WHERE lf.fans_user_type IS NULL AND lf.fans_id LIKE 'lawyer\_%';
+
+-- life_fans.followed_id
+UPDATE life_fans lf
+    INNER JOIN life_user lu ON lf.followed_id LIKE 'user\_%' AND lu.user_phone = SUBSTRING(lf.followed_id, 6) AND lu.delete_flag = 0
+SET lf.followed_user_type = 1, lf.followed_ref_id = lu.id
+WHERE lf.followed_user_type IS NULL AND lf.followed_id LIKE 'user\_%';
+
+UPDATE life_fans lf
+    INNER JOIN store_user su ON lf.followed_id LIKE 'store\_%' AND su.phone = SUBSTRING(lf.followed_id, 7) AND su.delete_flag = 0
+SET lf.followed_user_type = 2, lf.followed_ref_id = su.id
+WHERE lf.followed_user_type IS NULL AND lf.followed_id LIKE 'store\_%';
+
+UPDATE life_fans lf
+    INNER JOIN lawyer_user lwu ON lf.followed_id LIKE 'lawyer\_%' AND lwu.phone = SUBSTRING(lf.followed_id, 8) AND lwu.delete_flag = 0
+SET lf.followed_user_type = 3, lf.followed_ref_id = lwu.id
+WHERE lf.followed_user_type IS NULL AND lf.followed_id LIKE 'lawyer\_%';
+
+-- life_user_dynamics.phone_id
+UPDATE life_user_dynamics lud
+    INNER JOIN life_user lu ON lud.phone_id LIKE 'user\_%' AND lu.user_phone = SUBSTRING(lud.phone_id, 6) AND lu.delete_flag = 0
+SET lud.phone_user_type = 1, lud.phone_ref_id = lu.id
+WHERE lud.phone_user_type IS NULL AND lud.phone_id LIKE 'user\_%';
+
+UPDATE life_user_dynamics lud
+    INNER JOIN store_user su ON lud.phone_id LIKE 'store\_%' AND su.phone = SUBSTRING(lud.phone_id, 7) AND su.delete_flag = 0
+SET lud.phone_user_type = 2, lud.phone_ref_id = su.id
+WHERE lud.phone_user_type IS NULL AND lud.phone_id LIKE 'store\_%';
+
+UPDATE life_user_dynamics lud
+    INNER JOIN lawyer_user lwu ON lud.phone_id LIKE 'lawyer\_%' AND lwu.phone = SUBSTRING(lud.phone_id, 8) AND lwu.delete_flag = 0
+SET lud.phone_user_type = 3, lud.phone_ref_id = lwu.id
+WHERE lud.phone_user_type IS NULL AND lud.phone_id LIKE 'lawyer\_%';
+
+-- life_message.sender_id / receiver_id
+UPDATE life_message lm
+    INNER JOIN life_user lu ON lm.sender_id LIKE 'user\_%' AND lu.user_phone = SUBSTRING(lm.sender_id, 6) AND lu.delete_flag = 0
+SET lm.sender_user_type = 1, lm.sender_ref_id = lu.id
+WHERE lm.sender_user_type IS NULL AND lm.sender_id LIKE 'user\_%';
+
+UPDATE life_message lm
+    INNER JOIN store_user su ON lm.sender_id LIKE 'store\_%' AND su.phone = SUBSTRING(lm.sender_id, 7) AND su.delete_flag = 0
+SET lm.sender_user_type = 2, lm.sender_ref_id = su.id
+WHERE lm.sender_user_type IS NULL AND lm.sender_id LIKE 'store\_%';
+
+UPDATE life_message lm
+    INNER JOIN lawyer_user lwu ON lm.sender_id LIKE 'lawyer\_%' AND lwu.phone = SUBSTRING(lm.sender_id, 8) AND lwu.delete_flag = 0
+SET lm.sender_user_type = 3, lm.sender_ref_id = lwu.id
+WHERE lm.sender_user_type IS NULL AND lm.sender_id LIKE 'lawyer\_%';
+
+UPDATE life_message lm
+    INNER JOIN life_user lu ON lm.receiver_id LIKE 'user\_%' AND lu.user_phone = SUBSTRING(lm.receiver_id, 6) AND lu.delete_flag = 0
+SET lm.receiver_user_type = 1, lm.receiver_ref_id = lu.id
+WHERE lm.receiver_user_type IS NULL AND lm.receiver_id LIKE 'user\_%';
+
+UPDATE life_message lm
+    INNER JOIN store_user su ON lm.receiver_id LIKE 'store\_%' AND su.phone = SUBSTRING(lm.receiver_id, 7) AND su.delete_flag = 0
+SET lm.receiver_user_type = 2, lm.receiver_ref_id = su.id
+WHERE lm.receiver_user_type IS NULL AND lm.receiver_id LIKE 'store\_%';
+
+UPDATE life_message lm
+    INNER JOIN lawyer_user lwu ON lm.receiver_id LIKE 'lawyer\_%' AND lwu.phone = SUBSTRING(lm.receiver_id, 8) AND lwu.delete_flag = 0
+SET lm.receiver_user_type = 3, lm.receiver_ref_id = lwu.id
+WHERE lm.receiver_user_type IS NULL AND lm.receiver_id LIKE 'lawyer\_%';
+
+-- life_notice.sender_id / receiver_id(system 等非身份键前缀跳过)
+UPDATE life_notice ln
+    INNER JOIN life_user lu ON ln.sender_id LIKE 'user\_%' AND lu.user_phone = SUBSTRING(ln.sender_id, 6) AND lu.delete_flag = 0
+SET ln.sender_user_type = 1, ln.sender_ref_id = lu.id
+WHERE ln.sender_user_type IS NULL AND ln.sender_id LIKE 'user\_%';
+
+UPDATE life_notice ln
+    INNER JOIN store_user su ON ln.sender_id LIKE 'store\_%' AND su.phone = SUBSTRING(ln.sender_id, 7) AND su.delete_flag = 0
+SET ln.sender_user_type = 2, ln.sender_ref_id = su.id
+WHERE ln.sender_user_type IS NULL AND ln.sender_id LIKE 'store\_%';
+
+UPDATE life_notice ln
+    INNER JOIN lawyer_user lwu ON ln.sender_id LIKE 'lawyer\_%' AND lwu.phone = SUBSTRING(ln.sender_id, 8) AND lwu.delete_flag = 0
+SET ln.sender_user_type = 3, ln.sender_ref_id = lwu.id
+WHERE ln.sender_user_type IS NULL AND ln.sender_id LIKE 'lawyer\_%';
+
+UPDATE life_notice ln
+    INNER JOIN life_user lu ON ln.receiver_id LIKE 'user\_%' AND lu.user_phone = SUBSTRING(ln.receiver_id, 6) AND lu.delete_flag = 0
+SET ln.receiver_user_type = 1, ln.receiver_ref_id = lu.id
+WHERE ln.receiver_user_type IS NULL AND ln.receiver_id LIKE 'user\_%';
+
+UPDATE life_notice ln
+    INNER JOIN store_user su ON ln.receiver_id LIKE 'store\_%' AND su.phone = SUBSTRING(ln.receiver_id, 7) AND su.delete_flag = 0
+SET ln.receiver_user_type = 2, ln.receiver_ref_id = su.id
+WHERE ln.receiver_user_type IS NULL AND ln.receiver_id LIKE 'store\_%';
+
+UPDATE life_notice ln
+    INNER JOIN lawyer_user lwu ON ln.receiver_id LIKE 'lawyer\_%' AND lwu.phone = SUBSTRING(ln.receiver_id, 8) AND lwu.delete_flag = 0
+SET ln.receiver_user_type = 3, ln.receiver_ref_id = lwu.id
+WHERE ln.receiver_user_type IS NULL AND ln.receiver_id LIKE 'lawyer\_%';
+
+-- life_like_record.dianzan_id
+UPDATE life_like_record llr
+    INNER JOIN life_user lu ON llr.dianzan_id LIKE 'user\_%' AND lu.user_phone = SUBSTRING(llr.dianzan_id, 6) AND lu.delete_flag = 0
+SET llr.dianzan_user_type = 1, llr.dianzan_ref_id = lu.id
+WHERE llr.dianzan_user_type IS NULL AND llr.dianzan_id LIKE 'user\_%';
+
+UPDATE life_like_record llr
+    INNER JOIN store_user su ON llr.dianzan_id LIKE 'store\_%' AND su.phone = SUBSTRING(llr.dianzan_id, 7) AND su.delete_flag = 0
+SET llr.dianzan_user_type = 2, llr.dianzan_ref_id = su.id
+WHERE llr.dianzan_user_type IS NULL AND llr.dianzan_id LIKE 'store\_%';
+
+-- store_clock_in.phone_id(当前业务多为 user_)
+UPDATE store_clock_in sci
+    INNER JOIN life_user lu ON sci.phone_id LIKE 'user\_%' AND lu.user_phone = SUBSTRING(sci.phone_id, 6) AND lu.delete_flag = 0
+SET sci.phone_user_type = 1, sci.phone_ref_id = lu.id
+WHERE sci.phone_user_type IS NULL AND sci.phone_id LIKE 'user\_%';
+
+UPDATE store_clock_in sci
+    INNER JOIN store_user su ON sci.phone_id LIKE 'store\_%' AND su.phone = SUBSTRING(sci.phone_id, 7) AND su.delete_flag = 0
+SET sci.phone_user_type = 2, sci.phone_ref_id = su.id
+WHERE sci.phone_user_type IS NULL AND sci.phone_id LIKE 'store\_%';
+
+-- life_collect.user_id(仅回填 user_{phone} 格式;纯数字历史 userId 需另行处理)
+UPDATE life_collect lc
+    INNER JOIN life_user lu ON lc.user_id LIKE 'user\_%' AND lu.user_phone = SUBSTRING(lc.user_id, 6) AND lu.delete_flag = 0
+SET lc.user_id_user_type = 1, lc.user_id_ref_id = lu.id
+WHERE lc.user_id_user_type IS NULL AND lc.user_id LIKE 'user\_%';
+
+UPDATE life_collect lc
+    INNER JOIN store_user su ON lc.user_id LIKE 'store\_%' AND su.phone = SUBSTRING(lc.user_id, 7) AND su.delete_flag = 0
+SET lc.user_id_user_type = 2, lc.user_id_ref_id = su.id
+WHERE lc.user_id_user_type IS NULL AND lc.user_id LIKE 'store\_%';

+ 12 - 0
alien-entity/src/main/resources/mapper/LawyerUserMapper.xml

@@ -138,6 +138,18 @@
         ORDER BY user.created_time DESC
     </select>
 
+    <!-- 中台-律师注销列表(含已逻辑删除记录) -->
+    <select id="selectLogoutPage" resultMap="BaseResultMap">
+        SELECT
+            id, phone, name, created_time, updated_time, logout_flag, logout_reason, logout_time, delete_flag, status
+        FROM lawyer_user
+        WHERE logout_flag IN (1, 2)
+        <if test="name != null and name != ''">AND name LIKE CONCAT('%', #{name}, '%')</if>
+        <if test="phone != null and phone != ''">AND phone LIKE CONCAT('%', #{phone}, '%')</if>
+        <if test="logoutFlag != null">AND logout_flag = #{logoutFlag}</if>
+        ORDER BY logout_time DESC, created_time DESC
+    </select>
+
     <!-- 查询律师列表总数 -->
     <select id="selectLawyerUserListCount" resultType="java.lang.Long">
         SELECT COUNT(DISTINCT user.id)

+ 4 - 3
alien-entity/src/main/resources/mapper/LifeDiscountCouponUserMapper.xml

@@ -41,9 +41,10 @@
                 AND u.status = 1
             </when>
             <when test='tabType != null &amp;&amp; tabType.equals("3")'>
-                AND u.expiration_time &gt; #{sevenDaysAgo}
-                AND u.expiration_time &lt; #{today}
-                AND u.status = 0
+                AND (
+                    (u.status = 0 AND u.expiration_time IS NOT NULL AND u.expiration_time &lt; #{today})
+                    OR u.status = 2
+                )
             </when>
         </choose>
         <if test="couponType != null">

+ 59 - 36
alien-entity/src/main/resources/mapper/LifeUserDynamicsMapper.xml

@@ -9,6 +9,8 @@
         ANY_VALUE(dyna1.dynamicsType) AS dynamicsType,
         ANY_VALUE(dyna1.title) AS title,
         ANY_VALUE(dyna1.phoneId) AS phoneId,
+        ANY_VALUE(dyna1.phoneUserType) AS phoneUserType,
+        ANY_VALUE(dyna1.phoneRefId) AS phoneRefId,
         ANY_VALUE(dyna1.context) AS context,
         ANY_VALUE(dyna1.image_path) AS image_path,
         ANY_VALUE(dyna1.address) AS address,
@@ -44,6 +46,8 @@
         ANY_VALUE(dyna.dynamicsType) AS dynamicsType,
         ANY_VALUE(dyna.title) AS title,
         ANY_VALUE(dyna.phoneId) AS phoneId,
+        ANY_VALUE(dyna.phoneUserType) AS phoneUserType,
+        ANY_VALUE(dyna.phoneRefId) AS phoneRefId,
         ANY_VALUE(dyna.context) AS context,
         ANY_VALUE(dyna.image_path) AS image_path,
         ANY_VALUE(dyna.address) AS address,
@@ -78,13 +82,21 @@
         WHEN image_path REGEXP '.jpg|.jpeg|.png|.bmp|.webp|.gif|.svg' THEN 1
         ELSE 0
         END AS dynamicsType,
-        id, title, phone_id phoneId, context, image_path, address,address_name,address_context,
+        id, title, phone_user_type phoneUserType, phone_ref_id phoneRefId,
+        CASE WHEN phone_user_type = 1 THEN CONCAT('user_', (SELECT user_phone FROM life_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1))
+        WHEN phone_user_type = 2 THEN CONCAT('store_', (SELECT phone FROM store_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1))
+        WHEN phone_user_type = 3 THEN CONCAT('lawyer_', (SELECT phone FROM lawyer_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1))
+        END AS phoneId,
+        context, image_path, address,address_name,address_context,
         liulan_count, dianzan_count, type, created_time,
-        SUBSTRING_INDEX(phone_id, '_', 1) userType,
-        SUBSTRING_INDEX(phone_id, '_', -1) phone,
+        CASE WHEN phone_user_type = 1 THEN 'user' WHEN phone_user_type = 2 THEN 'store' WHEN phone_user_type = 3 THEN 'lawyer' END userType,
+        CASE WHEN phone_user_type = 1 THEN (SELECT user_phone FROM life_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1)
+        WHEN phone_user_type = 2 THEN (SELECT phone FROM store_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1)
+        WHEN phone_user_type = 3 THEN (SELECT phone FROM lawyer_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1)
+        END phone,
         draft , address_province, top_status, top_time, enable_status, transfer_count
         FROM life_user_dynamics
-        WHERE delete_flag = 0 AND draft = 0
+        WHERE delete_flag = 0 AND draft = 0 AND phone_user_type IS NOT NULL AND phone_ref_id IS NOT NULL
         )
         SELECT
         dynamice.*,
@@ -92,10 +104,10 @@
         info.id storeUserId, user.id storeOrUserId,
         0 isExpert, info.store_name
         FROM dynamice
-        LEFT JOIN store_user user ON dynamice.phone = user.phone AND user.delete_flag = 0
+        LEFT JOIN store_user user ON user.delete_flag = 0 AND dynamice.phoneUserType = 2 AND user.id = dynamice.phoneRefId
         LEFT JOIN store_info info ON info.id = user.store_id AND info.delete_flag = 0
         LEFT JOIN store_img img ON img.store_id = user.store_id AND img.img_type = '10' AND img.delete_flag = 0
-        WHERE dynamice.userType = 'store'
+        WHERE dynamice.phoneUserType = 2
         UNION
         SELECT
         dynamice.*,
@@ -104,9 +116,9 @@
         IF(lue.expert_code IS NOT NULL , 1, 0) AS isExpert,
         '' store_name
         FROM dynamice
-        JOIN life_user user ON dynamice.phone = user.user_phone AND user.delete_flag = 0
+        JOIN life_user user ON user.delete_flag = 0 AND dynamice.phoneUserType = 1 AND user.id = dynamice.phoneRefId
         LEFT JOIN life_user_expert  lue ON lue.user_id = user.id AND lue.delete_flag = 0
-        WHERE dynamice.userType = 'user'
+        WHERE dynamice.phoneUserType = 1
         ) dyna
         LEFT JOIN life_comment lc ON lc.dongtai_shequ_id = dyna.id
         LEFT JOIN store_comment sc ON sc.business_id = dyna.id AND sc.business_type = 2 AND sc.delete_flag = 0
@@ -114,7 +126,7 @@
         -- 4. GROUP BY 仅保留主键 id(因 dyna.id 是唯一主键,ANY_VALUE() 包裹的列在同一 id 下值唯一)
         GROUP BY dyna.id
         ) dyna1
-        LEFT JOIN life_fans lf1 ON lf1.followed_id = dyna1.phoneId AND lf1.delete_flag = 0
+        LEFT JOIN life_fans lf1 ON lf1.followed_user_type = dyna1.phoneUserType AND lf1.followed_ref_id = dyna1.phoneRefId AND lf1.delete_flag = 0
         WHERE 1=1
         <if test="nickName != null and nickName != ''">
             AND dyna1.userName LIKE CONCAT('%', #{nickName}, '%')
@@ -149,27 +161,38 @@
         WHEN image_path REGEXP '.mp4|.avi|.flv|.mkv|.rmvb|.wmv|.3gp|.mov' THEN 2
         WHEN image_path REGEXP '.jpg|.jpeg|.png|.bmp|.webp|.gif|.svg' THEN 1
         ELSE 0
-        END AS dynamicsType, id, title, phone_id phoneId, context, image_path, address,address_name,address_context, liulan_count, dianzan_count, type, created_time, substring_index(phone_id, '_', 1) userType, substring_index(phone_id, '_', -1) phone, draft , address_province, top_status, top_time, enable_status,transfer_count AS transferCount
+        END AS dynamicsType, id, title, phone_user_type phoneUserType, phone_ref_id phoneRefId,
+        CASE WHEN phone_user_type = 1 THEN CONCAT('user_', (SELECT user_phone FROM life_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1))
+        WHEN phone_user_type = 2 THEN CONCAT('store_', (SELECT phone FROM store_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1))
+        WHEN phone_user_type = 3 THEN CONCAT('lawyer_', (SELECT phone FROM lawyer_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1))
+        END AS phoneId,
+        context, image_path, address,address_name,address_context, liulan_count, dianzan_count, type, created_time,
+        CASE WHEN phone_user_type = 1 THEN 'user' WHEN phone_user_type = 2 THEN 'store' WHEN phone_user_type = 3 THEN 'lawyer' END userType,
+        CASE WHEN phone_user_type = 1 THEN (SELECT user_phone FROM life_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1)
+        WHEN phone_user_type = 2 THEN (SELECT phone FROM store_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1)
+        WHEN phone_user_type = 3 THEN (SELECT phone FROM lawyer_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1)
+        END phone,
+        draft , address_province, top_status, top_time, enable_status,transfer_count AS transferCount
         from life_user_dynamics
-        where id = #{id} and delete_flag = 0 and draft = 0 order by created_time desc
+        where id = #{id} and delete_flag = 0 and draft = 0 and phone_user_type is not null and phone_ref_id is not null order by created_time desc
         )
         select dynamice.*, user.nick_name userName, user.head_img userImage, info.id storeUserId, user.id storeOrUserId, 0 isExpert
         from dynamice
-        left join store_user user on dynamice.phone = user.phone and user.delete_flag = 0
+        left join store_user user on user.delete_flag = 0 and dynamice.phoneUserType = 2 and user.id = dynamice.phoneRefId
         left join store_info info on info.id = user.store_id and info.delete_flag = 0
         left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0
-        where dynamice.userType = 'store'
+        where dynamice.phoneUserType = 2
         union
         select dynamice.*, user.user_name userName, user.user_image userImage, user.id storeUserId, user.id storeOrUserId, IF(lue.expert_code IS NOT NULL , 1, 0) AS isExpert
         from dynamice
-        join life_user user on dynamice.phone = user.user_phone and user.delete_flag = 0
+        join life_user user on user.delete_flag = 0 and dynamice.phoneUserType = 1 and user.id = dynamice.phoneRefId
         left join life_user_expert  lue on lue.user_id = user.id and lue.delete_flag = 0
-        where dynamice.userType = 'user') dyna
+        where dynamice.phoneUserType = 1) dyna
         left join life_comment lc on lc.dongtai_shequ_id = dyna.id
         left join store_comment sc on sc.business_id = dyna.id and sc.business_type = 2 and sc.delete_flag = 0
         left join life_message lm on lm.business_id = dyna.id
         GROUP BY dyna.id order by  dyna.created_time desc) dyna1
-        left join life_fans lf1 on lf1.followed_id = dyna1.phoneId and lf1.delete_flag = 0
+        left join life_fans lf1 on lf1.followed_user_type = dyna1.phoneUserType and lf1.followed_ref_id = dyna1.phoneRefId and lf1.delete_flag = 0
         GROUP by dyna1.id order by dyna1.top_status desc, dyna1.top_time desc
     </select>
 
@@ -213,6 +236,8 @@
         dyna.id,
         ANY_VALUE(dyna.title) AS title,
         ANY_VALUE(dyna.phoneId) AS phoneId,
+        ANY_VALUE(dyna.phoneUserType) AS phoneUserType,
+        ANY_VALUE(dyna.phoneRefId) AS phoneRefId,
         ANY_VALUE(dyna.context) AS context,
         ANY_VALUE(dyna.image_path) AS image_path,
         ANY_VALUE(dyna.address) AS address,
@@ -247,10 +272,21 @@
         WHEN image_path REGEXP '.mp4|.avi|.flv|.mkv|.rmvb|.wmv|.3gp|.mov' THEN 2
         WHEN image_path REGEXP '.jpg|.jpeg|.png|.bmp|.webp|.gif|.svg' THEN 1
         ELSE 0
-        END AS dynamicsType, id, title, phone_id phoneId, context, image_path, address, address_name, address_context, liulan_count, dianzan_count, type, created_time, substring_index(phone_id, '_', 1) userType, substring_index(phone_id, '_', -1) phone, draft , address_province, top_status, top_time, enable_status,
+        END AS dynamicsType, id, title, phone_user_type phoneUserType, phone_ref_id phoneRefId,
+        CASE WHEN phone_user_type = 1 THEN CONCAT('user_', (SELECT user_phone FROM life_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1))
+        WHEN phone_user_type = 2 THEN CONCAT('store_', (SELECT phone FROM store_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1))
+        WHEN phone_user_type = 3 THEN CONCAT('lawyer_', (SELECT phone FROM lawyer_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1))
+        END AS phoneId,
+        context, image_path, address, address_name, address_context, liulan_count, dianzan_count, type, created_time,
+        CASE WHEN phone_user_type = 1 THEN 'user' WHEN phone_user_type = 2 THEN 'store' WHEN phone_user_type = 3 THEN 'lawyer' END userType,
+        CASE WHEN phone_user_type = 1 THEN (SELECT user_phone FROM life_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1)
+        WHEN phone_user_type = 2 THEN (SELECT phone FROM store_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1)
+        WHEN phone_user_type = 3 THEN (SELECT phone FROM lawyer_user WHERE id = phone_ref_id AND delete_flag = 0 LIMIT 1)
+        END phone,
+        draft , address_province, top_status, top_time, enable_status,
         business_id
         from life_user_dynamics
-        where phone_id = #{phoneId} and delete_flag = 0 and draft = 0 order by created_time desc
+        where phone_user_type = #{phoneUserType} and phone_ref_id = #{phoneRefId} and delete_flag = 0 and draft = 0 order by created_time desc
         )
         select
         dynamice.*,
@@ -264,8 +300,7 @@
         from
         dynamice
         left join store_user user on
-        dynamice.phone = user.phone
-        and user.delete_flag = 0
+        user.delete_flag = 0 and dynamice.phoneUserType = 2 and user.id = dynamice.phoneRefId
         left join store_info info on
         info.id = user.store_id
         and info.delete_flag = 0
@@ -276,25 +311,13 @@
         left join life_like_record llr on
         llr.huifu_id = dynamice.id
         and llr.delete_flag = 0
-        and llr.dianzan_id = (
-        select
-        CONCAT('user_', lu1.user_phone)
-        from
-        life_user lu1
-        where
-        lu1.id = #{userId})
+        and llr.dianzan_user_type = 1 and llr.dianzan_ref_id = #{userId})
         left join life_like_record llr1 on
         llr1.huifu_id = dynamice.id
         and llr1.delete_flag = 0
-        and llr1.dianzan_id = (
-        select
-        CONCAT('store_', lu2.phone)
-        from
-        store_user lu2
-        where
-        lu2.id = #{userId})
+        and llr1.dianzan_user_type = 2 and llr1.dianzan_ref_id = #{userId})
         where
-        dynamice.userType = 'store') dyna
+        dynamice.phoneUserType = 2) dyna
         left join life_comment lc on
         lc.dongtai_shequ_id = dyna.id
         left join store_comment sc on
@@ -308,7 +331,7 @@
         order by
         dyna.created_time desc) dyna1
         left join life_fans lf1 on
-        lf1.followed_id = dyna1.phoneId and lf1.delete_flag = 0
+        lf1.followed_user_type = dyna1.phoneUserType and lf1.followed_ref_id = dyna1.phoneRefId and lf1.delete_flag = 0
         GROUP by
         dyna1.id
         order by

+ 4 - 4
alien-entity/src/main/resources/mapper/OrderReviewMapper.xml

@@ -67,7 +67,7 @@
         LEFT JOIN law_firm lf ON lf.id = lu2.firm_id AND lf.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(orv.id, CHAR)
             AND llr.type = '7' 
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         LEFT JOIN comment_appeals ca ON ca.comment_id = orv.id AND ca.delete_flag = 0
         WHERE orv.delete_flag = 0
@@ -125,7 +125,7 @@
         LEFT JOIN law_firm lf ON lf.id = lu2.firm_id AND lf.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(orv.id, CHAR)
             AND llr.type = '7' 
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         WHERE orv.delete_flag = 0
         AND orv.order_id = #{orderId}
@@ -178,7 +178,7 @@
         LEFT JOIN law_firm lf ON lf.id = lu2.firm_id AND lf.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(orv.id, CHAR)
             AND llr.type = '7' 
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         WHERE orv.delete_flag = 0
         AND orv.id = #{reviewId}
@@ -353,7 +353,7 @@
         LEFT JOIN law_firm lf ON lf.id = lu2.firm_id AND lf.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(orv.id, CHAR)
             AND llr.type = '7'
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         WHERE orv.delete_flag = 0
         AND orv.lawyer_user_id = #{lawyerUserId}

+ 2 - 2
alien-entity/src/main/resources/mapper/PlatformLifeUserMapper.xml

@@ -30,7 +30,7 @@
         IFNULL(comment.bad_num, 0) AS bad_num,
 --         (
 --         IFNULL((SELECT SUM(like_count) FROM store_comment WHERE user_id = user.id), 0) +
-        IFNULL((SELECT SUM(dianzan_count) FROM life_user_dynamics WHERE phone_id = phoneId), 0)
+        IFNULL((SELECT SUM(dianzan_count) FROM life_user_dynamics WHERE phone_user_type = 1 AND phone_ref_id = user.id), 0)
 --         )
             AS likesNumber,
         COUNT(lf.id) AS fans_count,
@@ -40,7 +40,7 @@
         FROM life_user user
         LEFT JOIN orders ON orders.user_id = user.id
         LEFT JOIN comment ON comment.user_id = user.id
-        LEFT JOIN life_fans lf ON lf.followed_id = CONCAT('user_', user.user_phone) and lf.delete_flag = 0
+        LEFT JOIN life_fans lf ON lf.followed_user_type = 1 AND lf.followed_ref_id = user.id AND lf.delete_flag = 0
         LEFT JOIN life_user_expert lue ON lue.user_id = user.id and lue.delete_flag = 0
         LEFT JOIN second_user_credit suc ON suc.user_id = user.id and suc.delete_flag = 0
         <!-- 动态 SQL 片段,用于拼接查询条件 -->

+ 2 - 2
alien-entity/src/main/resources/mapper/ReviewCommentMapper.xml

@@ -88,7 +88,7 @@
             AND lu_lawyer_comment.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(rc.id, CHAR)
             AND llr.type = '8' 
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         WHERE rc.delete_flag = 0
         AND rc.review_id = #{reviewId}
@@ -160,7 +160,7 @@
             AND lu_lawyer_comment.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(rc.id, CHAR)
             AND llr.type = '8' 
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         WHERE rc.delete_flag = 0
         AND rc.head_id = #{headId}

+ 2 - 2
alien-entity/src/main/resources/mapper/StoreStaffCommentMapper.xml

@@ -81,7 +81,7 @@
             AND ssc_receive.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(ssc.id, CHAR)
             AND llr.type = '10' 
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         WHERE ssc.delete_flag = 0
         AND ssc.review_id = #{reviewId}
@@ -147,7 +147,7 @@
             AND ssc_receive.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(ssc.id, CHAR)
             AND llr.type = '10' 
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         WHERE ssc.delete_flag = 0
         AND ssc.head_id = #{headId}

+ 3 - 3
alien-entity/src/main/resources/mapper/StoreStaffReviewMapper.xml

@@ -62,7 +62,7 @@
         LEFT JOIN store_img si ON si.id = sp.img_id AND si.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(ssr.id, CHAR)
             AND llr.type = '9' 
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         WHERE ssr.delete_flag = 0
           AND ssr.audit_status = 1
@@ -112,7 +112,7 @@
         LEFT JOIN store_img si ON si.id = sp.img_id AND si.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(ssr.id, CHAR)
             AND llr.type = '9' 
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         WHERE ssr.delete_flag = 0
           AND ssr.audit_status = 1
@@ -237,7 +237,7 @@
         LEFT JOIN store_img si ON si.id = sp.img_id AND si.delete_flag = 0
         LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(ssr.id, CHAR)
             AND llr.type = '9' 
-            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.dianzan_user_type = 1 AND llr.dianzan_ref_id = #{currentUserId}
             AND llr.delete_flag = 0
         WHERE ssr.delete_flag = 0
         AND ssr.audit_status = 1

+ 11 - 11
alien-entity/src/main/resources/mapper/second/SecondGoodsInfoMapper.xml

@@ -74,7 +74,7 @@
                 CONCAT('user_', u.user_phone) as user_phone
             FROM
                 second_goods g inner join life_user u on u.id =  g.user_id and u.delete_flag = 0
-                left join life_like_record llr on llr.dianzan_id = #{phoneId} and llr.huifu_id = g.id and llr.type = 6 and llr.delete_flag = 0
+                left join life_like_record llr on llr.dianzan_user_type = 1 and llr.dianzan_ref_id = (select id from life_user where concat('user_', user_phone) = #{phoneId} and delete_flag = 0 limit 1) and llr.huifu_id = g.id and llr.type = 6 and llr.delete_flag = 0
             where g.delete_flag = 0
                 <if test="typeId != null and typeId != '' ">
                     and (g.category_one_id = #{typeId} or g.category_two_id =#{typeId} )
@@ -126,15 +126,15 @@
 <!--            (SELECT count(1) FROM store_comment c where c.business_id = g.id and c.business_type = 7 and c.delete_flag = 0 ) as commentCount-->
         from
             life_fans f inner join life_user u on
-                f.followed_id = CONCAT('user_', u.user_phone) and u.delete_flag = 0
+                f.followed_user_type = 1 and f.followed_ref_id = u.id and u.delete_flag = 0
             inner join second_goods g on
                 u.id = g.user_id
                 and g.goods_status = 3
                 and g.delete_flag = 0
-            left join life_like_record llr on llr.dianzan_id = #{phoneId} and llr.huifu_id = g.id and llr.type = 6 and llr.delete_flag = 0
-            left join life_collect lc on lc.business_id = g.id and lc.user_id = #{phoneId} and lc.delete_flag = 0 and lc.business_type = 1
+            left join life_like_record llr on llr.dianzan_user_type = 1 and llr.dianzan_ref_id = (select id from life_user where concat('user_', user_phone) = #{phoneId} and delete_flag = 0 limit 1) and llr.huifu_id = g.id and llr.type = 6 and llr.delete_flag = 0
+            left join life_collect lc on lc.business_id = g.id and lc.user_id_user_type = #{collectUserType} and lc.user_id_ref_id = #{collectRefId} and lc.delete_flag = 0 and lc.business_type = 1
         where
-            f.fans_id = #{phoneId} and f.delete_flag = 0
+            f.fans_user_type = #{fansUserType} and f.fans_ref_id = #{fansRefId} and f.delete_flag = 0
             and not exists (select 1 from shieldUser s where s.id = g.id)
             and not exists (select 1 from second_shield s where s.user_id = #{userId} and s.shield_type = 1 and s.shield_id = g.id and s.delete_flag = 0)
             and g.position != '' and g.position is not null
@@ -211,8 +211,8 @@
     <!--            (SELECT count(1) FROM store_comment c where c.business_id = g.id and c.business_type = 7 and c.delete_flag = 0 ) as commentCount-->
             FROM
                 second_goods g inner join life_user u on u.id = g.user_id
-                left join life_like_record llr on llr.dianzan_id = #{phoneId} and llr.huifu_id = g.id and llr.type = 6 and llr.delete_flag = 0
-                left join life_collect lc on lc.business_id = g.id and lc.user_id = #{phoneId} and lc.delete_flag = 0 and lc.business_type = 1
+                left join life_like_record llr on llr.dianzan_user_type = 1 and llr.dianzan_ref_id = (select id from life_user where concat('user_', user_phone) = #{phoneId} and delete_flag = 0 limit 1) and llr.huifu_id = g.id and llr.type = 6 and llr.delete_flag = 0
+                left join life_collect lc on lc.business_id = g.id and lc.user_id_user_type = #{collectUserType} and lc.user_id_ref_id = #{collectRefId} and lc.delete_flag = 0 and lc.business_type = 1
             where g.delete_flag = 0
                 and not exists (select 1 from shieldUser s where s.id = g.id)
                 and not exists (select 1 from second_shield s where s.user_id = #{userId} and s.shield_type = 1 and s.shield_id = g.id and s.delete_flag = 0)
@@ -261,8 +261,8 @@
             case when llr.id is null then '0' else '1' end likeStatus,
             case when lc.id is null then '0' else '1' end collectStatus
         from second_goods g inner join life_user lu on g.user_id = lu.id and lu.delete_flag = 0
-            left join life_like_record llr on llr.dianzan_id = #{phoneId} and llr.huifu_id = g.id and llr.type = 6 and llr.delete_flag = 0
-            left join life_collect lc on lc.business_id = g.id and lc.user_id = #{phoneId} and lc.delete_flag = 0 and lc.business_type = 1
+            left join life_like_record llr on llr.dianzan_user_type = 1 and llr.dianzan_ref_id = (select id from life_user where concat('user_', user_phone) = #{phoneId} and delete_flag = 0 limit 1) and llr.huifu_id = g.id and llr.type = 6 and llr.delete_flag = 0
+            left join life_collect lc on lc.business_id = g.id and lc.user_id_user_type = #{collectUserType} and lc.user_id_ref_id = #{collectRefId} and lc.delete_flag = 0 and lc.business_type = 1
         where g.id = #{goodsId} and g.delete_flag = 0
             and g.position != '' and g.position is not null
     </select>
@@ -292,8 +292,8 @@
             case when llr.id is null then '0' else '1' end likeStatus,
             case when lc.id is null then '0' else '1' end collectStatus
         from second_goods g inner join life_user lu on g.user_id = lu.id and lu.delete_flag = 0
-            left join life_like_record llr on llr.dianzan_id = #{phoneId} and llr.huifu_id = g.id and llr.type = 6 and llr.delete_flag = 0
-            left join life_collect lc on lc.business_id = g.id and lc.user_id = #{phoneId} and lc.delete_flag = 0 and lc.business_type = 1
+            left join life_like_record llr on llr.dianzan_user_type = 1 and llr.dianzan_ref_id = (select id from life_user where concat('user_', user_phone) = #{phoneId} and delete_flag = 0 limit 1) and llr.huifu_id = g.id and llr.type = 6 and llr.delete_flag = 0
+            left join life_collect lc on lc.business_id = g.id and lc.user_id_user_type = #{collectUserType} and lc.user_id_ref_id = #{collectRefId} and lc.delete_flag = 0 and lc.business_type = 1
         where g.id = #{goodsId} and g.delete_flag = 0
             and g.position != '' and g.position is not null
     </select>

+ 9 - 3
alien-gateway/src/main/java/shop/alien/gateway/config/JwtTokenFilter.java

@@ -162,6 +162,12 @@ public class JwtTokenFilter implements GlobalFilter, Ordered {
                             //别问, 问就是约定俗成
                             map.put("code", 777);
                         }
+                        if (storeUser != null && storeUser.getLogoutFlag() != null
+                                && StoreUser.LOGOUT_FLAG_DONE == storeUser.getLogoutFlag()
+                                && !Integer.valueOf(-1).equals(storeUser.getStatus())) {
+                            map.put("msg", "你的账号已注销");
+                            map.put("code", 777);
+                        }
                         if (!token.equals(redisVal)) {
                             map.put("msg", "账号在别处登录");
                             //别问, 问就是约定俗成
@@ -190,10 +196,10 @@ public class JwtTokenFilter implements GlobalFilter, Ordered {
                             map.put("code", 666);
                         }
                     } else if ("lawyer".equals(deviceType)) {
-                        //判断程序是否为用户禁用
                         LawyerUser lawyerUser = lawyerUserMapper.selectOne(new LambdaQueryWrapper<LawyerUser>().eq(LawyerUser::getPhone, phone));
-                        //注销标记, 0:未注销, 1:已注销
-                        if (lawyerUser != null && null != lawyerUser.getLogoutFlag() && lawyerUser.getLogoutFlag() == 1) {
+                        if (lawyerUser != null && lawyerUser.getLogoutFlag() != null
+                                && LawyerUser.LOGOUT_FLAG_DONE == lawyerUser.getLogoutFlag()
+                                && (lawyerUser.getDeleteFlag() == null || lawyerUser.getDeleteFlag() == 0)) {
                             map.put("msg", "你的账号已注销");
                             map.put("code", 777);
                         }

+ 4 - 0
alien-gateway/src/main/java/shop/alien/gateway/controller/LifeUserController.java

@@ -41,6 +41,10 @@ public class LifeUserController {
                                    @RequestParam(value = "macIp", required = false) String macIp,
                                    @RequestParam(value = "inviteCode", required = false) String inviteCode) {
         log.info("LifeUserController.userLogin?phoneNum={}&code={}", phoneNum, code);
+        String reRegisterBlockedMessage = lifeUserService.getReRegisterBlockedMessage(phoneNum);
+        if (reRegisterBlockedMessage != null) {
+            return R.fail(reRegisterBlockedMessage);
+        }
         LifeUserVo userVo = lifeUserService.userLogin(phoneNum, inviteCode, macIp);
         if (null == userVo) {
             return R.fail("登录失败");

+ 4 - 0
alien-gateway/src/main/java/shop/alien/gateway/controller/StoreUserController.java

@@ -69,6 +69,10 @@ public class StoreUserController {
         if (storeUser.getStatus() == 1) {
             return R.fail("账号被禁用");
         }
+//        if (storeUser.getLogoutFlag() != null && StoreUser.LOGOUT_FLAG_DONE == storeUser.getLogoutFlag()
+//                && !Integer.valueOf(-1).equals(storeUser.getStatus())) {
+//            return R.fail("账号已注销");
+//        }
         return Optional.ofNullable(storeUser).
                 map(user -> isPassword ? checkPassword(user, password) : storeUserService.createToKen(user)).
                 orElseGet(() -> R.fail("手机号不存在"));

+ 8 - 0
alien-gateway/src/main/java/shop/alien/gateway/mapper/LifeUserGatewayMapper.java

@@ -2,6 +2,8 @@ package shop.alien.gateway.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import shop.alien.entity.store.LifeUser;
 
 /**
@@ -10,4 +12,10 @@ import shop.alien.entity.store.LifeUser;
 @Mapper
 public interface LifeUserGatewayMapper extends BaseMapper<LifeUser> {
 
+    /**
+     * 查询已逻辑删除且已注销的用户(绕过 {@link LifeUser#deleteFlag} 的 TableLogic 过滤)。
+     */
+    @Select("SELECT * FROM life_user WHERE user_phone = #{phoneNum} AND delete_flag = 1 AND logout_flag = 1 LIMIT 1")
+    LifeUser selectCancelledDeletedByPhone(@Param("phoneNum") String phoneNum);
+
 }

+ 2 - 0
alien-gateway/src/main/java/shop/alien/gateway/service/LifeUserPasswordService.java

@@ -102,6 +102,8 @@ public class LifeUserPasswordService {
         userVo.setToken(token);
         addSessionToken(phoneNum, token);
         lifeUserService.addLifeUserLogInfo(user, macIp);
+        userVo.setHasLoginPassword(StringUtils.isNotBlank(user.getPassword()));
+        lifeUserService.fillLogoutEndTime(userVo, user);
         return userVo;
     }
 

+ 79 - 0
alien-gateway/src/main/java/shop/alien/gateway/service/LifeUserService.java

@@ -2,6 +2,7 @@ package shop.alien.gateway.service;
 
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import org.apache.commons.lang3.StringUtils;
@@ -32,6 +33,7 @@ import shop.alien.mapper.second.SecondUserCreditRecordMapper;
 import shop.alien.util.common.JwtUtil;
 
 import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -69,6 +71,78 @@ public class LifeUserService extends ServiceImpl<LifeUserGatewayMapper, LifeUser
     @Value("${jwt.expiration-time}")
     private String effectiveTime;
 
+    private static final DateTimeFormatter LOGOUT_END_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy/MM/dd");
+    private static final DateTimeFormatter RE_REGISTER_ALLOW_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
+
+    /**
+     * 申请注销结束时间:logoutTime 起算第 7 天(T+0~T+7),与定时任务冷静期一致。
+     */
+    public static String resolveLogoutEndTime(Date logoutTime, Integer logoutFlag) {
+        if (logoutTime == null || logoutFlag == null || logoutFlag == 0) {
+            return null;
+        }
+        return logoutTime.toInstant()
+                .atZone(ZoneId.systemDefault())
+                .toLocalDate()
+                .plusDays(7)
+                .format(LOGOUT_END_TIME_FORMAT);
+    }
+
+    public void fillLogoutEndTime(LifeUserVo userVo, LifeUser user) {
+        if (userVo == null || user == null) {
+            return;
+        }
+        userVo.setLogoutEndTime(resolveLogoutEndTime(user.getLogoutTime(), user.getLogoutFlag()));
+    }
+
+    /**
+     * 注销完成日 T(logoutTime)起,至 T+{@link LifeUser#RE_REGISTER_BLOCK_DAYS} 日 00:00 前禁止重新注册/登录。
+     */
+    public static boolean isWithinReRegisterBlockPeriod(Date logoutTime) {
+        if (logoutTime == null) {
+            return false;
+        }
+        ZoneId zone = ZoneId.systemDefault();
+        LocalDateTime allowFrom = logoutTime.toInstant()
+                .atZone(zone)
+                .toLocalDate()
+                .plusDays(LifeUser.RE_REGISTER_BLOCK_DAYS)
+                .atStartOfDay();
+        return LocalDateTime.now(zone).isBefore(allowFrom);
+    }
+
+    /**
+     * 可重新注册时间:logoutTime 对应日 T 的 T+14 日 00:00。
+     */
+    public static String resolveReRegisterAllowTime(Date logoutTime) {
+        if (logoutTime == null) {
+            return null;
+        }
+        return logoutTime.toInstant()
+                .atZone(ZoneId.systemDefault())
+                .toLocalDate()
+                .plusDays(LifeUser.RE_REGISTER_BLOCK_DAYS)
+                .atStartOfDay()
+                .format(RE_REGISTER_ALLOW_TIME_FORMAT);
+    }
+
+    /**
+     * 手机号处于注销禁登期时返回完整提示,否则返回 null。
+     */
+    public String getReRegisterBlockedMessage(String phoneNum) {
+        LifeUser user = getUserByPhoneDelete(phoneNum);
+        if (user == null
+                || LifeUser.LOGOUT_FLAG_DONE != user.getLogoutFlag()
+                || !isWithinReRegisterBlockPeriod(user.getLogoutTime())) {
+            return null;
+        }
+        String allowTime = resolveReRegisterAllowTime(user.getLogoutTime());
+        if (allowTime == null) {
+            return null;
+        }
+        return "原手机号无法注册新账号," + allowTime + "起,原手机号可重新注册";
+    }
+
     public LifeUserVo userLogin(String phoneNum, String inviteCode, String macIp) {
         LifeUser user = getUserByPhone(phoneNum);
         if (user == null) {
@@ -94,6 +168,7 @@ public class LifeUserService extends ServiceImpl<LifeUserGatewayMapper, LifeUser
                 // 二手平台登录log,同一个macip登录多账号记录
                 addLifeUserLogInfo(user2, macIp);
                 userVo.setHasLoginPassword(StringUtils.isNotBlank(user2.getPassword()));
+                fillLogoutEndTime(userVo, user2);
 
                 return userVo;
             } else {
@@ -113,6 +188,7 @@ public class LifeUserService extends ServiceImpl<LifeUserGatewayMapper, LifeUser
             // 二手平台登录log,同一个macip登录多账号记录
             addLifeUserLogInfo(user, macIp);
             userVo.setHasLoginPassword(StringUtils.isNotBlank(user.getPassword()));
+            fillLogoutEndTime(userVo, user);
 
             return userVo;
         }
@@ -164,6 +240,9 @@ public class LifeUserService extends ServiceImpl<LifeUserGatewayMapper, LifeUser
         return this.getOne(lambdaQueryWrapper);
     }
 
+    public LifeUser getUserByPhoneDelete(String phoneNum) {
+        return lifeUserMapper.selectCancelledDeletedByPhone(phoneNum);
+    }
     public R<String> saveDeviceId(Integer id, String deviceId) {
         if (id == null || id <= 0) {
             return R.fail("用户id不能为空");

+ 5 - 0
alien-gateway/src/main/java/shop/alien/gateway/service/impl/StoreUserServiceImpl.java

@@ -236,6 +236,11 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserGatewayMapper, St
             log.warn("账号被禁用 - userId: {}", userId);
             return R.fail("账号被禁用");
         }
+        if (storeUser.getLogoutFlag() != null && StoreUser.LOGOUT_FLAG_DONE == storeUser.getLogoutFlag()
+                && !Integer.valueOf(-1).equals(storeUser.getStatus())) {
+            log.warn("账号已注销 - userId: {}", userId);
+            return R.fail("账号已注销");
+        }
 
         // 3. 删除旧的token(参考switchingStates逻辑)
         baseRedisService.delete("store_" + storeUser.getPhone());

+ 4 - 0
alien-job/src/main/java/shop/alien/job/second/AiCheckXxlJob.java

@@ -28,6 +28,7 @@ import shop.alien.mapper.*;
 import shop.alien.mapper.second.SecondGoodsRecordMapper;
 import shop.alien.mapper.second.SecondRiskControlRecordMapper;
 import shop.alien.util.common.Constants;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.net.SocketException;
 import java.util.*;
@@ -86,6 +87,8 @@ public class AiCheckXxlJob {
 
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     /**
      * AI自动审核任务处理器
      * <p>
@@ -312,6 +315,7 @@ public class AiCheckXxlJob {
                                         lifeMessage.setSenderId("system");
                                         lifeMessage.setIsRead(0);
                                         lifeMessage.setNoticeType(1);
+                                        lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
                                         lifeNoticeMapper.insert(lifeMessage);
                                     }
                                 }

+ 4 - 0
alien-job/src/main/java/shop/alien/job/second/AiProductComplaintJob.java

@@ -20,6 +20,7 @@ import shop.alien.entity.store.LifeNotice;
 import shop.alien.entity.store.LifeUser;
 import shop.alien.entity.store.LifeUserViolation;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.LifeUserViolationMapper;
 
@@ -45,6 +46,8 @@ public class AiProductComplaintJob {
     private final LifeUserMapper lifeUserMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     @Value("${third-party-user-name-admin.base-url}")
     private String userName;
 
@@ -181,6 +184,7 @@ public class AiProductComplaintJob {
                     lifeMessage.setSenderId("system");
                     lifeMessage.setIsRead(0);
                     lifeMessage.setNoticeType(1);
+                    lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
                     lifeNoticeMapper.insert(lifeMessage);
                 }
 

+ 4 - 0
alien-job/src/main/java/shop/alien/job/second/AiUserViolationJob.java

@@ -23,6 +23,7 @@ import shop.alien.entity.second.SecondUserCredit;
 import shop.alien.entity.second.SecondUserCreditRecord;
 import shop.alien.entity.store.*;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.LifeUserViolationMapper;
 import shop.alien.mapper.second.SecondUserCreditMapper;
@@ -43,6 +44,8 @@ public class AiUserViolationJob {
     private final LifeUserMapper lifeUserMapper;
 
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final SecondUserCreditMapper secondUserCreditMapper;
     private final SecondUserCreditRecordMapper secondUserCreditRecordMapper;
 
@@ -193,6 +196,7 @@ public class AiUserViolationJob {
                     lifeMessage.setSenderId("system");
                     lifeMessage.setIsRead(0);
                     lifeMessage.setNoticeType(1);
+                    lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
                     lifeNoticeMapper.insert(lifeMessage);
 
                     QueryWrapper<LifeUserViolation> queryWrapper = new QueryWrapper<>();

+ 12 - 0
alien-job/src/main/java/shop/alien/job/second/SecondGoodsTradeXxlJob.java

@@ -20,6 +20,8 @@ import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.job.feign.AlienStoreFeign;
 import shop.alien.mapper.LifeMessageMapper;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeMessageUtil;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.second.SecondGoodsMapper;
 import shop.alien.mapper.second.SecondTradeOperationMapper;
@@ -41,6 +43,10 @@ public class SecondGoodsTradeXxlJob {
 
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
+    private final LifeMessageUtil lifeMessageUtil;
+
     private final LifeUserMapper lifeUserMapper;
 
     private final LifeMessageMapper lifeMessageMapper;
@@ -91,6 +97,7 @@ public class SecondGoodsTradeXxlJob {
                 lifeMessage.setReceiverId(buyerPhoneId);
                 lifeMessage.setContent(message.toJSONString());
                 lifeMessage.setType("5");
+                lifeMessageUtil.fillUserTypeAndRefId(lifeMessage);
                 lifeMessageMapper.insert(lifeMessage);
 
                 // 给买家推送消息
@@ -119,6 +126,7 @@ public class SecondGoodsTradeXxlJob {
                 noticeMessage.put("otherSideImage", null == seller ? "" : seller.getUserImage());
                 noticeMessage.put("message", "您有一笔交易即将开始, 请及时前往查看");
                 lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                 lifeNoticeMapper.insert(lifeNotice);
 
                 // 给买家推送通知
@@ -138,6 +146,7 @@ public class SecondGoodsTradeXxlJob {
                 lifeMessage.setReceiverId(sellerPhoneId);
                 lifeMessage.setContent(message.toJSONString());
                 lifeMessage.setType("5");
+                lifeMessageUtil.fillUserTypeAndRefId(lifeMessage);
                 lifeMessageMapper.insert(lifeMessage);
 
                 // 给卖家推送消息
@@ -166,6 +175,7 @@ public class SecondGoodsTradeXxlJob {
                 noticeMessage.put("otherSideImage", null == buyer ? "" : buyer.getUserImage());
                 noticeMessage.put("message", "您有一笔交易即将开始, 请及时前往查看");
                 lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                 lifeNoticeMapper.insert(lifeNotice);
 
                 // 给卖家推送通知
@@ -228,6 +238,7 @@ public class SecondGoodsTradeXxlJob {
                 noticeMessage.put("tradeStatus", tradeRecord.getTradeStatus());
                 noticeMessage.put("message", "您有一笔交易已完成, 请前往确认");
                 lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                 lifeNoticeMapper.insert(lifeNotice);
 
                 // 给买家推送通知
@@ -249,6 +260,7 @@ public class SecondGoodsTradeXxlJob {
                 lifeNotice.setTitle("商品是否交易成功");
                 lifeNotice.setNoticeType(1);
                 lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                 lifeNoticeMapper.insert(lifeNotice);
 
                 // 给卖家推送通知

+ 4 - 0
alien-job/src/main/java/shop/alien/job/store/BadReviewAppealJob.java

@@ -24,6 +24,7 @@ import org.springframework.web.client.RestTemplate;
 import shop.alien.entity.store.*;
 import shop.alien.entity.result.R;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.StoreCommentAppealMapper;
 import shop.alien.mapper.StoreCommentMapper;
 import shop.alien.mapper.StoreUserMapper;
@@ -63,6 +64,8 @@ public class BadReviewAppealJob {
     private final StoreCommentMapper storeCommentMapper;
     private final StoreUserMapper storeUserMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final CommonRatingMapper commonRatingMapper;
     private final CommonCommentMapper commonCommentMapper;
     private final LifeLikeRecordMapper lifeLikeRecordMapper;
@@ -237,6 +240,7 @@ public class BadReviewAppealJob {
                     lifeMessage.setSenderId("system");
                     lifeMessage.setIsRead(0);
                     lifeMessage.setNoticeType(1);
+                    lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
                     int insertResult = lifeNoticeMapper.insert(lifeMessage);
                     if (insertResult > 0) {
                         log.info("申诉通知发送成功,申诉ID: {},通知标题: {}", appeal.getId(), title);

+ 4 - 0
alien-job/src/main/java/shop/alien/job/store/LifeCouponJob.java

@@ -10,6 +10,7 @@ import org.springframework.util.StringUtils;
 import shop.alien.entity.store.*;
 import shop.alien.mapper.*;
 import shop.alien.util.common.constant.DiscountCouponEnum;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
@@ -36,6 +37,8 @@ public class LifeCouponJob {
     private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
     private final LifeUserMapper lifeUserMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final StoreInfoMapper storeInfoMapper;
 
     /**
@@ -118,6 +121,7 @@ public class LifeCouponJob {
                 notice.setTitle("优惠券即将到期");
                 notice.setIsRead(0);
                 notice.setNoticeType(2);
+                lifeNoticeUtil.fillUserTypeAndRefId(notice);
                 lifeNoticeMapper.insert(notice);
                 sendCount++;
             } catch (Exception e) {

+ 4 - 0
alien-job/src/main/java/shop/alien/job/store/LifeReverseGroupBuyingJob.java

@@ -13,6 +13,7 @@ import shop.alien.mapper.LifeMessageMapper;
 import shop.alien.mapper.LifeReverseGroupBuyingMapper;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.LifeUserOrderMapper;
+import shop.alien.util.type.LifeMessageUtil;
 import shop.alien.util.common.AlipayTradeRefund;
 import shop.alien.util.common.DateUtils;
 
@@ -41,6 +42,8 @@ public class LifeReverseGroupBuyingJob {
 
     private final LifeMessageMapper lifeMessageMapper;
 
+    private final LifeMessageUtil lifeMessageUtil;
+
     private final LifeUserMapper lifeUserMapper;
 
     /**
@@ -69,6 +72,7 @@ public class LifeReverseGroupBuyingJob {
                         lifeMessage.setReceiverId("user_" + lifeUser.getUserPhone());
                         lifeMessage.setContent("你有一笔反向团购退款失败, 请联系管理员");
                         lifeMessage.setType("2");
+                        lifeMessageUtil.fillUserTypeAndRefId(lifeMessage);
                         lifeMessageMapper.insert(lifeMessage);
                     }
 

+ 4 - 0
alien-job/src/main/java/shop/alien/job/store/LifeUserOrderJob.java

@@ -19,6 +19,7 @@ import shop.alien.util.common.constant.CouponTypeEnum;
 import shop.alien.util.common.constant.DiscountCouponEnum;
 import shop.alien.util.common.constant.OrderStatusEnum;
 import shop.alien.util.common.constant.PaymentEnum;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.math.BigDecimal;
 import java.text.ParseException;
@@ -51,6 +52,8 @@ public class LifeUserOrderJob {
     private final LifeUserMapper lifeUserMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     /**
      * 待支付订单超时处理
      */
@@ -305,6 +308,7 @@ public class LifeUserOrderJob {
                                 lifeMessage.setSenderId("system");
                                 lifeMessage.setIsRead(0);
                                 lifeMessage.setNoticeType(2);
+                                lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
                                 lifeNoticeMapper.insert(lifeMessage);
                             }
                         }

+ 140 - 48
alien-job/src/main/java/shop/alien/job/store/StoreMembershipCardJob.java

@@ -1,6 +1,7 @@
 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;
@@ -9,10 +10,14 @@ import shop.alien.config.redis.BaseRedisService;
 import shop.alien.entity.store.*;
 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 java.util.Calendar;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Component
@@ -39,8 +44,11 @@ public class StoreMembershipCardJob {
     private final static String MEMBERSHIP_CARD_BYE_TYPE_HALF_YEAR = "2";
     //会员卡购买类型-年卡
     private final static String MEMBERSHIP_CARD_BYE_TYPE_YEAR = "3";
-    //会员卡购买类型-年卡
-    private final static Integer LOGOUT_FLAY = 1;
+    // 商家账号注销: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;
 
@@ -56,8 +64,20 @@ public class StoreMembershipCardJob {
 
     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;
+
     /**
      * 会员卡状态及会员卡订单状态变更任务
      */
@@ -117,48 +137,41 @@ public class StoreMembershipCardJob {
     }
 
     /**
-     * 定时清理已申请注销超过7天的商家与用户
+     * 定时清理已申请注销超过7天的商家、用户与律师
      */
     @XxlJob("cancellationOfBusinessJob")
     public void cancellationOfBusinessJob() {
-        log.info("删除已申请注销超过7天的商家与用户: " + new Date());
+        log.info("处理已申请注销超过7天的商家、用户与律师: " + new Date());
         int successCount = 0;
         int failCount = 0;
 
-        // 获取全部申请注销的商家用户
-        List<StoreUser> storeUsers = storeUserMapper.selectList(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getLogoutFlag, LOGOUT_FLAY));
+        // 获取处于注销冷静期的商家用户(含旧数据 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()) {
-                    // 获取申请注销时间
-                    Date logoutTime = storeUser.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天的商家用户: userId={}, phone={}, logoutTime={}", 
-                                storeUser.getId(), storeUser.getPhone(), logoutTime);
-                        storeUserMapper.deleteById(storeUser.getId());
-                        alienStoreFeign.delMer(Boolean.TRUE, storeUser.getId().toString());
-                        //删除用户redis中的token
-                        baseRedisService.delete("store_" + storeUser.getPhone());
+                    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={}", 
+                log.error("商家账号注销状态更新失败: userId={}, phone={}, error={}",
                         storeUser.getId(), storeUser.getPhone(), e.getMessage(), e);
             }
         }
-        
+
         // 获取全部申请注销的店铺
-        List<StoreInfo> storeInfos = storeInfoMapper.selectList(new LambdaQueryWrapper<StoreInfo>().eq(StoreInfo::getLogoutFlag, LOGOUT_FLAY));
+        List<StoreInfo> storeInfos = storeInfoMapper.selectList(new LambdaQueryWrapper<StoreInfo>().eq(StoreInfo::getLogoutFlag, LOGOUT_FLAG_DONE));
         for (StoreInfo storeInfo : storeInfos) {
             try {
                 if (null != storeInfo.getLogoutTime()) {
@@ -174,6 +187,8 @@ public class StoreMembershipCardJob {
                     if (date.compareTo(sevenDay) >= 0) {
                         log.info("开始删除已注销超过7天的店铺: storeId={}, storeName={}, logoutTime={}", 
                                 storeInfo.getId(), storeInfo.getStoreName(), logoutTime);
+
+                        invalidateStoreCoupons(storeInfo.getId());
                         
                         // 先清理关联的商户用户注销状态,确保可以重新入驻
                         List<StoreUser> relatedStoreUsers = storeUserMapper.selectList(
@@ -201,8 +216,13 @@ public class StoreMembershipCardJob {
                             try {
                                 if (storeUser.getPhone() != null) {
                                     LambdaQueryWrapper<LifeFans> queryWrapper = new LambdaQueryWrapper<LifeFans>()
-                                            .eq(LifeFans::getFollowedId, "store_" + storeUser.getPhone())
-                                            .or().eq(LifeFans::getFansId, "store_" + storeUser.getPhone());
+                                            .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;
                                 }
@@ -233,38 +253,110 @@ public class StoreMembershipCardJob {
             }
         }
 
-        // 获取全部申请注销的用户
-        List<LifeUser> lifeUsers = lifeUserMapper.selectList(new LambdaQueryWrapper<LifeUser>().eq(LifeUser::getLogoutFlag, LOGOUT_FLAY));
+        // 获取处于注销冷静期的 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()) {
-                    // 获取申请注销时间
-                    Date logoutTime = lifeUser.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) {
-                        // 删除已过注销时间的用户
-                        lifeUserMapper.deleteById(lifeUser.getId());
-                        // 清理粉丝
-                        LambdaQueryWrapper<LifeFans> queryWrapper = new LambdaQueryWrapper<LifeFans>()
-                                .eq(LifeFans::getFollowedId, "user_" + lifeUser.getUserPhone())
-                                .or().eq(LifeFans::getFansId, "user_" + lifeUser.getUserPhone());
-                        lifeFansMapper.delete(queryWrapper);
+                    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("删除用户失败: userId={}, phone={}, error={}", 
+                log.error("C端用户注销状态更新失败: userId={}, phone={}, error={}",
                         lifeUser.getId(), lifeUser.getUserPhone(), e.getMessage(), 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());
+    }
 }

+ 5 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/config/WebSocketProcess.java

@@ -14,6 +14,7 @@ import shop.alien.entity.store.LifeMessage;
 import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.mapper.LifeBlacklistMapper;
 import shop.alien.mapper.LifeMessageMapper;
+import shop.alien.util.type.LifeMessageUtil;
 import shop.alien.util.common.safe.ImageReviewServiceEnum;
 import shop.alien.util.common.safe.TextModerationResultVO;
 import shop.alien.util.common.safe.TextModerationUtil;
@@ -43,6 +44,7 @@ public class WebSocketProcess implements ApplicationContextAware {
     private static ApplicationContext applicationContext;
     private static LifeBlacklistMapper lifeBlacklistMapper;
     private static LifeMessageMapper lifeMessageMapper;
+    private static LifeMessageUtil lifeMessageUtil;
     private static BaseRedisService baseRedisService;
     private static TextModerationUtil textModerationUtil;
     /*
@@ -55,6 +57,7 @@ public class WebSocketProcess implements ApplicationContextAware {
         WebSocketProcess.applicationContext = context;
         WebSocketProcess.lifeBlacklistMapper = context.getBean(LifeBlacklistMapper.class);
         WebSocketProcess.lifeMessageMapper = context.getBean(LifeMessageMapper.class);
+        WebSocketProcess.lifeMessageUtil = context.getBean(LifeMessageUtil.class);
         WebSocketProcess.baseRedisService = context.getBean(BaseRedisService.class);
         WebSocketProcess.textModerationUtil = context.getBean(TextModerationUtil.class);
     }
@@ -100,11 +103,13 @@ public class WebSocketProcess implements ApplicationContextAware {
             lifeMessage.setContent(webSocketVo.getText());
             lifeMessage.setType(webSocketVo.getType());
             lifeMessage.setBusinessId(webSocketVo.getBusinessId());
+            lifeMessageUtil.fillUserTypeAndRefId(lifeMessage);
             // 查询自己是否在对方的黑名单中
             if (baseRedisService.hasKey("blackList_" + webSocketVo.getSenderId())) {
                 List<String> blackList = baseRedisService.getList("blackList_" + webSocketVo.getSenderId());
                 if (blackList.contains(webSocketVo.getReceiverId())) {
                     lifeMessage.setDeletePhoneId(webSocketVo.getReceiverId());
+                    lifeMessageUtil.fillUserTypeAndRefId(lifeMessage);
                     lifeMessageMapper.insert(lifeMessage);
                     // 发送消息
                     webSocketVo.setMessageId(lifeMessage.getId());

+ 58 - 8
alien-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserController.java

@@ -8,10 +8,13 @@ import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerUser;
 import shop.alien.entity.store.StoreDictionary;
+import shop.alien.entity.store.dto.LawyerLogoutRequestDto;
 import shop.alien.entity.store.dto.LawyerPaymentAccountDto;
 import shop.alien.entity.store.dto.LawyerRecommendedDto;
 import shop.alien.entity.store.dto.LawyerUserDto;
 import shop.alien.entity.store.vo.LawyerUserVo;
+import org.springframework.util.StringUtils;
+import shop.alien.lawyer.config.BaseRedisService;
 import shop.alien.lawyer.service.LawyerUserService;
 import shop.alien.util.myBaticsPlus.QueryBuilder;
 
@@ -36,6 +39,8 @@ public class LawyerUserController {
 
     private final LawyerUserService lawyerUserService;
 
+    private final BaseRedisService baseRedisService;
+
     @ApiOperation("新增律师用户")
     @ApiOperationSupport(order = 1)
     @PostMapping("/addLawyerUser")
@@ -214,20 +219,65 @@ public class LawyerUserController {
     }
 
 
+    @ApiOperation("律师端注销前校验")
+    @ApiOperationSupport(order = 12)
+    @PostMapping("/lawyerCancelAccountVerification")
+    public R<Map<String, String>> lawyerCancelAccountVerification(@RequestBody LawyerLogoutRequestDto request) {
+        log.info("LawyerUserController.lawyerCancelAccountVerification?request={}", request);
+        if (request == null || request.getId() == null) {
+            return R.fail("律师ID不能为空");
+        }
+        return R.data(lawyerUserService.lawyerCancelAccountVerification(request.getId()));
+    }
+
     @ApiOperation("注销律师用户")
     @ApiOperationSupport(order = 13)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "id", value = "律师ID", dataType = "Integer", paramType = "query", required = true)
-    })
-    @GetMapping("/logOutLawyerUser")
-    public R<Map<String, Object>> logOutLawyerUser(@RequestParam(value = "id") Integer id) {
-        log.info("LawyerUserController.logOutLawyerUser?id={}", id);
-        return lawyerUserService.logOutLawyerUser(id);
+    @PostMapping("/logOutLawyerUser")
+    public R<Boolean> logOutLawyerUser(@RequestBody LawyerLogoutRequestDto request) {
+        log.info("LawyerUserController.logOutLawyerUser?request={}", request);
+        if (request == null || request.getId() == null) {
+            return R.fail("律师ID不能为空");
+        }
+        if (!StringUtils.hasText(request.getPhone())) {
+            return R.fail("手机号不能为空");
+        }
+        if (!StringUtils.hasText(request.getVerificationCode())) {
+            return R.fail("验证码不能为空");
+        }
+        String cacheCode = baseRedisService.getString("verification_lawyer_" + request.getPhone());
+        if (cacheCode == null) {
+            return R.fail("当验证码过期或未发送");
+        }
+        if (!cacheCode.trim().equals(request.getVerificationCode().trim())) {
+            return R.fail("验证码错误,请重新输入");
+        }
+        try {
+            lawyerUserService.logOutLawyerUser(request);
+        } catch (IllegalArgumentException e) {
+            return R.fail(e.getMessage());
+        }
+        return R.success("注销申请已提交");
+    }
+
+    @ApiOperation("撤销律师注销申请")
+    @ApiOperationSupport(order = 14)
+    @PostMapping("/cancelLogoutLawyerUser")
+    public R<Boolean> cancelLogoutLawyerUser(@RequestBody LawyerUser lawyerUser) {
+        log.info("LawyerUserController.cancelLogoutLawyerUser?lawyerUser={}", lawyerUser);
+        if (lawyerUser == null || lawyerUser.getId() == null) {
+            return R.fail("律师ID不能为空");
+        }
+        try {
+            lawyerUserService.cancelLogoutLawyerUser(lawyerUser.getId());
+        } catch (IllegalArgumentException e) {
+            return R.fail(e.getMessage());
+        }
+        return R.success("撤回注销成功");
     }
 
 
     @ApiOperation("律师信息详情")
-    @ApiOperationSupport(order = 14)
+    @ApiOperationSupport(order = 15)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true)
     })

+ 15 - 1
alien-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserLogInController.java

@@ -17,6 +17,7 @@ import shop.alien.entity.store.dto.LawyerUserDto;
 import shop.alien.entity.store.vo.LawyerUserVo;
 import shop.alien.lawyer.config.BaseRedisService;
 import shop.alien.lawyer.service.LawyerUserLogInService;
+import shop.alien.lawyer.service.LawyerUserService;
 import shop.alien.mapper.LawyerUserMapper;
 
 import java.util.Objects;
@@ -38,6 +39,7 @@ import java.util.Optional;
 public class LawyerUserLogInController {
 
     private final LawyerUserLogInService lawyerUserService;
+    private final LawyerUserService lawyerUserBizService;
     private final LawyerUserMapper lawyerUserMapper;
     private final BaseRedisService baseRedisService;
 
@@ -67,7 +69,11 @@ public class LawyerUserLogInController {
         if (null == lawyerUser) {
             return R.fail("当前账号不存在,请先去注册账号!");
         }
-        if (lawyerUser.getStatus() == 0) {
+        R<LawyerUser> loginCheck = lawyerUserBizService.checkLogin(lawyerUserDto.getPhone());
+        if ("账号已注销".equals(loginCheck.getMsg())) {
+            return R.fail("账号已注销");
+        }
+        if ("账号已被禁用".equals(loginCheck.getMsg())) {
             return R.fail("账号被禁用");
         }
         LawyerUser lawyerUser1 = new LawyerUser();
@@ -79,6 +85,14 @@ public class LawyerUserLogInController {
                 orElseGet(() -> R.fail("手机号不存在"));
     }
 
+    @ApiOperation("律师登录前校验")
+    @ApiOperationSupport(order = 7)
+    @GetMapping("/checkLogin")
+    public R<LawyerUser> checkLogin(String phone) {
+        log.info("LawyerUserLogInController.checkLogin?phone={}", phone);
+        return lawyerUserBizService.checkLogin(phone);
+    }
+
     @ApiOperation("律师用户登出")
     @ApiOperationSupport(order = 4)
     @PostMapping("/logout")

+ 19 - 12
alien-lawyer/src/main/java/shop/alien/lawyer/service/LawyerUserService.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerUser;
 import shop.alien.entity.store.StoreDictionary;
+import shop.alien.entity.store.dto.LawyerLogoutRequestDto;
 import shop.alien.entity.store.dto.LawyerUserDto;
 import shop.alien.entity.store.vo.LawyerUserVo;
 
@@ -114,19 +115,25 @@ public interface LawyerUserService extends IService<LawyerUser> {
     R<IPage<LawyerUser>> getAiRecommendList(int page, int size, Integer categoryId);
 
     /**
-     * 注销律师用户
-     * <p>
-     * 注销前会进行以下校验:
-     * 1. 参数校验:律师ID不能为空
-     * 2. 律师存在性校验:律师必须存在
-     * 3. 注销状态校验:已注销的律师不允许重复注销
-     * 4. 设置注销相关字段:注销标记、注销时间、状态等
-     * </p>
-     *
-     * @param id 律师ID
-     * @return R<Map < String, Object>> 注销结果,包含律师ID、姓名、手机号、注销标记、注销时间等信息
+     * 律师端注销前校验。
+     * accountMoney/accountConsultOrder/accountReceivingStatus:0 未通过,1 通过。
+     */
+    Map<String, String> lawyerCancelAccountVerification(Integer lawyerId);
+
+    /**
+     * 注销律师用户(进入 7 天冷静期)
+     */
+    void logOutLawyerUser(LawyerLogoutRequestDto request);
+
+    /**
+     * 撤销律师注销申请(冷静期内)
+     */
+    void cancelLogoutLawyerUser(Integer id);
+
+    /**
+     * 登录前校验律师账号状态
      */
-    R<Map<String, Object>> logOutLawyerUser(Integer id);
+    R<LawyerUser> checkLogin(String phone);
 
     /**
      * 获取律师信息

+ 7 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/AiUserAuditTaskServiceImpl.java

@@ -29,6 +29,7 @@ import shop.alien.lawyer.service.LawyerUserViolationService;
 import shop.alien.lawyer.util.ali.AliApi;
 import shop.alien.mapper.LawyerConsultationOrderMapper;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.LawyerUserMapper;
 import shop.alien.mapper.StoreUserMapper;
@@ -65,6 +66,8 @@ public class AiUserAuditTaskServiceImpl implements AiUserAuditTaskService {
 
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     private final WebSocketProcess webSocketProcess;
 
     private final LifeUserMapper lifeUserMapper;
@@ -350,6 +353,7 @@ public class AiUserAuditTaskServiceImpl implements AiUserAuditTaskService {
 
             // 创建并保存通知
             LifeNotice lifeNotice = createLifeNotice(violation.getId(), receiverId, "举报失败通知", message);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
 
             // 发送WebSocket消息
@@ -418,6 +422,7 @@ public class AiUserAuditTaskServiceImpl implements AiUserAuditTaskService {
 
             // 创建并保存通知
             LifeNotice lifeNotice = createLifeNotice(violation.getId(), receiverId, "举报成功通知", message);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
 
             // 发送WebSocket消息
@@ -451,6 +456,7 @@ public class AiUserAuditTaskServiceImpl implements AiUserAuditTaskService {
 
             // 创建并保存通知
             LifeNotice lifeNotice = createLifeNotice(violation.getId(), receiverId, "被举报成功通知", message);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
 
             // 发送WebSocket消息
@@ -647,6 +653,7 @@ public class AiUserAuditTaskServiceImpl implements AiUserAuditTaskService {
 
             // 发送退款到账通知
             LifeNotice refundArrivalNotice = createLifeNotice(order.getId(), receiverId, "退款到账通知", message);
+            lifeNoticeUtil.fillUserTypeAndRefId(refundArrivalNotice);
             lifeNoticeMapper.insert(refundArrivalNotice);
             sendWebSocketMessage(receiverId, refundArrivalNotice);
 

+ 6 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/CommentAppealServiceImpl.java

@@ -23,6 +23,7 @@ import shop.alien.lawyer.service.OrderReviewService;
 import shop.alien.lawyer.service.ReviewCommentService;
 import shop.alien.util.common.JwtUtil;
 import shop.alien.util.common.ListToPage;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -45,6 +46,8 @@ public class CommentAppealServiceImpl extends ServiceImpl<CommentAppealMapper, C
     private final ReviewCommentService reviewCommentService;
     private final WebSocketProcess webSocketProcess;
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final OrderReviewMapper orderReviewMapper;
     private final LifeUserMapper lifeUserMapper;
     private final LawyerUserMapper lawyerUserMapper;
@@ -231,6 +234,7 @@ public class CommentAppealServiceImpl extends ServiceImpl<CommentAppealMapper, C
 //            jsonObject.put("status", "pending"); // 待审核状态
             lifeNotice.setContext(jsonObject.toJSONString());
 
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
 
             // 发送 WebSocket 消息
@@ -295,6 +299,7 @@ public class CommentAppealServiceImpl extends ServiceImpl<CommentAppealMapper, C
             jsonObject.put("message", message);
             lifeNotice.setContext(jsonObject.toJSONString());
 
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
 
             // 发送 WebSocket 消息
@@ -341,6 +346,7 @@ public class CommentAppealServiceImpl extends ServiceImpl<CommentAppealMapper, C
             jsonObject.put("message", message);
             lifeNotice.setContext(jsonObject.toJSONString());
 
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
 
             // 发送 WebSocket 消息

+ 14 - 5
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerClientConsultationOrderServiceImpl.java

@@ -28,6 +28,8 @@ import shop.alien.lawyer.service.OrderExpirationService;
 import shop.alien.mapper.*;
 import shop.alien.util.common.constant.LawyerStatusEnum;
 import shop.alien.util.common.constant.OrderActionType;
+import shop.alien.util.type.LifeMessageUtil;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.text.SimpleDateFormat;
 import java.time.LocalDateTime;
@@ -54,9 +56,12 @@ public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<Lawyer
     private final OrderExpirationService orderExpirationService;
     private final AlienStoreFeign alienStoreFeign;
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final LifeUserMapper lifeUserMapper;
     private final WebSocketProcess webSocketProcess;
     private final LifeMessageMapper lifeMessageMapper;
+    private final LifeMessageUtil lifeMessageUtil;
     private final LawFirmMapper lawFirmMapper;
 
     /**
@@ -427,10 +432,10 @@ public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<Lawyer
         String phone = lawyerUser.getPhone();
 
         LambdaQueryWrapper<LifeMessage> lifeMessageLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        lifeMessageLambdaQueryWrapper.in(LifeMessage::getSenderId, lawyerIdList);
+        lifeMessageUtil.applySenderInQuery(lifeMessageLambdaQueryWrapper, lawyerIdList);
         lifeMessageLambdaQueryWrapper.eq(LifeMessage::getDeleteFlag, 0);
         lifeMessageLambdaQueryWrapper.eq(LifeMessage::getIsRead, 0);
-        lifeMessageLambdaQueryWrapper.eq(LifeMessage::getReceiverId, "lawyer_" + phone);
+        lifeMessageUtil.applyReceiverQuery(lifeMessageLambdaQueryWrapper, "lawyer_" + phone);
         List<LifeMessage> lifeMessageList = lifeMessageMapper.selectList(lifeMessageLambdaQueryWrapper);
 
 
@@ -1355,6 +1360,7 @@ public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<Lawyer
 
             // 创建并保存通知
             LifeNotice lifeNotice = createRefundNotice(order.getId(), receiverId, "退款申请处理通知", message);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             int noticeResult = lifeNoticeMapper.insert(lifeNotice);
             if (noticeResult <= 0) {
                 log.warn("发送同意退款通知失败:保存通知失败,订单ID={}", order.getId());
@@ -1400,6 +1406,7 @@ public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<Lawyer
 
             // 创建并保存通知
             LifeNotice lifeNotice = createRefundNotice(order.getId(), receiverId, "拒绝退款通知", message);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             int noticeResult = lifeNoticeMapper.insert(lifeNotice);
             if (noticeResult <= 0) {
                 log.warn("发送拒绝退款通知失败:保存通知失败,订单ID={}", order.getId());
@@ -1530,6 +1537,7 @@ public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<Lawyer
 
             // 创建并保存通知
             LifeNotice lifeNotice = createOrderNotice(order.getId(), receiverId, "接单通知", message);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             int noticeResult = lifeNoticeMapper.insert(lifeNotice);
             if (noticeResult <= 0) {
                 log.warn("发送接单通知失败:保存通知失败,订单ID={}", order.getId());
@@ -1579,6 +1587,7 @@ public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<Lawyer
 
             // 创建并保存通知
             LifeNotice lifeNotice = createOrderNotice(order.getId(), receiverId, "拒绝接单通知", message);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             int noticeResult = lifeNoticeMapper.insert(lifeNotice);
             if (noticeResult <= 0) {
                 log.warn("发送拒绝接单通知失败:保存通知失败,订单ID={}", order.getId());
@@ -1622,6 +1631,7 @@ public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<Lawyer
 
             // 创建并保存通知
             LifeNotice lifeNotice = createOrderNotice(order.getId(), receiverId, "退款到账通知", message);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             int noticeResult = lifeNoticeMapper.insert(lifeNotice);
             if (noticeResult <= 0) {
                 log.warn("发送退款到账通知失败:保存通知失败,订单ID={}", order.getId());
@@ -1876,9 +1886,8 @@ public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<Lawyer
 
             // 构建查询条件
             LambdaQueryWrapper<LifeMessage> queryWrapper = new LambdaQueryWrapper<>();
-            queryWrapper.eq(LifeMessage::getSenderId, senderId)
-                    .eq(LifeMessage::getReceiverId, receiverId)
-                    .gt(LifeMessage::getCreatedTime, acceptOrdersTime)
+            lifeMessageUtil.applySenderReceiverPairQuery(queryWrapper, senderId, receiverId);
+            queryWrapper.gt(LifeMessage::getCreatedTime, acceptOrdersTime)
                     .eq(LifeMessage::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
                     .last("LIMIT 1");
 

+ 8 - 2
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java

@@ -31,6 +31,8 @@ import shop.alien.lawyer.service.OrderExpirationService;
 import shop.alien.lawyer.util.DateUtils;
 import shop.alien.mapper.*;
 import shop.alien.util.common.constant.LawyerStatusEnum;
+import shop.alien.util.type.LifeMessageUtil;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -59,10 +61,13 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
 //    private final AliApi aliApi;
     private final LawFirmMapper lawFirmMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final LifeUserMapper lifeUserMapper;
     private final WebSocketProcess webSocketProcess;
 
     private final LifeMessageMapper lifeMessageMapper;
+    private final LifeMessageUtil lifeMessageUtil;
     private final StoreCommentMapper storeCommentMapper;
     private final LawyerUserViolationMapper lawyerUserViolationMapper;
     private final CommentAppealMapper commentAppealMapper;
@@ -999,10 +1004,10 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
         String phone = lifeUser.getUserPhone();
 
         LambdaQueryWrapper<LifeMessage> lifeMessageLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        lifeMessageLambdaQueryWrapper.in(LifeMessage::getSenderId, lawyerIdList);
+        lifeMessageUtil.applySenderInQuery(lifeMessageLambdaQueryWrapper, lawyerIdList);
         lifeMessageLambdaQueryWrapper.eq(LifeMessage::getDeleteFlag, 0);
         lifeMessageLambdaQueryWrapper.eq(LifeMessage::getIsRead, 0);
-        lifeMessageLambdaQueryWrapper.eq(LifeMessage::getReceiverId, "user_" + phone);
+        lifeMessageUtil.applyReceiverQuery(lifeMessageLambdaQueryWrapper, "user_" + phone);
         List<LifeMessage> lifeMessageList = lifeMessageMapper.selectList(lifeMessageLambdaQueryWrapper);
 
         // 按照senderId进行分组,返回senderId和数量的map
@@ -1862,6 +1867,7 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
                 return;
             }
 
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             int noticeResult = lifeNoticeMapper.insert(lifeNotice);
             if (noticeResult <= 0) {
                 log.warn("保存退款申请通知失败,订单ID={}, 用户ID={}", order.getId(), clientUserId);

+ 7 - 4
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerNoticeServiceImpl.java

@@ -20,6 +20,7 @@ import shop.alien.lawyer.service.LawyerNoticeService;
 import shop.alien.mapper.LawyerUserViolationMapper;
 import shop.alien.mapper.LifeMessageMapper;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.util.*;
 import java.util.stream.Collectors;
@@ -90,6 +91,8 @@ public class LawyerNoticeServiceImpl extends ServiceImpl<LifeNoticeMapper, LifeN
 
     private final LawyerUserViolationMapper lawyerUserViolationMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     @Override
     public R<IPage<LifeNoticeVo>> getLawyerNoticeList(Integer pageNum, Integer pageSize, String receiverId, Integer noticeType) {
         // 构建查询条件
@@ -142,8 +145,8 @@ public class LawyerNoticeServiceImpl extends ServiceImpl<LifeNoticeMapper, LifeN
      */
     private LambdaQueryWrapper<LifeNotice> buildQueryWrapper(String receiverId, Integer noticeType) {
         LambdaQueryWrapper<LifeNotice> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(LifeNotice::getReceiverId, receiverId)
-                .eq(LifeNotice::getNoticeType, noticeType)
+        lifeNoticeUtil.applyReceiverQuery(queryWrapper, receiverId);
+        queryWrapper.eq(LifeNotice::getNoticeType, noticeType)
                 .eq(LifeNotice::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
                 .orderByDesc(LifeNotice::getCreatedTime);
         return queryWrapper;
@@ -341,8 +344,8 @@ public class LawyerNoticeServiceImpl extends ServiceImpl<LifeNoticeMapper, LifeN
         
         // 构建查询条件:查询未读且未删除的通知,使用LIMIT 1优化性能
         LambdaQueryWrapper<LifeNotice> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(LifeNotice::getReceiverId, receiverId)
-                .eq(LifeNotice::getIsRead, IS_READ_UNREAD)
+        lifeNoticeUtil.applyReceiverQuery(queryWrapper, receiverId);
+        queryWrapper.eq(LifeNotice::getIsRead, IS_READ_UNREAD)
                 .eq(LifeNotice::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
                 .last("LIMIT 1");
         

+ 31 - 1
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerUserLogInServiceImpl.java

@@ -27,6 +27,8 @@ import shop.alien.util.common.JwtUtil;
 
 import java.util.*;
 import java.util.stream.Collectors;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 
 /**
  * 二期-门店用户 服务实现类
@@ -39,6 +41,8 @@ import java.util.stream.Collectors;
 @RequiredArgsConstructor
 public class LawyerUserLogInServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerUser> implements LawyerUserLogInService {
 
+    private static final DateTimeFormatter LOGOUT_END_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy/MM/dd");
+
     @Value("${jwt.expiration-time}")
     private String effectiveTime;
 
@@ -83,6 +87,7 @@ public class LawyerUserLogInServiceImpl extends ServiceImpl<LawyerUserMapper, La
 
         LawyerUserVo lawyerUserVo = new LawyerUserVo();
         BeanUtils.copyProperties(lawyerUser, lawyerUserVo);
+        lawyerUserVo.setLogoutEndTime(resolveLogoutEndTime(lawyerUser.getLogoutTime(), lawyerUser.getLogoutFlag()));
         Map<String, String> tokenMap = new HashMap<>();
         tokenMap.put("phone", lawyerUser.getPhone());
         tokenMap.put("userName", lawyerUser.getName());
@@ -94,6 +99,20 @@ public class LawyerUserLogInServiceImpl extends ServiceImpl<LawyerUserMapper, La
         return R.data(lawyerUserVo);
     }
 
+    /**
+     * 申请注销结束时间:logoutTime 起算第 7 天(T+0~T+7),与冷静期一致。
+     */
+    private static String resolveLogoutEndTime(Date logoutTime, Integer logoutFlag) {
+        if (logoutTime == null || logoutFlag == null || LawyerUser.LOGOUT_FLAG_COOLING != logoutFlag) {
+            return null;
+        }
+        return logoutTime.toInstant()
+                .atZone(ZoneId.systemDefault())
+                .toLocalDate()
+                .plusDays(7)
+                .format(LOGOUT_END_TIME_FORMAT);
+    }
+
     @Override
     public R<LawyerUserVo> register(LawyerUserDto lawyerUserDto) {
         LawyerUser lawyerUser = lawyerUserMapper.selectOne(new LambdaQueryWrapper<LawyerUser>()
@@ -102,6 +121,10 @@ public class LawyerUserLogInServiceImpl extends ServiceImpl<LawyerUserMapper, La
             if (lawyerUser.getStatus() == 0) {
                 return R.fail("账号被禁用");
             }
+            if (lawyerUser.getLogoutFlag() != null && LawyerUser.LOGOUT_FLAG_DONE == lawyerUser.getLogoutFlag()
+                    && (lawyerUser.getDeleteFlag() == null || lawyerUser.getDeleteFlag() == 0)) {
+                return R.fail("账号已注销");
+            }
             return createToKen(lawyerUser);
         }else {
             LawyerUser user = new LawyerUser();
@@ -118,7 +141,7 @@ public class LawyerUserLogInServiceImpl extends ServiceImpl<LawyerUserMapper, La
                     return R.fail("该律师执业证号已被使用,无法重复注册");
                 }
             }
-            user.setLogoutFlag(0);
+            user.setLogoutFlag(LawyerUser.LOGOUT_FLAG_NORMAL);
             user.setStatus(1);
             user.setDeleteFlag(0);
             user.setIsOnline(1);
@@ -160,6 +183,13 @@ public class LawyerUserLogInServiceImpl extends ServiceImpl<LawyerUserMapper, La
                         .eq(LawyerUser::getPhone, lawyerUserDto.getPhone()));
                 LawyerUserVo vo = new LawyerUserVo();
                 if (ObjectUtils.isNotEmpty(lawyerUser)) {
+                    if (lawyerUser.getStatus() != null && lawyerUser.getStatus() == 0) {
+                        return R.fail("账号被禁用");
+                    }
+                    if (lawyerUser.getLogoutFlag() != null && LawyerUser.LOGOUT_FLAG_DONE == lawyerUser.getLogoutFlag()
+                            && (lawyerUser.getDeleteFlag() == null || lawyerUser.getDeleteFlag() == 0)) {
+                        return R.fail("账号已注销");
+                    }
                     BeanUtils.copyProperties(lawyerUser, vo);
                     return createToKen(lawyerUser);
                 }

+ 218 - 96
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerUserServiceImpl.java

@@ -2,6 +2,7 @@ package shop.alien.lawyer.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -15,10 +16,13 @@ import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.*;
+import shop.alien.entity.store.dto.LawyerLogoutRequestDto;
 import shop.alien.entity.store.dto.LawyerUserDto;
 import shop.alien.entity.store.excelVo.LawyerUserExcelVo;
 import shop.alien.entity.store.vo.LawyerUserVo;
+import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.lawyer.config.BaseRedisService;
+import shop.alien.lawyer.config.WebSocketProcess;
 import shop.alien.lawyer.service.LawyerLegalProblemScenarioService;
 import shop.alien.lawyer.service.LawyerServiceAreaService;
 import shop.alien.lawyer.service.LawyerUserSearchHistoryService;
@@ -26,7 +30,9 @@ import shop.alien.lawyer.service.LawyerUserService;
 import shop.alien.lawyer.util.ai.LawyerLicenseVerifyUtil;
 import shop.alien.mapper.*;
 import shop.alien.util.common.ListToPage;
+import shop.alien.util.common.constant.LawyerStatusEnum;
 import shop.alien.util.excel.EasyExcelUtil;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import javax.servlet.http.HttpServletResponse;
 import java.text.SimpleDateFormat;
@@ -45,6 +51,17 @@ import java.util.stream.Collectors;
 @RequiredArgsConstructor
 public class LawyerUserServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerUser> implements LawyerUserService {
 
+    private static final String LOGOUT_REASON_OTHER = "其他";
+    private static final int LOGOUT_CONTENT_MAX_LENGTH = 300;
+    private static final int LOGOUT_COOLING_DAYS = 7;
+    private static final String CANCEL_REVOKE_NOT_IN_COOLING_MSG = "当前账号不在注销冷静期内,无法撤销";
+
+    /** 咨询订单未完成:待支付、待接单、进行中 */
+    private static final List<Integer> PENDING_CONSULTATION_ORDER_STATUSES = Arrays.asList(
+            LawyerStatusEnum.WAIT_PAY.getStatus(),
+            LawyerStatusEnum.WAIT_ACCEPT.getStatus(),
+            LawyerStatusEnum.INPROGRESS.getStatus());
+
     private final LawyerUserMapper lawyerUserMapper;
     private final LawyerServiceAreaService lawyerServiceAreaService;
     private final LawyerServiceAreaMapper lawyerServiceAreaMapper;
@@ -56,8 +73,10 @@ public class LawyerUserServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerU
     private final LawFirmPaymentMapper lawFirmPaymentmapper;
     private final OrderReviewMapper orderReviewMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final LawyerLicenseVerifyUtil lawyerLicenseVerifyUtil;
     private final StoreDictionaryMapper storeDictionaryMapper;
+    private final WebSocketProcess webSocketProcess;
 
     @Override
     public R<IPage<LawyerUser>> getLawyerUserList(int pageNum, int pageSize, String name, String phone, Integer status) {
@@ -478,119 +497,222 @@ public class LawyerUserServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerU
         return R.data(pageResult);
     }
 
-    /**
-     * 注销律师用户
-     * <p>
-     * 注销前会进行以下校验:
-     * 1. 参数校验:律师ID不能为空
-     * 2. 律师存在性校验:律师必须存在
-     * 3. 注销状态校验:已注销的律师不允许重复注销
-     * 4. 设置注销相关字段:注销标记、注销时间、状态等
-     * </p>
-     *
-     * @param id 律师ID
-     * @return 注销结果,包含注销信息
-     */
+    @Override
+    public Map<String, String> lawyerCancelAccountVerification(Integer lawyerId) {
+        Map<String, String> result = new HashMap<>();
+        if (lawyerId == null) {
+            return result;
+        }
+        LawyerUser lawyerUser = this.getById(lawyerId);
+        if (lawyerUser == null) {
+            return result;
+        }
+        result.put("status", lawyerUser.getStatus() != null ? lawyerUser.getStatus().toString() : "1");
+        if (lawyerUser.getMoney() != null && lawyerUser.getMoney() > 0) {
+            result.put("accountMoney", "0");
+        } else {
+            result.put("accountMoney", "1");
+        }
+        long pendingOrderCount = lawyerConsultationOrderMapper.selectCount(new LambdaQueryWrapper<LawyerConsultationOrder>()
+                .eq(LawyerConsultationOrder::getLawyerUserId, lawyerId)
+                .in(LawyerConsultationOrder::getOrderStatus, PENDING_CONSULTATION_ORDER_STATUSES)
+                .eq(LawyerConsultationOrder::getDeleteFlag, 0));
+        result.put("accountConsultOrder", pendingOrderCount > 0 ? "0" : "1");
+        result.put("accountConsultOrderCount", String.valueOf(pendingOrderCount));
+        if (lawyerUser.getOrderReceivingStatus() != null && lawyerUser.getOrderReceivingStatus() == 1) {
+            result.put("accountReceivingStatus", "0");
+        } else {
+            result.put("accountReceivingStatus", "1");
+        }
+        return result;
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public R<Map<String, Object>> logOutLawyerUser(Integer id) {
-        log.info("开始注销律师用户,律师ID={}", id);
+    public void logOutLawyerUser(LawyerLogoutRequestDto request) {
+        log.info("开始注销律师用户,request={}", request);
+        if (request == null || request.getId() == null) {
+            throw new IllegalArgumentException("律师ID不能为空");
+        }
+        Integer id = request.getId();
 
-        Map<String, Object> resultMap = new HashMap<>();
+        String logoutReason = request.getLogoutReason() == null ? null : request.getLogoutReason().trim();
+        if (!StringUtils.hasText(logoutReason)) {
+            throw new IllegalArgumentException("请选择注销原因");
+        }
 
-        // 参数校验
-        if (id == null) {
-            log.warn("注销律师用户失败:律师ID为空");
-            return R.fail("律师ID不能为空");
+        String logoutContent = request.getLogoutContent() == null ? null : request.getLogoutContent().trim();
+        if (LOGOUT_REASON_OTHER.equals(logoutReason)) {
+            if (!StringUtils.hasText(logoutContent)) {
+                throw new IllegalArgumentException("请填写注销内容");
+            }
+            if (logoutContent.length() > LOGOUT_CONTENT_MAX_LENGTH) {
+                throw new IllegalArgumentException("注销内容不能超过300字");
+            }
         }
 
-        // 查询律师信息
         LawyerUser lawyerUser = this.getById(id);
         if (lawyerUser == null) {
-            log.warn("注销律师用户失败:律师不存在,律师ID={}", id);
-            return R.fail("律师不存在");
+            throw new IllegalArgumentException("律师不存在");
         }
-
-        String key = "lawyer_" + lawyerUser.getPhone();
-        String redisVerificationCode = baseRedisService.getString(key);
-        //对redisVerificationCode进行校验,如果不是空的话清除redis中的记录
-//        if (redisVerificationCode != null) {
-//            baseRedisService.delete(key);
-//        }
-        // 检查是否已经注销
-        if (lawyerUser.getLogoutFlag() != null && lawyerUser.getLogoutFlag() == 1 && lawyerUser.getDeleteFlag() == 1) {
-            log.warn("注销律师用户失败:律师已注销,律师ID={}, 律师姓名={}", id, lawyerUser.getName());
-            return R.fail("该律师账号已注销,无需重复注销");
-        }
-
-        // 检查是否已删除
         if (lawyerUser.getDeleteFlag() != null && lawyerUser.getDeleteFlag() == 1) {
-            log.warn("注销律师用户失败:律师已删除,律师ID={}, 律师姓名={}", id, lawyerUser.getName());
-            return R.fail("该律师账号已删除");
+            throw new IllegalArgumentException("该律师账号已删除");
+        }
+        if (isLawyerLogoutCooling(lawyerUser) || isLawyerLoggedOut(lawyerUser)) {
+            throw new IllegalArgumentException("账号已在注销流程中");
+        }
+
+        lawyerUser.setLogoutReason(logoutReason);
+        lawyerUser.setLogoutCode(LOGOUT_REASON_OTHER.equals(logoutReason) ? logoutContent : null);
+        lawyerUser.setLogoutFlag(LawyerUser.LOGOUT_FLAG_COOLING);
+        lawyerUser.setLogoutTime(new Date());
+        lawyerUser.setOrderReceivingStatus(0);
+        lawyerUser.setIsOnline(0);
+
+        int num = lawyerUserMapper.updateById(lawyerUser);
+        if (num <= 0) {
+            throw new IllegalArgumentException("注销失败,请稍后重试");
+        }
+
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setReceiverId("lawyer_" + lawyerUser.getPhone());
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String logoutDate = simpleDateFormat.format(new Date());
+        String text = "您在" + logoutDate + "注销了账号,平台将为您保留7天,在此期间您可撤销注销账号,7天后账号将变为已注销状态。";
+        com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
+        jsonObject.put("message", text);
+        lifeNotice.setContext(jsonObject.toJSONString());
+        lifeNotice.setTitle("注销律师账号通知");
+        lifeNotice.setSenderId("system");
+        lifeNotice.setIsRead(0);
+        lifeNotice.setNoticeType(1);
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
+        lifeNoticeMapper.insert(lifeNotice);
+
+        WebSocketVo websocketVo = new WebSocketVo();
+        websocketVo.setSenderId("system");
+        websocketVo.setReceiverId("lawyer_" + lawyerUser.getPhone());
+        websocketVo.setCategory("notice");
+        websocketVo.setNoticeType("1");
+        websocketVo.setIsRead(0);
+        websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeNotice).toJSONString());
+        try {
+            webSocketProcess.sendMessage("lawyer_" + lawyerUser.getPhone(),
+                    com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
+        } catch (Exception e) {
+            log.error("律师注销通知 WebSocket 发送失败: {}", e.getMessage());
         }
+        log.info("律师注销申请已提交,律师ID={}, phone={}", id, lawyerUser.getPhone());
+    }
 
-        List<LawyerConsultationOrder> list = lawyerConsultationOrderMapper.selectOrder(null, id);
-
-
-        if (!CollectionUtils.isEmpty(list)) {
-            // 提示前端有未完成订单 1:有未完成订单  0:无未完成订单
-            resultMap.put("unfinishedOrder", 1);
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void cancelLogoutLawyerUser(Integer id) {
+        if (id == null) {
+            throw new IllegalArgumentException("律师ID不能为空");
         }
+        LawyerUser lawyerUser = this.getById(id);
+        if (lawyerUser == null) {
+            throw new IllegalArgumentException("律师不存在");
+        }
+        if (!isLawyerLogoutCooling(lawyerUser)) {
+            throw new IllegalArgumentException(CANCEL_REVOKE_NOT_IN_COOLING_MSG);
+        }
+
+        lawyerUser.setLogoutFlag(LawyerUser.LOGOUT_FLAG_NORMAL);
+        lawyerUser.setLogoutTime(null);
+        lawyerUser.setLogoutReason(null);
+        lawyerUser.setLogoutCode(null);
+        int updated = lawyerUserMapper.updateById(lawyerUser);
+        if (updated == 0) {
+            throw new IllegalArgumentException("撤销注销失败,请稍后重试");
+        }
+
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setReceiverId("lawyer_" + lawyerUser.getPhone());
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String revokeDate = simpleDateFormat.format(new Date());
+        String text = "您在" + revokeDate + "撤销了注销账号,所有数据均已保留,您可继续在平台使用。";
+        com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
+        jsonObject.put("message", text);
+        lifeNotice.setContext(jsonObject.toJSONString());
+        lifeNotice.setTitle("撤销律师账号注销通知");
+        lifeNotice.setSenderId("system");
+        lifeNotice.setIsRead(0);
+        lifeNotice.setNoticeType(1);
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
+        lifeNoticeMapper.insert(lifeNotice);
+
+        WebSocketVo websocketVo = new WebSocketVo();
+        websocketVo.setSenderId("system");
+        websocketVo.setReceiverId("lawyer_" + lawyerUser.getPhone());
+        websocketVo.setCategory("notice");
+        websocketVo.setNoticeType("1");
+        websocketVo.setIsRead(0);
+        websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeNotice).toJSONString());
+        try {
+            webSocketProcess.sendMessage("lawyer_" + lawyerUser.getPhone(),
+                    com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
+        } catch (Exception e) {
+            log.error("律师撤销注销通知 WebSocket 发送失败: {}", e.getMessage());
+        }
+        log.info("律师撤销注销成功,律师ID={}", id);
+    }
 
-        if (lawyerUser.getOrderReceivingStatus() != null && lawyerUser.getOrderReceivingStatus() == 1) {
-            // 提示前端接单状态为接单中 1:接单中  0:未接单
-            resultMap.put("orderReceivingStatus", 1);
-        }
-
-        if (lawyerUser.getOrderReceivingStatus() != null) {
-
-            if (CollectionUtils.isEmpty(list) && lawyerUser.getOrderReceivingStatus() == 0) {
-                // 设置注销相关字段
-                lawyerUser.setLogoutFlag(1); // 注销标记:1-已注销
-                lawyerUser.setLogoutTime(new Date()); // 注销时间
-                lawyerUser.setStatus(0); // 状态设置为禁用
-                lawyerUser.setOrderReceivingStatus(0); // 接单状态设置为不接单
-                lawyerUser.setIsOnline(0); // 在线状态设置为离线
-                lawyerUser.setIsRecommended(0); // 推荐状态设置为不推荐
-                lawyerUser.setIsOnline(0);
-                lawyerUser.setDeleteFlag(1);
-                lawyerUser.setLastOnlineTime(new Date());
-
-                // 执行更新操作
-                int result = lawyerUserMapper.updateLawyerUser(lawyerUser);
-                if (result==0) {
-                    log.error("注销律师用户失败:更新数据库失败,律师ID={}", id);
-                    return R.fail("注销失败,请稍后重试");
-                }
-
-                //对redisVerificationCode进行校验,如果不是空的话清除redis中的记录
-                if (redisVerificationCode != null) {
-                    baseRedisService.delete(key);
-                }
+    @Override
+    public R<LawyerUser> checkLogin(String phone) {
+        LambdaQueryWrapper<LawyerUser> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerUser::getPhone, phone)
+                .eq(LawyerUser::getDeleteFlag, 0)
+                .orderByDesc(LawyerUser::getCreatedTime)
+                .last("LIMIT 1");
+        LawyerUser lawyerUser = lawyerUserMapper.selectOne(queryWrapper);
+        if (lawyerUser == null) {
+            return R.success("用户不存在,快去注册吧!");
+        }
+        if (lawyerUser.getStatus() != null && lawyerUser.getStatus() == 0) {
+            return R.success("账号已被禁用");
+        }
+        if (isLawyerLoggedOut(lawyerUser)) {
+            return R.data(lawyerUser, "账号已注销");
+        }
+        if (isLawyerLogoutCooling(lawyerUser)) {
+            return R.data(lawyerUser, "账号注销冷静期中");
+        }
+        return R.data(lawyerUser, "success");
+    }
 
-                log.info("注销律师用户成功,律师ID={}, 律师姓名={}, 注销时间={}",
-                        id, lawyerUser.getName(), lawyerUser.getLogoutTime());
-
-
-                lifeNoticeMapper.update(
-                        null,
-                        new UpdateWrapper<LifeNotice>()
-                                .eq("receiver_id", key)
-                                .set("delete_flag", 1)
-                );
-
-                // 构建返回结果
-                resultMap.put("id", lawyerUser.getId());
-                resultMap.put("name", lawyerUser.getName());
-                resultMap.put("phone", lawyerUser.getPhone());
-                resultMap.put("logoutFlag", lawyerUser.getLogoutFlag());
-                resultMap.put("logoutTime", lawyerUser.getLogoutTime());
-                resultMap.put("status", lawyerUser.getStatus());
-                resultMap.put("message", "律师账号注销成功");
-            }
+    /** 是否处于注销冷静期(含旧数据:logout_flag=1 且未逻辑删除、仍在 7 天内) */
+    private static boolean isLawyerLogoutCooling(LawyerUser user) {
+        if (user == null) {
+            return false;
+        }
+        if (LawyerUser.LOGOUT_FLAG_COOLING == user.getLogoutFlag()) {
+            return true;
+        }
+        if (LawyerUser.LOGOUT_FLAG_DONE == user.getLogoutFlag()
+                && (user.getDeleteFlag() == null || user.getDeleteFlag() == 0)
+                && user.getLogoutTime() != null) {
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(user.getLogoutTime());
+            calendar.add(Calendar.DAY_OF_YEAR, LOGOUT_COOLING_DAYS);
+            return !new Date().after(calendar.getTime());
         }
+        return false;
+    }
 
-        return R.data(resultMap);
+    /** 是否已注销(logout_flag=1 且已过冷静期,或旧数据已逻辑删除) */
+    private static boolean isLawyerLoggedOut(LawyerUser user) {
+        if (user == null) {
+            return false;
+        }
+        if (user.getDeleteFlag() != null && user.getDeleteFlag() == 1) {
+            return true;
+        }
+        if (LawyerUser.LOGOUT_FLAG_DONE != user.getLogoutFlag()) {
+            return false;
+        }
+        return !isLawyerLogoutCooling(user);
     }
 
     @Override

+ 8 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerUserViolationServiceImpl.java

@@ -26,6 +26,7 @@ import shop.alien.lawyer.service.LawyerUserService;
 import shop.alien.lawyer.util.ali.AliApi;
 import shop.alien.util.common.EnumUtil;
 import shop.alien.util.common.constant.LawyerStatusEnum;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.io.IOException;
 import java.math.BigDecimal;
@@ -119,6 +120,8 @@ public class LawyerUserViolationServiceImpl extends ServiceImpl<LawyerUserViolat
 
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     private final WebSocketProcess webSocketProcess;
 
     private final StoreDictionaryMapper storeDictionaryMapper;
@@ -301,6 +304,7 @@ public class LawyerUserViolationServiceImpl extends ServiceImpl<LawyerUserViolat
                 return;
             }
 
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             int noticeResult = lifeNoticeMapper.insert(lifeNotice);
             if (noticeResult <= 0) {
                 log.warn("保存举报人通知失败,举报ID:{}", lawyerUserViolation.getId());
@@ -333,6 +337,7 @@ public class LawyerUserViolationServiceImpl extends ServiceImpl<LawyerUserViolat
                 return;
             }
 
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNoticeReported);
             int noticeResult = lifeNoticeMapper.insert(lifeNoticeReported);
             if (noticeResult <= 0) {
                 log.warn("保存被举报人通知失败,举报ID:{}", lawyerUserViolation.getId());
@@ -1239,6 +1244,7 @@ public class LawyerUserViolationServiceImpl extends ServiceImpl<LawyerUserViolat
             // 创建并保存通知
             LifeNotice lifeNotice = createLifeNotice(violation.getId(), receiverId,
                     notificationInfo.getTitle(), notificationInfo.getMessage());
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
 
             // 发送WebSocket消息
@@ -1251,6 +1257,7 @@ public class LawyerUserViolationServiceImpl extends ServiceImpl<LawyerUserViolat
             String notificationInfo1 = "您的编号为" + violation.getOrderNumber() + "的订单,订单金额已原路返还至您的支付渠道,请查收";
             LifeNotice lifeNotice1 = createLifeNotice(violation.getId(), receiverId,
                     "退款到账通知", notificationInfo1);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice1);
             lifeNoticeMapper.insert(lifeNotice1);
 
             // 发送WebSocket消息
@@ -1284,6 +1291,7 @@ public class LawyerUserViolationServiceImpl extends ServiceImpl<LawyerUserViolat
             // 创建并保存通知
             LifeNotice lifeNotice = createLifeNotice(violation.getId(), receiverId,
                     notificationInfo.getTitle(), notificationInfo.getMessage());
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
 
             // 发送WebSocket消息

+ 7 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderExpirationServiceImpl.java

@@ -21,6 +21,7 @@ import shop.alien.lawyer.listener.RedisKeyExpirationHandler;
 import shop.alien.lawyer.service.OrderExpirationService;
 import shop.alien.mapper.LawyerConsultationOrderMapper;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.util.common.DateUtils;
 import shop.alien.util.common.constant.PaymentEnum;
@@ -55,6 +56,8 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
     private final LawyerConsultationOrderMapper orderMapper;
     private final AliController aliController;
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final LifeUserMapper lifeUserMapper;
     private final WebSocketProcess webSocketProcess;
     private final AlienStoreFeign alienStoreFeign;
@@ -167,6 +170,7 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
                 LifeNotice lifeNotice = buildUserLifeNotice(order, title, message);
                 WebSocketVo webSocketVo = buildWebSocketVo(lifeNotice);
 
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                 lifeNoticeMapper.insert(lifeNotice);
                 webSocketProcess.sendMessage(lifeNotice.getReceiverId(), JSONObject.from(webSocketVo).toJSONString());
 
@@ -188,6 +192,7 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
                     WebSocketVo webSocketVo2 = buildWebSocketVo(lifeNotice2);
                     lifeNotice2.setCreatedTime(DateUtils.calcMinute(new Date(), 2));
 
+                    lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice2);
                     lifeNoticeMapper.insert(lifeNotice2);
                     webSocketProcess.sendMessage(lifeNotice2.getReceiverId(), JSONObject.from(webSocketVo2).toJSONString());
 
@@ -216,6 +221,7 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
                 LifeNotice lifeNotice = buildUserLifeNotice(order, "同意退款通知", "您的编号为" + orderNo + "的订单,律师已同意您的退款申请,订单金额将在1-3个工作日原路返还,请注意查收。");
                 WebSocketVo webSocketVo = buildWebSocketVo(lifeNotice);
 
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                 lifeNoticeMapper.insert(lifeNotice);
                 webSocketProcess.sendMessage(lifeNotice.getReceiverId(), JSONObject.from(webSocketVo).toJSONString());
 
@@ -235,6 +241,7 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
                     WebSocketVo webSocketVo2 = buildWebSocketVo(lifeNotice2);
                     lifeNotice2.setCreatedTime(DateUtils.calcMinute(lifeNotice2.getCreatedTime(), 2));
 
+                    lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice2);
                     lifeNoticeMapper.insert(lifeNotice2);
                     webSocketProcess.sendMessage(lifeNotice2.getReceiverId(), JSONObject.from(webSocketVo2).toJSONString());
 

+ 12 - 2
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderReviewServiceImpl.java

@@ -26,6 +26,9 @@ import shop.alien.lawyer.config.WebSocketProcess;
 import shop.alien.lawyer.service.OrderReviewService;
 import shop.alien.lawyer.service.ReviewCommentService;
 import shop.alien.mapper.*;
+import shop.alien.util.type.LifeNoticeUtil;
+import shop.alien.lawyer.util.LawyerLikeRecordIdentityHelper;
+import shop.alien.lawyer.util.LawyerLikeRecordIdentityHelper.LawyerLikerScope;
 
 import java.util.ArrayList;
 import java.util.Date;
@@ -48,8 +51,11 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
     private final ReviewCommentService reviewCommentService;
     private final ReviewCommentMapper reviewCommentMapper;
     private final LifeLikeRecordMapper lifeLikeRecordMapper;
+    private final LawyerLikeRecordIdentityHelper lifeLikeIdentityHelper;
     private final LawyerUserMapper lawyerUserMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final WebSocketProcess webSocketProcess;
     private final LifeUserMapper lifeUserMapper;
 
@@ -549,17 +555,19 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
         }
 
         // 检查是否已点赞
+        LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
         LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(LifeLikeRecord::getType, "7")
-                .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
                 .eq(LifeLikeRecord::getHuifuId, String.valueOf(reviewId))
                 .eq(LifeLikeRecord::getDeleteFlag, 0);
+        lifeLikeIdentityHelper.applyLikerFilter(queryWrapper, likerScope);
         List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
 
         if (CollectionUtils.isEmpty(records)) {
             // 插入点赞记录
             LifeLikeRecord likeRecord = new LifeLikeRecord();
             likeRecord.setDianzanId(String.valueOf(userId));
+            lifeLikeIdentityHelper.normalizeBeforeSave(likeRecord);
             likeRecord.setHuifuId(String.valueOf(reviewId));
             likeRecord.setType("7");
             likeRecord.setCreatedTime(new Date());
@@ -595,11 +603,12 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
         }
 
         // 查询点赞记录
+        LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
         LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(LifeLikeRecord::getType, "7")
-                .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
                 .eq(LifeLikeRecord::getHuifuId, String.valueOf(reviewId))
                 .eq(LifeLikeRecord::getDeleteFlag, 0);
+        lifeLikeIdentityHelper.applyLikerFilter(queryWrapper, likerScope);
         List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
 
         if (!CollectionUtils.isEmpty(records)) {
@@ -778,6 +787,7 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
             LifeNotice lifeNotice = createBadReviewNotice(review.getId(), receiverId, orderNumber, message);
 
             // 保存通知
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             int insertResult = lifeNoticeMapper.insert(lifeNotice);
             if (insertResult > 0) {
                 log.info("发送差评通知成功,评价ID={},订单编号={},接收人ID={}", 

+ 11 - 3
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/ReviewCommentServiceImpl.java

@@ -18,6 +18,8 @@ import shop.alien.entity.store.dto.ReviewReplyDto;
 import shop.alien.entity.store.vo.ReviewCommentVo;
 import shop.alien.lawyer.service.OrderReviewService;
 import shop.alien.lawyer.service.ReviewCommentService;
+import shop.alien.lawyer.util.LawyerLikeRecordIdentityHelper;
+import shop.alien.lawyer.util.LawyerLikeRecordIdentityHelper.LawyerLikerScope;
 import shop.alien.mapper.LawyerUserMapper;
 import shop.alien.mapper.LifeLikeRecordMapper;
 import shop.alien.mapper.ReviewCommentMapper;
@@ -42,15 +44,18 @@ public class ReviewCommentServiceImpl extends ServiceImpl<ReviewCommentMapper, R
     private final OrderReviewService orderReviewService;
     private final LifeLikeRecordMapper lifeLikeRecordMapper;
     private final LawyerUserMapper lawyerUserMapper;
+    private final LawyerLikeRecordIdentityHelper lifeLikeIdentityHelper;
 
     public ReviewCommentServiceImpl(ReviewCommentMapper reviewCommentMapper,
                                     @Lazy OrderReviewService orderReviewService,
                                     LifeLikeRecordMapper lifeLikeRecordMapper,
-                                    LawyerUserMapper lawyerUserMapper) {
+                                    LawyerUserMapper lawyerUserMapper,
+                                    LawyerLikeRecordIdentityHelper lifeLikeIdentityHelper) {
         this.reviewCommentMapper = reviewCommentMapper;
         this.orderReviewService = orderReviewService;
         this.lifeLikeRecordMapper = lifeLikeRecordMapper;
         this.lawyerUserMapper = lawyerUserMapper;
+        this.lifeLikeIdentityHelper = lifeLikeIdentityHelper;
     }
 
     /**
@@ -185,17 +190,19 @@ public class ReviewCommentServiceImpl extends ServiceImpl<ReviewCommentMapper, R
         }
 
         // 检查是否已点赞
+        LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
         LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(LifeLikeRecord::getType, "8")
-                .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
                 .eq(LifeLikeRecord::getHuifuId, String.valueOf(commentId))
                 .eq(LifeLikeRecord::getDeleteFlag, 0);
+        lifeLikeIdentityHelper.applyLikerFilter(queryWrapper, likerScope);
         List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
 
         if (CollectionUtils.isEmpty(records)) {
             // 插入点赞记录
             LifeLikeRecord likeRecord = new LifeLikeRecord();
             likeRecord.setDianzanId(String.valueOf(userId));
+            lifeLikeIdentityHelper.normalizeBeforeSave(likeRecord);
             likeRecord.setHuifuId(String.valueOf(commentId));
             likeRecord.setType("8");
             likeRecord.setCreatedTime(new Date());
@@ -233,11 +240,12 @@ public class ReviewCommentServiceImpl extends ServiceImpl<ReviewCommentMapper, R
         }
 
         // 查询点赞记录
+        LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
         LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(LifeLikeRecord::getType, "8")
-                .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
                 .eq(LifeLikeRecord::getHuifuId, String.valueOf(commentId))
                 .eq(LifeLikeRecord::getDeleteFlag, 0);
+        lifeLikeIdentityHelper.applyLikerFilter(queryWrapper, likerScope);
         List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
 
         if (!CollectionUtils.isEmpty(records)) {

+ 31 - 9
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/StoreCommentServiceImpl.java

@@ -36,6 +36,9 @@ import shop.alien.util.common.netease.TextCheckUtil;
 import shop.alien.util.common.safe.TextModerationResultVO;
 import shop.alien.util.common.safe.TextModerationUtil;
 import shop.alien.util.common.safe.TextReviewServiceEnum;
+import shop.alien.util.type.LifeNoticeUtil;
+import shop.alien.lawyer.util.LawyerLikeRecordIdentityHelper;
+import shop.alien.lawyer.util.LawyerLikeRecordIdentityHelper.LawyerLikerScope;
 
 import java.io.IOException;
 import java.math.BigDecimal;
@@ -61,6 +64,8 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
 
     private final LifeLikeRecordMapper lifeLikeRecordMapper;
 
+    private final LawyerLikeRecordIdentityHelper lifeLikeIdentityHelper;
+
     private final FileUploadUtil fileUploadUtil;
 
     private final StoreImgMapper storeImgMapper;
@@ -77,6 +82,8 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
 
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     private final WebSocketProcess webSocketProcess;
 
     private final TagsSynonymMapper tagsSynonymMapper;
@@ -249,8 +256,10 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
             storeCommentVo.setCommitCount(0);
             //父级点赞状态
             if (StringUtils.isNotEmpty(phoneId)) {
+                LawyerLikerScope likerScope = lifeLikeIdentityHelper.resolveFromDianzanId(phoneId);
                 LambdaQueryWrapper<LifeLikeRecord> likeRecordQueryWrapper = new LambdaQueryWrapper<>();
-                likeRecordQueryWrapper.eq(LifeLikeRecord::getDianzanId, phoneId).eq(LifeLikeRecord::getHuifuId, storeCommentVo.getId());
+                likeRecordQueryWrapper.eq(LifeLikeRecord::getHuifuId, storeCommentVo.getId());
+                lifeLikeIdentityHelper.applyLikerFilter(likeRecordQueryWrapper, likerScope);
                 Integer i = lifeLikeRecordMapper.selectCount(likeRecordQueryWrapper);
                 if (i > 0) {
                     storeCommentVo.setIsLike(1);
@@ -317,8 +326,10 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
             //子级点赞状态
             if (StringUtils.isNotEmpty(phoneId)) {
                 childCommentList.forEach(child -> {
+                    LawyerLikerScope childLikerScope = lifeLikeIdentityHelper.resolveFromDianzanId(phoneId);
                     LambdaQueryWrapper<LifeLikeRecord> childlikeRecordQueryWrapper = new LambdaQueryWrapper<>();
-                    childlikeRecordQueryWrapper.eq(LifeLikeRecord::getDianzanId, phoneId).eq(LifeLikeRecord::getHuifuId, child.getId());
+                    childlikeRecordQueryWrapper.eq(LifeLikeRecord::getHuifuId, child.getId());
+                    lifeLikeIdentityHelper.applyLikerFilter(childlikeRecordQueryWrapper, childLikerScope);
                     if (lifeLikeRecordMapper.selectCount(childlikeRecordQueryWrapper) > 0) {
                         child.setIsLike(1);
                     } else {
@@ -693,6 +704,7 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
                 lifeMessage.setIsRead(0);
                 lifeMessage.setNoticeType(1);
                 lifeMessage.setBusinessType(1);
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
                 lifeNoticeMapper.insert(lifeMessage);
 
                 WebSocketVo websocketVo = new WebSocketVo();
@@ -841,6 +853,7 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
                 lifeMessage.setIsRead(0);
                 lifeMessage.setNoticeType(1);
                 lifeMessage.setBusinessType(1);
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
                 lifeNoticeMapper.insert(lifeMessage);
 
                 WebSocketVo websocketVo = new WebSocketVo();
@@ -1470,11 +1483,12 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
 
         // 检查当前用户是否已点赞
         if (currentUserId != null) {
+            LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(currentUserId);
             LambdaQueryWrapper<LifeLikeRecord> likeWrapper = new LambdaQueryWrapper<>();
             likeWrapper.eq(LifeLikeRecord::getType, "7")
-                    .eq(LifeLikeRecord::getDianzanId, String.valueOf(currentUserId))
                     .eq(LifeLikeRecord::getHuifuId, String.valueOf(review.getId()))
                     .eq(LifeLikeRecord::getDeleteFlag, 0);
+            lifeLikeIdentityHelper.applyLikerFilter(likeWrapper, likerScope);
             long likeCount = lifeLikeRecordMapper.selectCount(likeWrapper);
             reviewVo.setIsLiked(likeCount > 0 ? 1 : 0);
         } else {
@@ -1520,17 +1534,19 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
         }
 
         // 检查是否已点赞(type=7 表示订单评价点赞)
+        LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
         LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(LifeLikeRecord::getType, "7")
-                .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
                 .eq(LifeLikeRecord::getHuifuId, String.valueOf(reviewId))
                 .eq(LifeLikeRecord::getDeleteFlag, 0);
+        lifeLikeIdentityHelper.applyLikerFilter(queryWrapper, likerScope);
         List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
 
         if (CollectionUtils.isEmpty(records)) {
             // 插入点赞记录
             LifeLikeRecord likeRecord = new LifeLikeRecord();
             likeRecord.setDianzanId(String.valueOf(userId));
+            lifeLikeIdentityHelper.normalizeBeforeSave(likeRecord);
             likeRecord.setHuifuId(String.valueOf(reviewId));
             likeRecord.setType("7");
             likeRecord.setCreatedTime(new Date());
@@ -1558,11 +1574,12 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
         log.info("StoreCommentServiceImpl.cancelLikeOrderReview?reviewId={}, userId={}", reviewId, userId);
 
         // 查询点赞记录
+        LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
         LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(LifeLikeRecord::getType, "7")
-                .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
                 .eq(LifeLikeRecord::getHuifuId, String.valueOf(reviewId))
                 .eq(LifeLikeRecord::getDeleteFlag, 0);
+        lifeLikeIdentityHelper.applyLikerFilter(queryWrapper, likerScope);
         List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
 
         if (!CollectionUtils.isEmpty(records)) {
@@ -1750,11 +1767,12 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
             
             // 检查是否已点赞
             if (currentUserId != null) {
+                LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(currentUserId);
                 LambdaQueryWrapper<LifeLikeRecord> likeWrapper = new LambdaQueryWrapper<>();
                 likeWrapper.eq(LifeLikeRecord::getType, "8")
-                        .eq(LifeLikeRecord::getDianzanId, String.valueOf(currentUserId))
                         .eq(LifeLikeRecord::getHuifuId, String.valueOf(comment.getId()))
                         .eq(LifeLikeRecord::getDeleteFlag, 0);
+                lifeLikeIdentityHelper.applyLikerFilter(likeWrapper, likerScope);
                 vo.setIsLiked(lifeLikeRecordMapper.selectCount(likeWrapper) > 0 ? 1 : 0);
             } else {
                 vo.setIsLiked(0);
@@ -1793,17 +1811,19 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
         }
 
         // 检查是否已点赞(type=8 表示评论点赞)
+        LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
         LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(LifeLikeRecord::getType, "8")
-                .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
                 .eq(LifeLikeRecord::getHuifuId, String.valueOf(commentId))
                 .eq(LifeLikeRecord::getDeleteFlag, 0);
+        lifeLikeIdentityHelper.applyLikerFilter(queryWrapper, likerScope);
         List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
 
         if (CollectionUtils.isEmpty(records)) {
             // 插入点赞记录
             LifeLikeRecord likeRecord = new LifeLikeRecord();
             likeRecord.setDianzanId(String.valueOf(userId));
+            lifeLikeIdentityHelper.normalizeBeforeSave(likeRecord);
             likeRecord.setHuifuId(String.valueOf(commentId));
             likeRecord.setType("8");
             likeRecord.setCreatedTime(new Date());
@@ -1840,11 +1860,12 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
         }
 
         // 查询点赞记录
+        LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
         LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(LifeLikeRecord::getType, "8")
-                .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
                 .eq(LifeLikeRecord::getHuifuId, String.valueOf(commentId))
                 .eq(LifeLikeRecord::getDeleteFlag, 0);
+        lifeLikeIdentityHelper.applyLikerFilter(queryWrapper, likerScope);
         List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
 
         if (!CollectionUtils.isEmpty(records)) {
@@ -1952,11 +1973,12 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
             
             // 检查是否已点赞
             if (currentUserId != null) {
+                LawyerLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(currentUserId);
                 LambdaQueryWrapper<LifeLikeRecord> likeWrapper = new LambdaQueryWrapper<>();
                 likeWrapper.eq(LifeLikeRecord::getType, "8")
-                        .eq(LifeLikeRecord::getDianzanId, String.valueOf(currentUserId))
                         .eq(LifeLikeRecord::getHuifuId, String.valueOf(reply.getId()))
                         .eq(LifeLikeRecord::getDeleteFlag, 0);
+                lifeLikeIdentityHelper.applyLikerFilter(likeWrapper, likerScope);
                 vo.setIsLiked(lifeLikeRecordMapper.selectCount(likeWrapper) > 0 ? 1 : 0);
             } else {
                 vo.setIsLiked(0);

+ 204 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/util/LawyerLikeRecordIdentityHelper.java

@@ -0,0 +1,204 @@
+package shop.alien.lawyer.util;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.LifeLikeRecord;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.mapper.LawyerUserMapper;
+import shop.alien.mapper.LifeUserMapper;
+import shop.alien.mapper.StoreUserMapper;
+import shop.alien.util.type.PhoneTypeIdResult;
+import shop.alien.util.type.TypeUtil;
+
+import java.util.Objects;
+
+/**
+ * alien-lawyer:life_like_record 点赞人身份工具。
+ */
+@Component
+@RequiredArgsConstructor
+public class LawyerLikeRecordIdentityHelper {
+
+    public static final int TYPE_USER = 1;
+    public static final int TYPE_STORE = 2;
+    public static final int TYPE_LAWYER = 3;
+
+    private final TypeUtil typeUtil;
+    private final LifeUserMapper lifeUserMapper;
+    private final StoreUserMapper storeUserMapper;
+    private final LawyerUserMapper lawyerUserMapper;
+
+    public static final class LawyerLikerScope {
+        private final String legacyDianzanId;
+        private final Integer dianzanUserType;
+        private final Integer dianzanRefId;
+
+        public LawyerLikerScope(String legacyDianzanId, Integer dianzanUserType, Integer dianzanRefId) {
+            this.legacyDianzanId = legacyDianzanId;
+            this.dianzanUserType = dianzanUserType;
+            this.dianzanRefId = dianzanRefId;
+        }
+
+        public String getLegacyDianzanId() {
+            return legacyDianzanId;
+        }
+
+        public Integer getDianzanUserType() {
+            return dianzanUserType;
+        }
+
+        public Integer getDianzanRefId() {
+            return dianzanRefId;
+        }
+
+        public boolean hasTypeRef() {
+            return dianzanUserType != null && dianzanRefId != null;
+        }
+    }
+
+    public LawyerLikerScope resolveFromDianzanId(String dianzanId) {
+        if (!StringUtils.hasText(dianzanId)) {
+            return new LawyerLikerScope(null, null, null);
+        }
+        String trimmed = dianzanId.trim();
+        if (typeUtil.containsUnderscore(trimmed)) {
+            PhoneTypeIdResult resolved = typeUtil.resolveTypeAndId(trimmed);
+            if (resolved != null) {
+                return new LawyerLikerScope(trimmed, resolved.getType(), resolved.getId());
+            }
+            return new LawyerLikerScope(trimmed, null, null);
+        }
+        try {
+            int refId = Integer.parseInt(trimmed);
+            return new LawyerLikerScope(trimmed, TYPE_USER, refId);
+        } catch (NumberFormatException e) {
+            return new LawyerLikerScope(trimmed, null, null);
+        }
+    }
+
+    public LawyerLikerScope fromLifeUserId(Integer lifeUserId) {
+        if (lifeUserId == null) {
+            return new LawyerLikerScope(null, null, null);
+        }
+        return new LawyerLikerScope(String.valueOf(lifeUserId), TYPE_USER, lifeUserId);
+    }
+
+    public LawyerLikerScope fromStoreUserId(Integer storeUserId) {
+        if (storeUserId == null) {
+            return new LawyerLikerScope(null, null, null);
+        }
+        StoreUser storeUser = storeUserMapper.selectById(storeUserId);
+        String legacy = storeUser != null && StringUtils.hasText(storeUser.getPhone())
+                ? "store_" + storeUser.getPhone()
+                : String.valueOf(storeUserId);
+        return new LawyerLikerScope(legacy, TYPE_STORE, storeUserId);
+    }
+
+    public LawyerLikerScope resolveFromRecord(LifeLikeRecord record) {
+        if (record == null
+                || record.getDianzanUserType() == null
+                || record.getDianzanRefId() == null) {
+            return new LawyerLikerScope(null, null, null);
+        }
+        String legacy = buildLegacyDianzanId(record.getDianzanUserType(), record.getDianzanRefId());
+        return new LawyerLikerScope(legacy, record.getDianzanUserType(), record.getDianzanRefId());
+    }
+
+    public void normalizeBeforeSave(LifeLikeRecord record) {
+        if (record == null) {
+            return;
+        }
+        if (record.getDianzanUserType() == null || record.getDianzanRefId() == null) {
+            LawyerLikerScope scope = resolveFromDianzanId(record.getDianzanId());
+            if (scope.hasTypeRef()) {
+                record.setDianzanUserType(scope.getDianzanUserType());
+                record.setDianzanRefId(scope.getDianzanRefId());
+            }
+        }
+        record.setDianzanId(null);
+    }
+
+    public void applyLikerFilter(LambdaQueryWrapper<LifeLikeRecord> wrapper, LawyerLikerScope scope) {
+        if (wrapper == null || scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq(LifeLikeRecord::getDianzanUserType, scope.getDianzanUserType())
+                .eq(LifeLikeRecord::getDianzanRefId, scope.getDianzanRefId());
+    }
+
+    public void applyLikerFilter(LambdaUpdateWrapper<LifeLikeRecord> wrapper, LawyerLikerScope scope) {
+        if (wrapper == null || scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq(LifeLikeRecord::getDianzanUserType, scope.getDianzanUserType())
+                .eq(LifeLikeRecord::getDianzanRefId, scope.getDianzanRefId());
+    }
+
+    public void applyLikerFilter(QueryWrapper<LifeLikeRecord> wrapper, LawyerLikerScope scope) {
+        applyLikerFilter(wrapper, scope, null);
+    }
+
+    public void applyLikerFilter(QueryWrapper<LifeLikeRecord> wrapper, LawyerLikerScope scope, String tableAlias) {
+        if (wrapper == null || scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        String prefix = StringUtils.hasText(tableAlias) ? tableAlias + "." : "";
+        wrapper.eq(prefix + "dianzan_user_type", scope.getDianzanUserType())
+                .eq(prefix + "dianzan_ref_id", scope.getDianzanRefId());
+    }
+
+    public String resolveComparableDianzanId(LifeLikeRecord record) {
+        LawyerLikerScope scope = resolveFromRecord(record);
+        if (scope.hasTypeRef()) {
+            String legacy = buildLegacyDianzanId(scope.getDianzanUserType(), scope.getDianzanRefId());
+            if (legacy != null) {
+                return legacy;
+            }
+            if (Objects.equals(scope.getDianzanUserType(), TYPE_USER)) {
+                return String.valueOf(scope.getDianzanRefId());
+            }
+        }
+        return record != null ? record.getDianzanId() : null;
+    }
+
+    public String buildLegacyDianzanId(Integer dianzanUserType, Integer dianzanRefId) {
+        if (dianzanUserType == null || dianzanRefId == null) {
+            return null;
+        }
+        switch (dianzanUserType) {
+            case TYPE_USER: {
+                LifeUser user = lifeUserMapper.selectById(dianzanRefId);
+                return user != null && StringUtils.hasText(user.getUserPhone())
+                        ? "user_" + user.getUserPhone() : String.valueOf(dianzanRefId);
+            }
+            case TYPE_STORE: {
+                StoreUser storeUser = storeUserMapper.selectById(dianzanRefId);
+                return storeUser != null && StringUtils.hasText(storeUser.getPhone())
+                        ? "store_" + storeUser.getPhone() : null;
+            }
+            case TYPE_LAWYER: {
+                LawyerUser lawyerUser = lawyerUserMapper.selectById(dianzanRefId);
+                return lawyerUser != null && StringUtils.hasText(lawyerUser.getPhone())
+                        ? "lawyer_" + lawyerUser.getPhone() : null;
+            }
+            default:
+                return null;
+        }
+    }
+
+    public void ensureEntityLegacyDianzanId(LifeLikeRecord record) {
+        if (record == null || record.getDianzanUserType() == null || record.getDianzanRefId() == null) {
+            return;
+        }
+        String legacy = buildLegacyDianzanId(record.getDianzanUserType(), record.getDianzanRefId());
+        if (legacy != null) {
+            record.setDianzanId(legacy);
+        }
+    }
+}

+ 4 - 0
alien-second/src/main/java/shop/alien/second/service/impl/RiskControlServiceImpl.java

@@ -21,6 +21,7 @@ import shop.alien.entity.store.LifeUser;
 import shop.alien.entity.store.LifeNotice;
 import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.second.SecondRiskControlRecordMapper;
 import shop.alien.mapper.second.SecondUserCreditMapper;
@@ -42,6 +43,8 @@ public class RiskControlServiceImpl extends ServiceImpl<SecondRiskControlRecordM
 
     private final SecondRiskControlRecordMapper secondRiskControlRecordMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final LifeUserMapper lifeUserMapper;
     private final AlienStoreFeign alienStoreFeign;
 
@@ -142,6 +145,7 @@ public class RiskControlServiceImpl extends ServiceImpl<SecondRiskControlRecordM
         JSONObject noticeMessage = new JSONObject();
         noticeMessage.put("message", "账号违反平台规约进行封禁处理");
         lifeNotice.setContext(noticeMessage.toJSONString());
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
         lifeNoticeMapper.insert(lifeNotice);
 
         // 给买家推送通知

+ 6 - 0
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsNotificationServiceImpl.java

@@ -9,6 +9,7 @@ import shop.alien.entity.store.LifeNotice;
 import shop.alien.entity.store.LifeUser;
 import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.second.feign.AlienStoreFeign;
 import shop.alien.second.service.SecondGoodsNotificationService;
@@ -33,6 +34,8 @@ public class SecondGoodsNotificationServiceImpl implements SecondGoodsNotificati
      */
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     /**
      * 店铺服务Feign接口
      */
@@ -62,6 +65,7 @@ public class SecondGoodsNotificationServiceImpl implements SecondGoodsNotificati
         lifeNotice.setContext(jsonObject.toJSONString());
         lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
         lifeNotice.setIsRead(0);
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
         lifeNoticeMapper.insert(lifeNotice);
         
         sendNotice("user_" + phone, lifeNotice);
@@ -91,6 +95,7 @@ public class SecondGoodsNotificationServiceImpl implements SecondGoodsNotificati
         lifeNotice.setContext(jsonObject.toJSONString());
         lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
         lifeNotice.setIsRead(0);
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
         lifeNoticeMapper.insert(lifeNotice);
         
         sendNotice("user_" + phone, lifeNotice);
@@ -120,6 +125,7 @@ public class SecondGoodsNotificationServiceImpl implements SecondGoodsNotificati
             lifeNotice.setContext(jsonObject.toJSONString());
             lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
             lifeNotice.setIsRead(0);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
             
             sendNotice("user_" + phone, lifeNotice);

+ 4 - 0
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsReportingServiceImpl.java

@@ -22,6 +22,7 @@ import shop.alien.second.util.AiUserViolationUtils;
 import shop.alien.second.util.JsonUtils;
 import shop.alien.util.common.Constants;
 import shop.alien.util.common.JwtUtil;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
@@ -42,6 +43,8 @@ public class SecondGoodsReportingServiceImpl implements SecondGoodsReportingServ
     @Autowired
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     @Autowired
     private final LifeUserViolationMapper lifeUserViolationMapper;
 
@@ -247,6 +250,7 @@ public class SecondGoodsReportingServiceImpl implements SecondGoodsReportingServ
                     String phoneId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");
                     // 举报通知
                     LifeNotice lifeNotice = getLifeNotice(lifeuserViolation);
+                    lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                     lifeNoticeMapper.insert(lifeNotice);
 
                     // 保存图片

+ 30 - 14
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsServiceImpl.java

@@ -36,6 +36,10 @@ import shop.alien.second.util.SecondHandSearchAiUtils;
 import shop.alien.util.common.Constants;
 import shop.alien.util.common.VideoUtils;
 import shop.alien.util.common.safe.*;
+import shop.alien.util.type.LifeCollectIdentityQuery;
+import shop.alien.util.type.LifeNoticeUtil;
+import shop.alien.second.util.SecondLikeRecordIdentityHelper;
+import shop.alien.second.util.SecondLikeRecordIdentityHelper.SecondLikerScope;
 
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
@@ -105,6 +109,8 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
      */
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     /**
      * 店铺图片Mapper,用于操作图片信息表
      */
@@ -120,6 +126,8 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
      */
     private final LifeLikeRecordMapper lifeLikeRecordMapper;
 
+    private final SecondLikeRecordIdentityHelper lifeLikeIdentityHelper;
+
     /**
      * 收藏Mapper,用于操作收藏记录表
      */
@@ -1299,6 +1307,7 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
         lifeNotice.setContext(jsonObject.toJSONString());
         lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
         lifeNotice.setIsRead(0);
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
         lifeNoticeMapper.insert(lifeNotice);
         sendNotice("user_"+ phone, lifeNotice);
     }
@@ -1346,6 +1355,7 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
         lifeNotice.setContext(jsonObject.toJSONString());
         lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
         lifeNotice.setIsRead(0);
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
         lifeNoticeMapper.insert(lifeNotice);
         sendNotice("user_"+ phone, lifeNotice);
     }
@@ -1589,11 +1599,12 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
         searchGoodsList.getRecords().forEach(goods -> {
             if (userId != null) {
                 // 设置点赞状态
-                LambdaUpdateWrapper<LifeLikeRecord> updateWrapper = new LambdaUpdateWrapper<>();
-                updateWrapper.eq(LifeLikeRecord::getHuifuId, goods.getId())
-                        .eq(LifeLikeRecord::getDianzanId, "user_"+phone).
-                        eq(LifeLikeRecord::getType, Constants.LikeType.SECOND_HAND_GOODS); // 6-二手商品
-                if (lifeLikeRecordMapper.selectCount(updateWrapper) > 0) {
+                SecondLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
+                LambdaQueryWrapper<LifeLikeRecord> likeQueryWrapper = new LambdaQueryWrapper<>();
+                likeQueryWrapper.eq(LifeLikeRecord::getHuifuId, goods.getId())
+                        .eq(LifeLikeRecord::getType, Constants.LikeType.SECOND_HAND_GOODS);
+                lifeLikeIdentityHelper.applyLikerFilter(likeQueryWrapper, likerScope);
+                if (lifeLikeRecordMapper.selectCount(likeQueryWrapper) > 0) {
                     goods.setLikeStatus(1);
                 }
             }
@@ -1610,9 +1621,10 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
         // 批量设置收藏状态
         searchGoodsList.getRecords().forEach(goods -> {
             if (userId != null) {
+                LifeCollectIdentityQuery.Scope collectScope = LifeCollectIdentityQuery.fromLifeUserId(userId);
                 LambdaUpdateWrapper<LifeCollect> updateWrapper = new LambdaUpdateWrapper<>();
-                updateWrapper.eq(LifeCollect::getUserId, "user_"+phone)
-                        .eq(LifeCollect::getBusinessType, Constants.CollectBusinessType.DEFAULT)
+                LifeCollectIdentityQuery.applyUserSide(updateWrapper, collectScope);
+                updateWrapper.eq(LifeCollect::getBusinessType, Constants.CollectBusinessType.DEFAULT)
                         .eq(LifeCollect::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED)
                         .eq(LifeCollect::getBusinessId, goods.getId());
                 if (lifeCollectMapper.selectCount(updateWrapper) > 0) {
@@ -1685,7 +1697,7 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
      */
     @Override
     public IPage<SecondGoodsVo> getCollectGoodsPage(IPage<SecondGoodsVo> page, int userId) {
-        LifeUser lifeUser = lifeUserMapper.selectById(userId);
+        LifeCollectIdentityQuery.Scope collectScope = LifeCollectIdentityQuery.fromLifeUserId(userId);
         // 获取商品屏蔽列表
         List<SecondGoods> shieldedGoodsList = getShieldedGoodsList(userId);
         // 提取屏蔽商品ID
@@ -1699,7 +1711,8 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
                 .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
                 .eq("lc.delete_flag", Constants.DeleteFlag.NOT_DELETED)
                 .notIn(CollectionUtil.isNotEmpty(shieldedGoodsIds), "sg.id", shieldedGoodsIds)
-                .eq("lc.user_id", "user_"+lifeUser.getUserPhone())
+                .eq("lc.user_id_user_type", collectScope.getUserType())
+                .eq("lc.user_id_ref_id", collectScope.getRefId())
                 .orderByDesc("lc.created_time");
         return secondGoodsMapper.getCollectGoodsPage(page, queryWrapper);
     }
@@ -1801,15 +1814,17 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
      */
     @Override
     public IPage<SecondGoodsVo> getLikeGoodsPage(IPage<SecondGoodsVo> page, int userId, String phone) {
+        SecondLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
         QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
         queryWrapper
-                // 可以查看已删除的商品数据
-//                .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
                 .eq("lc.delete_flag", Constants.DeleteFlag.NOT_DELETED)
-                .eq("lc.dianzan_id", "user_"+phone)
-                .eq("lc.type", Constants.LikeType.SECOND_HAND_GOODS) // 6-二手商品
-                .eq("sg.user_id", userId) // 用户ID
+                .eq("lc.type", Constants.LikeType.SECOND_HAND_GOODS)
+                .eq("sg.user_id", userId)
                 .orderByDesc("lc.created_time");
+        if (likerScope.hasTypeRef()) {
+            queryWrapper.eq("lc.dianzan_user_type", likerScope.getDianzanUserType())
+                    .eq("lc.dianzan_ref_id", likerScope.getDianzanRefId());
+        }
         return secondGoodsMapper.getLikeGoodsPage(page, queryWrapper);
     }
 
@@ -2325,6 +2340,7 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
             lifeNotice.setContext(jsonObject.toJSONString());
             lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
             lifeNotice.setIsRead(0);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
             sendShelveNotice("user_"+ phone, lifeNotice);
         } catch (Exception e) {

+ 35 - 11
alien-second/src/main/java/shop/alien/second/service/impl/SecondRecommendServiceImpl.java

@@ -2,6 +2,7 @@ package shop.alien.second.service.impl;
 
 import cn.hutool.core.collection.CollectionUtil;
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -19,6 +20,9 @@ import shop.alien.mapper.StoreImgMapper;
 import shop.alien.mapper.second.SecondRecommendMapper;
 import shop.alien.second.service.SecondRecommendService;
 import shop.alien.util.common.JwtUtil;
+import shop.alien.util.type.LifeCollectIdentityQuery;
+import shop.alien.util.type.LifeFansIdentityQuery;
+import shop.alien.util.type.TypeUtil;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
@@ -40,6 +44,9 @@ public class SecondRecommendServiceImpl extends ServiceImpl<SecondRecommendMappe
 
     @Autowired
     private LifeFansMapper lifeFansMapper;
+
+    @Autowired
+    private TypeUtil typeUtil;
     /**
      * 获取二手商品推荐列表
      * @param page 分页信息
@@ -94,7 +101,11 @@ public class SecondRecommendServiceImpl extends ServiceImpl<SecondRecommendMappe
             if (StringUtil.isBlank(phoneId)) {
                 return null;
             }
-            IPage<SecondGoodsRecommendVo> list = mapper.querySecondConcernByPage(page, userId, "user_" + phoneId, position);
+            LifeCollectIdentityQuery.Scope collectScope = resolveCollectScope(phoneId, userId);
+            LifeFansIdentityQuery.Scope fansScope = LifeFansIdentityQuery.resolve("user_" + phoneId, typeUtil);
+            IPage<SecondGoodsRecommendVo> list = mapper.querySecondConcernByPage(page, userId, "user_" + phoneId, position,
+                    collectScope.getUserType(), collectScope.getRefId(),
+                    fansScope.getUserType(), fansScope.getRefId());
             List<Integer> idList = list.getRecords().stream() // 创建流
                     .map(obj -> obj.getId())   // 提取每个元素的 ID
                     .collect(Collectors.toList());
@@ -140,14 +151,17 @@ public class SecondRecommendServiceImpl extends ServiceImpl<SecondRecommendMappe
             JSONObject data = JwtUtil.getCurrentUserInfo();
             String phoneId = null;
             String userId = null;
+            Integer lifeUserId = null;
             if (data != null) {
                 phoneId = data.getString("phone");
                 userId = data.getString("userId");
+                lifeUserId = data.getInteger("userId");
             }
             if (StringUtil.isBlank(phoneId)) {
                 return null;
             }
-            IPage<SecondGoodsRecommendVo> list = mapper.querySecondNewGoodsByPage(page, userId,"user_" + phoneId, position, radiusKm, timeRange);
+            LifeCollectIdentityQuery.Scope collectScope = resolveCollectScope(phoneId, lifeUserId);
+            IPage<SecondGoodsRecommendVo> list = mapper.querySecondNewGoodsByPage(page, userId,"user_" + phoneId, position, radiusKm, timeRange, collectScope.getUserType(), collectScope.getRefId());
             List<Integer> idList = list.getRecords().stream() // 创建流
                     .map(obj -> obj.getId())   // 提取每个元素的 ID
                     .collect(Collectors.toList());
@@ -192,9 +206,10 @@ public class SecondRecommendServiceImpl extends ServiceImpl<SecondRecommendMappe
                 return null;
             }
             String collectUserId = toLifeCollectUserId(phoneId);
+            LifeCollectIdentityQuery.Scope collectScope = LifeCollectIdentityQuery.resolve(collectUserId, typeUtil);
             SecondGoodsRecommendVo item = StringUtil.isBlank(position)
-                    ? mapper.querySecondGoodsDetailWithoutPosition(goodsId, collectUserId)
-                    : mapper.querySecondGoodsDetail(goodsId, collectUserId, position);
+                    ? mapper.querySecondGoodsDetailWithoutPosition(goodsId, collectUserId, collectScope.getUserType(), collectScope.getRefId())
+                    : mapper.querySecondGoodsDetail(goodsId, collectUserId, position, collectScope.getUserType(), collectScope.getRefId());
             applySecondGoodsDetailEnrichment(item, phoneId, goodsId);
             return item;
         } catch (Exception e) {
@@ -210,7 +225,8 @@ public class SecondRecommendServiceImpl extends ServiceImpl<SecondRecommendMappe
                 return null;
             }
             String collectUserId = toLifeCollectUserId(phoneId);
-            SecondGoodsRecommendVo item = mapper.querySecondGoodsDetail(goodsId, collectUserId, position);
+            LifeCollectIdentityQuery.Scope collectScope = LifeCollectIdentityQuery.resolve(collectUserId, typeUtil);
+            SecondGoodsRecommendVo item = mapper.querySecondGoodsDetail(goodsId, collectUserId, position, collectScope.getUserType(), collectScope.getRefId());
             applySecondGoodsDetailEnrichment(item, phoneId, goodsId);
             return item;
         } catch (Exception e) {
@@ -232,6 +248,13 @@ public class SecondRecommendServiceImpl extends ServiceImpl<SecondRecommendMappe
         return t.startsWith("user_") ? t : "user_" + t;
     }
 
+    private LifeCollectIdentityQuery.Scope resolveCollectScope(String phoneId, Integer lifeUserId) {
+        if (lifeUserId != null) {
+            return LifeCollectIdentityQuery.fromLifeUserId(lifeUserId);
+        }
+        return LifeCollectIdentityQuery.resolve(toLifeCollectUserId(phoneId), typeUtil);
+    }
+
     /**
      * 二手商品详情公共填充:图片列表(含视频排序)、关注状态、距离文案、价格展示;收藏状态由 SQL 已带出,此处补默认「未收藏」。
      */
@@ -259,12 +282,13 @@ public class SecondRecommendServiceImpl extends ServiceImpl<SecondRecommendMappe
             item.setImgList(imgArr);
         }
 
-        QueryWrapper<LifeFans> query1 = new QueryWrapper<>();
-        query1.lambda()
-                .eq(LifeFans::getFollowedId, item.getUserPhone())
-                .eq(LifeFans::getDeleteFlag, 0)
-                .eq(LifeFans::getFansId, toLifeCollectUserId(phoneId));
-        List<LifeFans> lifeFans = lifeFansMapper.selectList(query1);
+        LifeFansIdentityQuery.Scope fanScope = LifeFansIdentityQuery.resolve(toLifeCollectUserId(phoneId), typeUtil);
+        LifeFansIdentityQuery.Scope followedScope = LifeFansIdentityQuery.resolve(item.getUserPhone(), typeUtil);
+        LambdaQueryWrapper<LifeFans> fansQuery = new LambdaQueryWrapper<>();
+        fansQuery.eq(LifeFans::getDeleteFlag, 0);
+        LifeFansIdentityQuery.applyFansSide(fansQuery, fanScope);
+        LifeFansIdentityQuery.applyFollowedSide(fansQuery, followedScope);
+        List<LifeFans> lifeFans = lifeFansMapper.selectList(fansQuery);
         if (lifeFans.size() > 0) {
             item.setFansStatus(1);
         }

+ 13 - 1
alien-second/src/main/java/shop/alien/second/service/impl/SecondTradeRecordServiceImpl.java

@@ -30,6 +30,8 @@ import shop.alien.entity.second.vo.SellerEvaluationVo;
 import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.mapper.LifeMessageMapper;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeMessageUtil;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.StoreDictionaryMapper;
 import shop.alien.mapper.second.*;
@@ -64,6 +66,9 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
     private final SecondUserCreditRecordMapper secondUserCreditRecordMapper;
     private final LifeMessageMapper lifeMessageMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeMessageUtil lifeMessageUtil;
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final LifeUserMapper lifeUserMapper;
     private final AlienStoreFeign alienStoreFeign;
     private final StoreDictionaryMapper storeDictionaryMapper;
@@ -198,6 +203,7 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
         lifeMessage.setReceiverId(receiverId);
         lifeMessage.setType(messageType);
         lifeMessage.setContent(message.toJSONString());
+        lifeMessageUtil.fillUserTypeAndRefId(lifeMessage);
         lifeMessageMapper.insert(lifeMessage);
         message.put("createdTime", lifeMessage.getCreatedTime());
 
@@ -491,6 +497,7 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
             message.setReceiverId(receiverId);
             message.setContent(messageContent.toJSONString());
             message.setType("6");
+            lifeMessageUtil.fillUserTypeAndRefId(message);
             lifeMessageMapper.insert(message);
 
             // 推送已签到消息
@@ -815,6 +822,7 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
             lifeMessage.setReceiverId(buyerPhoneId);
             lifeMessage.setContent(message.toJSONString());
             lifeMessage.setType("5");
+            lifeMessageUtil.fillUserTypeAndRefId(lifeMessage);
             lifeMessageMapper.insert(lifeMessage);
 
             // 给买家推送消息
@@ -843,6 +851,7 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
             noticeMessage.put("otherSideImage", null == seller ? "" : seller.getUserImage());
             noticeMessage.put("message", "您有一笔交易即将开始, 请及时前往查看");
             lifeNotice.setContext(noticeMessage.toJSONString());
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
 
             // 给买家推送通知
@@ -862,6 +871,7 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
             lifeMessage.setReceiverId(sellerPhoneId);
             lifeMessage.setContent(message.toJSONString());
             lifeMessage.setType("5");
+            lifeMessageUtil.fillUserTypeAndRefId(lifeMessage);
             lifeMessageMapper.insert(lifeMessage);
 
             // 给卖家推送消息
@@ -890,6 +900,7 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
             noticeMessage.put("otherSideImage", null == buyer ? "" : buyer.getUserImage());
             noticeMessage.put("message", "您有一笔交易即将开始, 请及时前往查看");
             lifeNotice.setContext(noticeMessage.toJSONString());
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
             lifeNoticeMapper.insert(lifeNotice);
 
             // 给卖家推送通知
@@ -1130,7 +1141,8 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
                     LambdaQueryWrapper<LifeMessage> wrapper = new LambdaQueryWrapper<>();
                     wrapper.eq(LifeMessage::getType, 10);
                     wrapper.isNull(LifeMessage::getContent);
-                    wrapper.apply("((receiver_id = 'user_" + otherUser.getUserPhone() + "' and sender_id = 'user_" + user.getUserPhone() + "') or (receiver_id = 'user_" + user.getUserPhone() + "' and sender_id = 'user_" + otherUser.getUserPhone() + "'))");
+                    lifeMessageUtil.applyConversationQuery(wrapper,
+                            "user_" + user.getUserPhone(), "user_" + otherUser.getUserPhone());
                     List<Integer> messageIds = lifeMessageMapper.selectList(wrapper).stream().map(LifeMessage::getId).collect(Collectors.toList());
 
                     if (CollectionUtils.isNotEmpty(messageIds)) {

+ 12 - 0
alien-second/src/main/java/shop/alien/second/task/Task.java

@@ -19,6 +19,8 @@ import shop.alien.entity.store.LifeUser;
 import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.mapper.LifeMessageMapper;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeMessageUtil;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.second.SecondGoodsMapper;
 import shop.alien.mapper.second.SecondTradeOperationMapper;
@@ -40,6 +42,10 @@ public class Task {
 
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
+    private final LifeMessageUtil lifeMessageUtil;
+
     private final LifeUserMapper lifeUserMapper;
 
     private final LifeMessageMapper lifeMessageMapper;
@@ -97,6 +103,7 @@ public class Task {
                 lifeMessage.setReceiverId(buyerPhoneId);
                 lifeMessage.setContent(message.toJSONString());
                 lifeMessage.setType("5");
+                lifeMessageUtil.fillUserTypeAndRefId(lifeMessage);
                 lifeMessageMapper.insert(lifeMessage);
 
                 // 给买家推送消息
@@ -125,6 +132,7 @@ public class Task {
                 noticeMessage.put("otherSideImage", null == seller ? "" : seller.getUserImage());
                 noticeMessage.put("message", "您有一笔交易即将开始, 请及时前往查看");
                 lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                 lifeNoticeMapper.insert(lifeNotice);
 
                 // 给买家推送通知
@@ -144,6 +152,7 @@ public class Task {
                 lifeMessage.setReceiverId(sellerPhoneId);
                 lifeMessage.setContent(message.toJSONString());
                 lifeMessage.setType("5");
+                lifeMessageUtil.fillUserTypeAndRefId(lifeMessage);
                 lifeMessageMapper.insert(lifeMessage);
 
                 // 给卖家推送消息
@@ -172,6 +181,7 @@ public class Task {
                 noticeMessage.put("otherSideImage", null == buyer ? "" : buyer.getUserImage());
                 noticeMessage.put("message", "您有一笔交易即将开始, 请及时前往查看");
                 lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                 lifeNoticeMapper.insert(lifeNotice);
 
                 // 给卖家推送通知
@@ -238,6 +248,7 @@ public class Task {
                 noticeMessage.put("tradeStatus", tradeRecord.getTradeStatus());
                 noticeMessage.put("message", "您有一笔交易已完成, 请前往确认");
                 lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                 lifeNoticeMapper.insert(lifeNotice);
 
                 // 给买家推送通知
@@ -259,6 +270,7 @@ public class Task {
                 lifeNotice.setTitle("商品是否交易成功");
                 lifeNotice.setNoticeType(1);
                 lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                 lifeNoticeMapper.insert(lifeNotice);
 
                 // 给卖家推送通知

+ 7 - 15
alien-second/src/main/java/shop/alien/second/util/AiUserViolationUtils.java

@@ -1,6 +1,7 @@
 package shop.alien.second.util;
 
 import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -20,9 +21,11 @@ import shop.alien.entity.store.LifeMessage;
 import shop.alien.entity.store.LifeUserViolation;
 import shop.alien.entity.store.vo.LifeUserVo;
 import shop.alien.mapper.LifeMessageMapper;
+import shop.alien.util.type.LifeMessageUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.LifeUserViolationMapper;
 import shop.alien.mapper.second.SecondGoodsMapper;
+import shop.alien.util.type.LifeMessageUtil;
 
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -38,6 +41,7 @@ public class AiUserViolationUtils {
     private final RestTemplate restTemplate;
     private final LifeUserMapper lifeUserMapper;
     private final LifeMessageMapper lifeMessageMapper;
+    private final LifeMessageUtil lifeMessageUtil;
     private final SecondGoodsMapper secondGoodsMapper;
 
     @Value("${third-party-login.base-url}")
@@ -391,22 +395,10 @@ public class AiUserViolationUtils {
      */
     public List<LifeMessage> getRecentChatMessages(String userReporterUserById, String userReportedUserById) {
         // 构建查询条件
-        QueryWrapper<LifeMessage> queryWrapper = new QueryWrapper<>();
-
-        // 查询双方的聊天记录
-        queryWrapper.and(wrapper -> wrapper
-                        .eq("sender_id", userReporterUserById)
-                        .eq("receiver_id", userReportedUserById))
-                .or(wrapper -> wrapper
-                        .eq("sender_id", userReportedUserById)
-                        .eq("receiver_id", userReporterUserById));
-
-        // 按时间倒序排列
-        queryWrapper.orderByDesc("created_time");
-
-        // 限制50条记录
+        LambdaQueryWrapper<LifeMessage> queryWrapper = new LambdaQueryWrapper<>();
+        lifeMessageUtil.applyConversationQuery(queryWrapper, userReporterUserById, userReportedUserById);
+        queryWrapper.orderByDesc(LifeMessage::getCreatedTime);
         queryWrapper.last("LIMIT 50");
-
         return lifeMessageMapper.selectList(queryWrapper);
     }
 

+ 204 - 0
alien-second/src/main/java/shop/alien/second/util/SecondLikeRecordIdentityHelper.java

@@ -0,0 +1,204 @@
+package shop.alien.second.util;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.LifeLikeRecord;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.mapper.LawyerUserMapper;
+import shop.alien.mapper.LifeUserMapper;
+import shop.alien.mapper.StoreUserMapper;
+import shop.alien.util.type.PhoneTypeIdResult;
+import shop.alien.util.type.TypeUtil;
+
+import java.util.Objects;
+
+/**
+ * alien-second:life_like_record 点赞人身份工具。
+ */
+@Component
+@RequiredArgsConstructor
+public class SecondLikeRecordIdentityHelper {
+
+    public static final int TYPE_USER = 1;
+    public static final int TYPE_STORE = 2;
+    public static final int TYPE_LAWYER = 3;
+
+    private final TypeUtil typeUtil;
+    private final LifeUserMapper lifeUserMapper;
+    private final StoreUserMapper storeUserMapper;
+    private final LawyerUserMapper lawyerUserMapper;
+
+    public static final class SecondLikerScope {
+        private final String legacyDianzanId;
+        private final Integer dianzanUserType;
+        private final Integer dianzanRefId;
+
+        public SecondLikerScope(String legacyDianzanId, Integer dianzanUserType, Integer dianzanRefId) {
+            this.legacyDianzanId = legacyDianzanId;
+            this.dianzanUserType = dianzanUserType;
+            this.dianzanRefId = dianzanRefId;
+        }
+
+        public String getLegacyDianzanId() {
+            return legacyDianzanId;
+        }
+
+        public Integer getDianzanUserType() {
+            return dianzanUserType;
+        }
+
+        public Integer getDianzanRefId() {
+            return dianzanRefId;
+        }
+
+        public boolean hasTypeRef() {
+            return dianzanUserType != null && dianzanRefId != null;
+        }
+    }
+
+    public SecondLikerScope resolveFromDianzanId(String dianzanId) {
+        if (!StringUtils.hasText(dianzanId)) {
+            return new SecondLikerScope(null, null, null);
+        }
+        String trimmed = dianzanId.trim();
+        if (typeUtil.containsUnderscore(trimmed)) {
+            PhoneTypeIdResult resolved = typeUtil.resolveTypeAndId(trimmed);
+            if (resolved != null) {
+                return new SecondLikerScope(trimmed, resolved.getType(), resolved.getId());
+            }
+            return new SecondLikerScope(trimmed, null, null);
+        }
+        try {
+            int refId = Integer.parseInt(trimmed);
+            return new SecondLikerScope(trimmed, TYPE_USER, refId);
+        } catch (NumberFormatException e) {
+            return new SecondLikerScope(trimmed, null, null);
+        }
+    }
+
+    public SecondLikerScope fromLifeUserId(Integer lifeUserId) {
+        if (lifeUserId == null) {
+            return new SecondLikerScope(null, null, null);
+        }
+        return new SecondLikerScope(String.valueOf(lifeUserId), TYPE_USER, lifeUserId);
+    }
+
+    public SecondLikerScope fromStoreUserId(Integer storeUserId) {
+        if (storeUserId == null) {
+            return new SecondLikerScope(null, null, null);
+        }
+        StoreUser storeUser = storeUserMapper.selectById(storeUserId);
+        String legacy = storeUser != null && StringUtils.hasText(storeUser.getPhone())
+                ? "store_" + storeUser.getPhone()
+                : String.valueOf(storeUserId);
+        return new SecondLikerScope(legacy, TYPE_STORE, storeUserId);
+    }
+
+    public SecondLikerScope resolveFromRecord(LifeLikeRecord record) {
+        if (record == null
+                || record.getDianzanUserType() == null
+                || record.getDianzanRefId() == null) {
+            return new SecondLikerScope(null, null, null);
+        }
+        String legacy = buildLegacyDianzanId(record.getDianzanUserType(), record.getDianzanRefId());
+        return new SecondLikerScope(legacy, record.getDianzanUserType(), record.getDianzanRefId());
+    }
+
+    public void normalizeBeforeSave(LifeLikeRecord record) {
+        if (record == null) {
+            return;
+        }
+        if (record.getDianzanUserType() == null || record.getDianzanRefId() == null) {
+            SecondLikerScope scope = resolveFromDianzanId(record.getDianzanId());
+            if (scope.hasTypeRef()) {
+                record.setDianzanUserType(scope.getDianzanUserType());
+                record.setDianzanRefId(scope.getDianzanRefId());
+            }
+        }
+        record.setDianzanId(null);
+    }
+
+    public void applyLikerFilter(LambdaQueryWrapper<LifeLikeRecord> wrapper, SecondLikerScope scope) {
+        if (wrapper == null || scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq(LifeLikeRecord::getDianzanUserType, scope.getDianzanUserType())
+                .eq(LifeLikeRecord::getDianzanRefId, scope.getDianzanRefId());
+    }
+
+    public void applyLikerFilter(LambdaUpdateWrapper<LifeLikeRecord> wrapper, SecondLikerScope scope) {
+        if (wrapper == null || scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        wrapper.eq(LifeLikeRecord::getDianzanUserType, scope.getDianzanUserType())
+                .eq(LifeLikeRecord::getDianzanRefId, scope.getDianzanRefId());
+    }
+
+    public void applyLikerFilter(QueryWrapper<LifeLikeRecord> wrapper, SecondLikerScope scope) {
+        applyLikerFilter(wrapper, scope, null);
+    }
+
+    public void applyLikerFilter(QueryWrapper<LifeLikeRecord> wrapper, SecondLikerScope scope, String tableAlias) {
+        if (wrapper == null || scope == null || !scope.hasTypeRef()) {
+            return;
+        }
+        String prefix = StringUtils.hasText(tableAlias) ? tableAlias + "." : "";
+        wrapper.eq(prefix + "dianzan_user_type", scope.getDianzanUserType())
+                .eq(prefix + "dianzan_ref_id", scope.getDianzanRefId());
+    }
+
+    public String resolveComparableDianzanId(LifeLikeRecord record) {
+        SecondLikerScope scope = resolveFromRecord(record);
+        if (scope.hasTypeRef()) {
+            String legacy = buildLegacyDianzanId(scope.getDianzanUserType(), scope.getDianzanRefId());
+            if (legacy != null) {
+                return legacy;
+            }
+            if (Objects.equals(scope.getDianzanUserType(), TYPE_USER)) {
+                return String.valueOf(scope.getDianzanRefId());
+            }
+        }
+        return record != null ? record.getDianzanId() : null;
+    }
+
+    public String buildLegacyDianzanId(Integer dianzanUserType, Integer dianzanRefId) {
+        if (dianzanUserType == null || dianzanRefId == null) {
+            return null;
+        }
+        switch (dianzanUserType) {
+            case TYPE_USER: {
+                LifeUser user = lifeUserMapper.selectById(dianzanRefId);
+                return user != null && StringUtils.hasText(user.getUserPhone())
+                        ? "user_" + user.getUserPhone() : String.valueOf(dianzanRefId);
+            }
+            case TYPE_STORE: {
+                StoreUser storeUser = storeUserMapper.selectById(dianzanRefId);
+                return storeUser != null && StringUtils.hasText(storeUser.getPhone())
+                        ? "store_" + storeUser.getPhone() : null;
+            }
+            case TYPE_LAWYER: {
+                LawyerUser lawyerUser = lawyerUserMapper.selectById(dianzanRefId);
+                return lawyerUser != null && StringUtils.hasText(lawyerUser.getPhone())
+                        ? "lawyer_" + lawyerUser.getPhone() : null;
+            }
+            default:
+                return null;
+        }
+    }
+
+    public void ensureEntityLegacyDianzanId(LifeLikeRecord record) {
+        if (record == null || record.getDianzanUserType() == null || record.getDianzanRefId() == null) {
+            return;
+        }
+        String legacy = buildLegacyDianzanId(record.getDianzanUserType(), record.getDianzanRefId());
+        if (legacy != null) {
+            record.setDianzanId(legacy);
+        }
+    }
+}

+ 1 - 1
alien-second/src/main/resources/bootstrap.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: test
+    active: dev

+ 4 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformLoginController.java

@@ -109,6 +109,10 @@ public class StorePlatformLoginController {
         if (storeUser.getStatus() == 1) {
             return R.fail("账号被禁用");
         }
+        if (storeUser.getLogoutFlag() != null && StoreUser.LOGOUT_FLAG_DONE == storeUser.getLogoutFlag()
+                && !Integer.valueOf(-1).equals(storeUser.getStatus())) {
+            return R.fail("账号已注销");
+        }
 
         return Optional.ofNullable(storeUser).
                 map(user -> isPassword ? checkPassword(user, password) : storePlatformLoginService.createToKen(user)).

+ 8 - 1
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/LifeGroupBuyPlatformServiceImpl.java

@@ -25,6 +25,8 @@ import shop.alien.mapper.*;
 import shop.alien.mapper.storePlantform.LifeGroupBuyMainPlantformMapper;
 import shop.alien.storeplatform.service.LifeGroupBuyPlatformService;
 import shop.alien.util.common.JwtUtil;
+import shop.alien.util.type.LifeCollectIdentityQuery;
+import shop.alien.util.type.TypeUtil;
 
 import java.math.BigDecimal;
 import java.util.*;
@@ -68,6 +70,8 @@ public class LifeGroupBuyPlatformServiceImpl extends ServiceImpl<LifeGroupBuyMai
      */
     private final LifeCollectMapper lifeCollectMapper;
 
+    private final TypeUtil typeUtil;
+
     /**
      * 审核记录 Mapper
      */
@@ -338,7 +342,10 @@ public class LifeGroupBuyPlatformServiceImpl extends ServiceImpl<LifeGroupBuyMai
         BeanUtils.copyProperties(lifeGroupBuyMain, platform);
 
         // 是否收藏了该团购
-        LifeCollect lifeCollect = lifeCollectMapper.selectOne(new QueryWrapper<LifeCollect>().eq("user_id", userId).eq("coupon_id", id));
+        LambdaQueryWrapper<LifeCollect> collectQuery = new LambdaQueryWrapper<>();
+        LifeCollectIdentityQuery.applyUserSide(collectQuery, LifeCollectIdentityQuery.resolve(userId, typeUtil));
+        collectQuery.eq(LifeCollect::getCouponId, id);
+        LifeCollect lifeCollect = lifeCollectMapper.selectOne(collectQuery);
         dto.setIsCollect("0");
         if (!Objects.isNull(lifeCollect)) {
             dto.setIsCollect("1");

+ 10 - 5
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/MerchantUserServiceImpl.java

@@ -13,6 +13,7 @@ import shop.alien.entity.store.vo.StoreUserVo;
 import shop.alien.mapper.*;
 import shop.alien.storeplatform.service.MerchantUserService;
 import shop.alien.storeplatform.util.LoginUserUtil;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -44,6 +45,8 @@ public class MerchantUserServiceImpl implements MerchantUserService {
     private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
     private final LifeDiscountCouponUserMapper lifeDiscountCouponUserMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     @Override
     public StoreUserVo getMerchantByPhone(String phone) {
         log.info("MerchantUserServiceImpl.getMerchantByPhone - 查询商户用户: phone={}", phone);
@@ -61,8 +64,10 @@ public class MerchantUserServiceImpl implements MerchantUserService {
             return storeUserVo;
         }
         
-        // 3. 如果用户状态为注销中(status=-1),计算注销倒计时
-        if (user.getStatus() == -1) {
+        // 3. 注销冷静期(logout_flag=2,兼容旧数据 status=-1)计算倒计时
+        boolean logoutCooling = StoreUser.LOGOUT_FLAG_COOLING == user.getLogoutFlag()
+                || (StoreUser.LOGOUT_FLAG_DONE == user.getLogoutFlag() && Integer.valueOf(-1).equals(user.getStatus()));
+        if (logoutCooling) {
             try {
                 // 将注销时间转换为LocalDateTime
                 LocalDateTime logoutDateTime = user.getLogoutTime()
@@ -370,8 +375,8 @@ public class MerchantUserServiceImpl implements MerchantUserService {
             if (StringUtils.isNotEmpty(storeUser.getPhone())) {
                 String receiverId = "store_" + storeUser.getPhone();
                 LambdaQueryWrapper<LifeNotice> noticeWrapper = new LambdaQueryWrapper<>();
-                noticeWrapper.eq(LifeNotice::getReceiverId, receiverId)
-                        .eq(LifeNotice::getTitle, "入驻店铺申请");
+                lifeNoticeUtil.applyReceiverQuery(noticeWrapper, receiverId);
+                noticeWrapper.eq(LifeNotice::getTitle, "入驻店铺申请");
                 int deletedNoticeCount = lifeNoticeMapper.delete(noticeWrapper);
                 log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除入驻申请通知: count={}", deletedNoticeCount);
             }
@@ -422,7 +427,7 @@ public class MerchantUserServiceImpl implements MerchantUserService {
             if (StringUtils.isNotEmpty(storeUser.getPhone())) {
                 String receiverId = "store_" + storeUser.getPhone();
                 LambdaQueryWrapper<LifeNotice> allNoticeWrapper = new LambdaQueryWrapper<>();
-                allNoticeWrapper.eq(LifeNotice::getReceiverId, receiverId);
+                lifeNoticeUtil.applyReceiverQuery(allNoticeWrapper, receiverId);
                 int deletedAllNoticeCount = lifeNoticeMapper.delete(allNoticeWrapper);
                 log.info("MerchantUserServiceImpl.resetToInitialStatus - 删除所有通知消息: count={}", deletedAllNoticeCount);
             }

+ 6 - 3
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/NoticeServiceImpl.java

@@ -19,6 +19,7 @@ import shop.alien.mapper.LifeMessageMapper;
 import shop.alien.mapper.LifeNoticeMapper;
 import shop.alien.mapper.LifeUserViolationMapper;
 import shop.alien.storeplatform.service.NoticeService;
+import shop.alien.util.type.LifeNoticeUtil;
 
 import java.util.*;
 import java.util.stream.Collectors;
@@ -40,6 +41,8 @@ public class NoticeServiceImpl implements NoticeService {
     
     private final LifeUserViolationMapper lifeUserViolationMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     @Override
     public JSONObject getNoticeStatistics(String receiverId) {
         log.info("NoticeServiceImpl.getNoticeStatistics - 查询通知统计: receiverId={}", receiverId);
@@ -48,7 +51,7 @@ public class NoticeServiceImpl implements NoticeService {
 
         // 1. 查询系统通知和订单提醒(noticeType: 1-系统通知, 2-订单提醒)
         LambdaQueryWrapper<LifeNotice> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(LifeNotice::getReceiverId, receiverId);
+        lifeNoticeUtil.applyReceiverQuery(queryWrapper, receiverId);
         queryWrapper.in(LifeNotice::getNoticeType, 1, 2);
         List<LifeNotice> lifeNoticeList = lifeNoticeMapper.selectList(queryWrapper);
 
@@ -117,7 +120,7 @@ public class NoticeServiceImpl implements NoticeService {
 
         // 1. 查询通知列表
         LambdaQueryWrapper<LifeNotice> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(LifeNotice::getReceiverId, receiverId);
+        lifeNoticeUtil.applyReceiverQuery(queryWrapper, receiverId);
         queryWrapper.eq(LifeNotice::getNoticeType, noticeType);
         queryWrapper.eq(LifeNotice::getDeleteFlag, 0);
         queryWrapper.orderByDesc(LifeNotice::getCreatedTime);
@@ -266,7 +269,7 @@ public class NoticeServiceImpl implements NoticeService {
         LambdaUpdateWrapper<LifeNotice> wrapper = new LambdaUpdateWrapper<>();
         
         // 1. 接收人条件(必须)
-        wrapper.eq(LifeNotice::getReceiverId, receiverId);
+        lifeNoticeUtil.applyReceiverUpdate(wrapper, receiverId);
         
         // 2. 只更新未读的通知
         wrapper.eq(LifeNotice::getIsRead, 0);

+ 4 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivityServiceImpl.java

@@ -29,6 +29,7 @@ import shop.alien.entity.storePlatform.vo.StoreOperationalActivityVO;
 import shop.alien.mapper.LifeCouponMapper;
 import shop.alien.mapper.LifeDiscountCouponMapper;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.StoreImgMapper;
 import shop.alien.mapper.StoreUserMapper;
 import shop.alien.mapper.storePlantform.StoreOperationalActivityMapper;
@@ -79,6 +80,8 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
 
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     private final AlienAIFeign alienAIFeign;
 
     private final AlienStoreFeign alienStoreFeign;
@@ -673,6 +676,7 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
         lifeNotice.setIsRead(0);
 
         // 保存通知到数据库
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
         lifeNoticeMapper.insert(lifeNotice);
 
         // 通过WebSocket发送实时通知

+ 4 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivitySignupServiceImpl.java

@@ -17,6 +17,7 @@ import shop.alien.entity.storePlatform.StoreOperationalActivity;
 import shop.alien.entity.storePlatform.StoreOperationalActivitySignup;
 import shop.alien.entity.storePlatform.vo.StoreOperationalActivitySignupVO;
 import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.util.type.LifeNoticeUtil;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.storePlantform.StoreOperationalActivityMapper;
 import shop.alien.mapper.storePlantform.StoreOperationalActivitySignupMapper;
@@ -43,6 +44,8 @@ public class OperationalActivitySignupServiceImpl implements OperationalActivity
     private final StoreOperationalActivityMapper activityMapper;
     private final LifeUserMapper lifeUserMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeNoticeUtil lifeNoticeUtil;
     private final AlienStoreFeign alienStoreFeign;
 
     @Override
@@ -398,6 +401,7 @@ public class OperationalActivitySignupServiceImpl implements OperationalActivity
         lifeNotice.setIsRead(0);
 
         // 保存通知到数据库
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
         lifeNoticeMapper.insert(lifeNotice);
 
         // 通过WebSocket发送实时通知

+ 25 - 10
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreBusinessServiceImpl.java

@@ -44,6 +44,10 @@ import shop.alien.util.common.DistanceUtil;
 import shop.alien.util.common.constant.CouponStatusEnum;
 import shop.alien.util.common.constant.CouponTypeEnum;
 import shop.alien.util.common.constant.OrderStatusEnum;
+import shop.alien.util.type.LifeCollectIdentityQuery;
+import shop.alien.util.type.LifeFansIdentityQuery;
+import shop.alien.util.type.LifeNoticeUtil;
+import shop.alien.util.type.TypeUtil;
 
 import javax.annotation.Resource;
 import java.io.File;
@@ -110,6 +114,8 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
 
     private final LifeCollectMapper lifeCollectMapper;
 
+    private final TypeUtil typeUtil;
+
     private final StoreStaffConfigMapper storeStaffConfigMapper;
 
     private final StoreClockInMapper storeClockInMapper;
@@ -122,6 +128,8 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
 
     private final LifeNoticeMapper lifeNoticeMapper;
 
+    private final LifeNoticeUtil lifeNoticeUtil;
+
     private final LifeGroupBuyMainMapper lifeGroupBuyMainMapper;
 
 
@@ -747,6 +755,7 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
         lifeMessage.setSenderId("system");
         lifeMessage.setIsRead(0);
         lifeMessage.setNoticeType(1);
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
         lifeNoticeMapper.insert(lifeMessage);
 
         WebSocketVo websocketVo = new WebSocketVo();
@@ -1137,7 +1146,8 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
         }
         // 当前登录用户是否收藏
         LambdaUpdateWrapper<LifeCollect> shouCangWrapper = new LambdaUpdateWrapper<>();
-        shouCangWrapper.eq(LifeCollect::getUserId, userId).eq(LifeCollect::getStoreId, storeId);
+        LifeCollectIdentityQuery.applyUserSide(shouCangWrapper, LifeCollectIdentityQuery.resolve(userId, typeUtil));
+        shouCangWrapper.eq(LifeCollect::getStoreId, storeId);
         List<LifeCollect> shouCangList = lifeCollectMapper.selectList(shouCangWrapper);
         if (null == shouCangList || shouCangList.isEmpty()) {
             result.setCollection(0);
@@ -1241,11 +1251,6 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
         result.setClockInNum(clockInNum);
 
         // 获取店铺动态列表
-        QueryWrapper<LifeUserDynamics> dynamicsWrapper = new QueryWrapper<>();
-        dynamicsWrapper.eq("phone_id", "store_" + result.getStorePhone()).orderByDesc("lud.created_time");
-        dynamicsWrapper.eq("lud.delete_flag", 0);
-        //List<LifeUserDynamicsVo> storeDynamicslist = lifeUserDynamicsMapper.getStoreDynamicslist(userId, dynamicsWrapper);
-
         LambdaQueryWrapper<LifeBlacklist> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
         lambdaQueryWrapper1.eq(LifeBlacklist :: getBlockerId, userId);
         lambdaQueryWrapper1.eq(LifeBlacklist :: getBlockedPhoneId, "store_" + result.getStorePhone());
@@ -1254,7 +1259,12 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
 
         //判断没有拉黑当前门店账户 查出门店动态
         if(blacklist == null){
-            storeDynamicslist = lifeUserDynamicsMapper.getStoreDynamicslist(userId, "store_" + result.getStorePhone());
+            StoreUser storePublisherUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>()
+                    .eq(StoreUser::getPhone, result.getStorePhone())
+                    .eq(StoreUser::getDeleteFlag, 0)
+                    .last("LIMIT 1"));
+            Integer storeRefId = storePublisherUser != null ? storePublisherUser.getId() : null;
+            storeDynamicslist = lifeUserDynamicsMapper.getStoreDynamicslist(userId, 2, storeRefId);
         }
 
         List<String> followList = new ArrayList<>();
@@ -1264,16 +1274,18 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
             LifeUser lifeUser = lifeUserMapper.selectById(userId);
             if (lifeUser != null && StringUtils.isNotEmpty(lifeUser.getUserPhone())) {
                 // 查询我的关注信息,构建关注者ID列表
+                LifeFansIdentityQuery.Scope storeViewerScope = LifeFansIdentityQuery.resolve("user_" + result.getStorePhone(), typeUtil);
                 LambdaQueryWrapper<LifeFans> lifeFansWrapper = new LambdaQueryWrapper<>();
-                lifeFansWrapper.eq(LifeFans::getFansId, "user_" + result.getStorePhone());
+                lifeFansWrapper.eq(LifeFans::getDeleteFlag, 0);
+                LifeFansIdentityQuery.applyFansSide(lifeFansWrapper, storeViewerScope);
                 List<LifeFans> lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
                 if (!CollectionUtils.isEmpty(lifeFansList)) {
                     followList = lifeFansList.stream().map(LifeFans::getFollowedId).collect(Collectors.toList());
                 }
 
-                // 查询我的粉丝信息,构建粉丝ID列表
                 lifeFansWrapper = new LambdaQueryWrapper<>();
-                lifeFansWrapper.eq(LifeFans::getFollowedId, "user_" + result.getStorePhone());
+                lifeFansWrapper.eq(LifeFans::getDeleteFlag, 0);
+                LifeFansIdentityQuery.applyFollowedSide(lifeFansWrapper, storeViewerScope);
                 lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
                 if (!CollectionUtils.isEmpty(lifeFansList)) {
                     fansList = lifeFansList.stream().map(LifeFans::getFansId).collect(Collectors.toList());
@@ -1378,6 +1390,7 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
         lifeNotice.setContext(jsonObject.toJSONString());
         lifeNotice.setNoticeType(1); // 系统通知
         lifeNotice.setIsRead(0);
+        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
         lifeNoticeMapper.insert(lifeNotice);
 
         WebSocketVo websocketVo = new WebSocketVo();
@@ -1887,6 +1900,7 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
             if (num > 0) {
                 // 发送通知
                 LifeNotice lifeMessage = getLifeNotice(storeInfo);
+                lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
                 lifeNoticeMapper.insert(lifeMessage);
                 WebSocketVo websocketVo = new WebSocketVo();
                 websocketVo.setSenderId("system");
@@ -1944,6 +1958,7 @@ public class StoreBusinessServiceImpl extends ServiceImpl<StoreInfoMapper, Store
             lifeMessage.setSenderId("system");
             lifeMessage.setIsRead(0);
             lifeMessage.setNoticeType(1);
+            lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
             lifeNoticeMapper.insert(lifeMessage);
 
             WebSocketVo websocketVo = new WebSocketVo();

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff