Ver código fonte

迁移gateway服务,创建价目表实体

lutong 3 meses atrás
pai
commit
8dc7e820d3
74 arquivos alterados com 5054 adições e 5 exclusões
  1. 69 0
      alien-service-entity/src/main/java/shop/alien/entity/delicious/DeliciousCombo.java
  2. 47 0
      alien-service-entity/src/main/java/shop/alien/entity/delicious/DeliciousComboProd.java
  3. 72 0
      alien-service-entity/src/main/java/shop/alien/entity/delicious/DeliciousCommodityIdx.java
  4. 69 0
      alien-service-entity/src/main/java/shop/alien/entity/delicious/DeliciousProduct.java
  5. 81 0
      alien-service-entity/src/main/java/shop/alien/entity/store/ActivityInviteConfig.java
  6. 75 0
      alien-service-entity/src/main/java/shop/alien/entity/store/ActivityInviteLog.java
  7. 78 0
      alien-service-entity/src/main/java/shop/alien/entity/store/LifeSys.java
  8. 108 0
      alien-service-entity/src/main/java/shop/alien/entity/store/LifeSysMenu.java
  9. 47 0
      alien-service-entity/src/main/java/shop/alien/entity/store/UserPoint.java
  10. 20 0
      alien-service-entity/src/main/java/shop/alien/entity/store/dto/SystemLoginDto.java
  11. 38 0
      alien-service-entity/src/main/java/shop/alien/entity/store/dto/SystemRegisterDto.java
  12. 47 0
      alien-service-entity/src/main/java/shop/alien/entity/store/vo/ActivityInviteLogVo.java
  13. 32 0
      alien-service-entity/src/main/java/shop/alien/entity/store/vo/SystemLoginVo.java
  14. 13 0
      alien-service-entity/src/main/java/shop/alien/mapper/ActivityInviteConfigMapper.java
  15. 53 0
      alien-service-entity/src/main/java/shop/alien/mapper/ActivityInviteLogMapper.java
  16. 19 0
      alien-service-entity/src/main/java/shop/alien/mapper/LifeSysMenuMapper.java
  17. 13 0
      alien-service-entity/src/main/java/shop/alien/mapper/UserPointMapper.java
  18. 11 0
      alien-service-entity/src/main/java/shop/alien/mapper/delicious/DeliciousComboMapper.java
  19. 11 0
      alien-service-entity/src/main/java/shop/alien/mapper/delicious/DeliciousComboProdMapper.java
  20. 11 0
      alien-service-entity/src/main/java/shop/alien/mapper/delicious/DeliciousCommodityIdxMapper.java
  21. 11 0
      alien-service-entity/src/main/java/shop/alien/mapper/delicious/DeliciousProductMapper.java
  22. 27 0
      alien-service-entity/src/main/java/shop/alien/mapper/second/LifeUserLogMapper.java
  23. 49 0
      alien-service-entity/src/main/resources/mapper/LifeSysMenuMapper.xml
  24. 19 0
      alien-service-entity/src/main/resources/mapper/delicious/DeliciousComboMapper.xml
  25. 14 0
      alien-service-entity/src/main/resources/mapper/delicious/DeliciousComboProdMapper.xml
  26. 20 0
      alien-service-entity/src/main/resources/mapper/delicious/DeliciousCommodityIdxMapper.xml
  27. 19 0
      alien-service-entity/src/main/resources/mapper/delicious/DeliciousProductMapper.xml
  28. 11 0
      alien-service-gateway/README.md
  29. 253 5
      alien-service-gateway/pom.xml
  30. 31 0
      alien-service-gateway/src/main/java/shop/alien/gateway/AlienGatewayApplication.java
  31. 247 0
      alien-service-gateway/src/main/java/shop/alien/gateway/config/BaseRedisService.java
  32. 57 0
      alien-service-gateway/src/main/java/shop/alien/gateway/config/BeanConfig.java
  33. 26 0
      alien-service-gateway/src/main/java/shop/alien/gateway/config/CorsConfig.java
  34. 35 0
      alien-service-gateway/src/main/java/shop/alien/gateway/config/FeignHttpMessageConvertersConfig.java
  35. 236 0
      alien-service-gateway/src/main/java/shop/alien/gateway/config/JwtTokenFilter.java
  36. 120 0
      alien-service-gateway/src/main/java/shop/alien/gateway/config/RiskControlProperties.java
  37. 70 0
      alien-service-gateway/src/main/java/shop/alien/gateway/controller/CaptchaImageController.java
  38. 101 0
      alien-service-gateway/src/main/java/shop/alien/gateway/controller/LawyerUserLogInController.java
  39. 72 0
      alien-service-gateway/src/main/java/shop/alien/gateway/controller/LifeUserController.java
  40. 90 0
      alien-service-gateway/src/main/java/shop/alien/gateway/controller/StoreUserController.java
  41. 484 0
      alien-service-gateway/src/main/java/shop/alien/gateway/controller/SystemController.java
  42. 36 0
      alien-service-gateway/src/main/java/shop/alien/gateway/feign/SecondServiceFeign.java
  43. 22 0
      alien-service-gateway/src/main/java/shop/alien/gateway/feign/StoreServiceFeign.java
  44. 30 0
      alien-service-gateway/src/main/java/shop/alien/gateway/mapper/LawyerServiceAreaMapper.java
  45. 89 0
      alien-service-gateway/src/main/java/shop/alien/gateway/mapper/LawyerUserGatewayMapper.java
  46. 12 0
      alien-service-gateway/src/main/java/shop/alien/gateway/mapper/LifeSysGatewayMapper.java
  47. 13 0
      alien-service-gateway/src/main/java/shop/alien/gateway/mapper/LifeUserGatewayMapper.java
  48. 27 0
      alien-service-gateway/src/main/java/shop/alien/gateway/mapper/LifeUserLogGatewayMapper.java
  49. 15 0
      alien-service-gateway/src/main/java/shop/alien/gateway/mapper/StoreInfoGatewayMapper.java
  50. 17 0
      alien-service-gateway/src/main/java/shop/alien/gateway/mapper/StoreUserGatewayMapper.java
  51. 15 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/ActivityInviteConfigService.java
  52. 52 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/LifeUserLogTransactionService.java
  53. 240 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/LifeUserService.java
  54. 16 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/StoreInfoService.java
  55. 22 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/StoreUserService.java
  56. 79 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/SystemService.java
  57. 15 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/UserPointService.java
  58. 120 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/ActivityInviteConfigServiceImpl.java
  59. 150 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/LawyerUserLogInServiceImpl.java
  60. 22 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/StoreInfoServiceImpl.java
  61. 92 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/StoreUserServiceImpl.java
  62. 303 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/SystemServiceImpl.java
  63. 50 0
      alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/UserPointServiceImpl.java
  64. 253 0
      alien-service-gateway/src/main/java/shop/alien/gateway/util/Base64.java
  65. 56 0
      alien-service-gateway/src/main/java/shop/alien/gateway/util/KaptchaTextCreator.java
  66. 108 0
      alien-service-gateway/src/main/java/shop/alien/gateway/util/SmsUtil.java
  67. 20 0
      alien-service-gateway/src/main/resources/bootstrap-dev.yml
  68. 21 0
      alien-service-gateway/src/main/resources/bootstrap-localhost.yml
  69. 22 0
      alien-service-gateway/src/main/resources/bootstrap-prod.yml
  70. 22 0
      alien-service-gateway/src/main/resources/bootstrap-test.yml
  71. 22 0
      alien-service-gateway/src/main/resources/bootstrap-uat.yml
  72. 3 0
      alien-service-gateway/src/main/resources/bootstrap.yml
  73. 173 0
      alien-service-gateway/src/main/resources/logback-spring.xml
  74. 11 0
      alien-service-gateway/src/main/resources/mapper/StoreUserGatewayMapper.xml

+ 69 - 0
alien-service-entity/src/main/java/shop/alien/entity/delicious/DeliciousCombo.java

@@ -0,0 +1,69 @@
+package shop.alien.entity.delicious;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 美食套餐表
+ */
+@Data
+@JsonInclude
+@TableName("delicious_combo")
+public class DeliciousCombo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.INPUT)
+    @ApiModelProperty(value = "主键ID")
+    private Integer id;
+
+    @TableField("name")
+    @ApiModelProperty(value = "套餐名")
+    private String name;
+
+    @TableField("price")
+    @ApiModelProperty(value = "价格")
+    private BigDecimal price;
+
+    @TableField("status")
+    @ApiModelProperty(value = "状态 0禁用 1启用")
+    private Integer status;
+
+    @TableField("version")
+    @ApiModelProperty(value = "版本")
+    private String version;
+
+    @TableField("attributes")
+    @ApiModelProperty(value = "属性数据")
+    private String attributes;
+
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "创建时间")
+    private Date createdTime;
+
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "更新时间")
+    private Date updatedTime;
+
+    @TableField("created_user_id")
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createdUserId;
+
+    @TableField("updated_user_id")
+    @ApiModelProperty(value = "更新人ID")
+    private Integer updatedUserId;
+
+    @TableField("delete_flag")
+    @ApiModelProperty(value = "删除标记 0:未删除 1:已删除")
+    @TableLogic
+    private Integer deleteFlag;
+}
+

+ 47 - 0
alien-service-entity/src/main/java/shop/alien/entity/delicious/DeliciousComboProd.java

@@ -0,0 +1,47 @@
+package shop.alien.entity.delicious;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 美食套餐单品绑定表
+ */
+@Data
+@JsonInclude
+@TableName("delicious_combo_prod")
+public class DeliciousComboProd implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    @ApiModelProperty(value = "主键ID")
+    private Integer id;
+
+    @TableField("prod_id")
+    @ApiModelProperty(value = "商品ID")
+    private Integer prodId;
+
+    @TableField("combo_id")
+    @ApiModelProperty(value = "套餐ID")
+    private Integer comboId;
+
+    @TableField("prod_num")
+    @ApiModelProperty(value = "单品数量")
+    private Integer prodNum;
+
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "创建时间")
+    private Date createdTime;
+
+    @TableField("delete_flag")
+    @ApiModelProperty(value = "删除标记 0:未删除 1:已删除")
+    @TableLogic
+    private Integer deleteFlag;
+}
+

+ 72 - 0
alien-service-entity/src/main/java/shop/alien/entity/delicious/DeliciousCommodityIdx.java

@@ -0,0 +1,72 @@
+package shop.alien.entity.delicious;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 美食商品索引表
+ */
+@Data
+@JsonInclude
+@TableName("delicious_commodity_idx")
+public class DeliciousCommodityIdx implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.INPUT)
+    @ApiModelProperty(value = "主键ID")
+    private Integer id;
+
+    @TableField("category")
+    @ApiModelProperty(value = "小类")
+    private String category;
+
+    @TableField("commodity_id")
+    @ApiModelProperty(value = "商品ID")
+    private Integer commodityId;
+
+    @TableField("table_name")
+    @ApiModelProperty(value = "商品表表名")
+    private String tableName;
+
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "创建时间")
+    private Date createdTime;
+
+    @TableField("store_id")
+    @ApiModelProperty(value = "店铺ID")
+    private String storeId;
+
+    @TableField("status")
+    @ApiModelProperty(value = "状态")
+    private Integer status;
+
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "更新时间")
+    private Date updatedTime;
+
+    @TableField("commodity_type")
+    @ApiModelProperty(value = "商品类别")
+    private String commodityType;
+
+    @TableField("created_user_id")
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createdUserId;
+
+    @TableField("updated_user_id")
+    @ApiModelProperty(value = "更新人ID")
+    private Integer updatedUserId;
+
+    @TableField("delete_flag")
+    @ApiModelProperty(value = "删除标记 0:未删除 1:已删除")
+    @TableLogic
+    private Integer deleteFlag;
+}
+

+ 69 - 0
alien-service-entity/src/main/java/shop/alien/entity/delicious/DeliciousProduct.java

@@ -0,0 +1,69 @@
+package shop.alien.entity.delicious;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 美食单品表
+ */
+@Data
+@JsonInclude
+@TableName("delicious_product")
+public class DeliciousProduct implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.INPUT)
+    @ApiModelProperty(value = "主键ID")
+    private Integer id;
+
+    @TableField("name")
+    @ApiModelProperty(value = "商品名")
+    private String name;
+
+    @TableField("status")
+    @ApiModelProperty(value = "状态 0禁用 1启用")
+    private Integer status;
+
+    @TableField("version")
+    @ApiModelProperty(value = "版本")
+    private String version;
+
+    @TableField("attributes")
+    @ApiModelProperty(value = "属性数据")
+    private String attributes;
+
+    @TableField("price")
+    @ApiModelProperty(value = "价格")
+    private BigDecimal price;
+
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "创建时间")
+    private Date createdTime;
+
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "更新时间")
+    private Date updatedTime;
+
+    @TableField("created_user_id")
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createdUserId;
+
+    @TableField("updated_user_id")
+    @ApiModelProperty(value = "更新人ID")
+    private Integer updatedUserId;
+
+    @TableField("delete_flag")
+    @ApiModelProperty(value = "删除标记 0:未删除 1:已删除")
+    @TableLogic
+    private Integer deleteFlag;
+}
+

+ 81 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/ActivityInviteConfig.java

@@ -0,0 +1,81 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+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
+@TableName("activity_invite_config")
+@ApiModel(value = "ActivityInviteConfig", description = "邀请活动配置表")
+public class ActivityInviteConfig {
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "活动名称")
+    private String activityName;
+
+    @ApiModelProperty(value = "活动类型:1,邀请好友,2.其他")
+    private Integer activityType;
+
+    @ApiModelProperty(value = "活动规则")
+    private String activityRule;
+
+    @ApiModelProperty(value = "活动状态:0-未启用,1-启用,2-已结束")
+    private Integer status;
+
+    @ApiModelProperty(value = "邀请成功条件:1,注册即算,2.实名认证,3,首单支付并成功核销")
+    private Integer inviteCondition;
+
+    @ApiModelProperty(value = "邀请人数")
+    private Integer invitePersonNum;
+
+    @ApiModelProperty(value = "邀请奖励类型:1.优惠券,2.积分")
+    private Integer inviteRewardType;
+
+    @ApiModelProperty(value = "邀请奖励积分")
+    private Integer inviteRewardPoint;
+
+    @ApiModelProperty(value = "邀请奖励优惠券")
+    private Integer inviteRewardCoupon;
+
+    @ApiModelProperty(value = "被邀请奖励类型:1.优惠券,2.积分")
+    private Integer invitedRewardType;
+
+    @ApiModelProperty(value = "被邀请奖励积分")
+    private Integer invitedRewardPoint;
+
+    @ApiModelProperty(value = "被邀请奖励优惠券")
+    private Integer invitedRewardCoupon;
+
+
+    @ApiModelProperty(value = "每天最大邀请人数")
+    private Integer maxInviteNum;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+}

+ 75 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/ActivityInviteLog.java

@@ -0,0 +1,75 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+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
+@TableName("activity_invite_log")
+@ApiModel(value = "ActivityInviteLog", description = "邀请记录表")
+public class ActivityInviteLog {
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "活动ID")
+    private Integer activityId;
+
+    @ApiModelProperty(value = "邀请人用户ID")
+    private Integer inviteUserId;
+
+    @ApiModelProperty(value = "邀请时间")
+    private Date inviteTime;
+
+    @ApiModelProperty(value = "被邀请人用户ID")
+    private Integer invitedUserId;
+
+    @ApiModelProperty(value = "邀请奖励类型:1.优惠券,2.积分")
+    private Integer inviteRewardType;
+
+    @ApiModelProperty(value = "邀请奖励积分")
+    private Integer inviteRewardPoint;
+
+    @ApiModelProperty(value = "邀请奖励优惠券")
+    private Integer inviteRewardCoupon;
+
+    @ApiModelProperty(value = "被邀请奖励类型:1.优惠券,2.积分")
+    private Integer invitedRewardType;
+
+    @ApiModelProperty(value = "被邀请奖励积分")
+    private Integer invitedRewardPoint;
+
+    @ApiModelProperty(value = "被邀请奖励优惠券")
+    private Integer invitedRewardCoupon;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "发放状态, 0:未发放, 1:已发放")
+    @TableField("distribution_status")
+    private Integer distributionStatus;
+}

+ 78 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/LifeSys.java

@@ -0,0 +1,78 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 系统用户
+ */
+@Data
+@JsonInclude
+@TableName("life_sys")
+public class LifeSys {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "用户名")
+    private String userName;
+
+    @ApiModelProperty(value = "密码")
+    private String userPassword;
+
+    @ApiModelProperty(value = "角色id")
+    private String roleId;
+
+    @ApiModelProperty(value = "真实姓名")
+    @TableField("real_name")
+    private String realName;
+
+    @ApiModelProperty(value = "联系电话")
+    @TableField("phone")
+    private String phone;
+
+    @ApiModelProperty(value = "邮箱")
+    @TableField("email")
+    private String email;
+
+    @ApiModelProperty(value = "所属部门ID")
+    @TableField("department_id")
+    private String departmentId;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "启用1 禁用 0")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "备注")
+    @TableField("remark")
+    private String remark;
+}

+ 108 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/LifeSysMenu.java

@@ -0,0 +1,108 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+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;
+import java.util.List;
+
+/**
+ * 菜单权限表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("life_sys_menu")
+@ApiModel(value = "LifeSysMenu对象", description = "菜单权限表")
+public class LifeSysMenu {
+
+    @ApiModelProperty(value = "菜单ID")
+    @TableId(value = "menu_id", type = IdType.AUTO)
+    private Long menuId;
+
+    @ApiModelProperty(value = "菜单名称")
+    @TableField("menu_name")
+    private String menuName;
+
+    @ApiModelProperty(value = "父菜单ID(0表示根菜单)")
+    @TableField("parent_id")
+    private Long parentId;
+
+    @ApiModelProperty(value = "菜单类型(M目录 C菜单 F按钮)")
+    @TableField("menu_type")
+    private String menuType;
+
+    @ApiModelProperty(value = "显示顺序")
+    @TableField("menu_sort")
+    private Integer menuSort;
+
+    @ApiModelProperty(value = "路由地址")
+    @TableField("path")
+    private String path;
+
+    @ApiModelProperty(value = "组件路径")
+    @TableField("component")
+    private String component;
+
+    @ApiModelProperty(value = "权限标识(如:sys:user:list)")
+    @TableField("perms")
+    private String perms;
+
+    @ApiModelProperty(value = "菜单图标")
+    @TableField("icon")
+    private String icon;
+
+    @ApiModelProperty(value = "菜单状态(0正常 1停用)")
+    @TableField("status")
+    private String status;
+
+    @ApiModelProperty(value = "是否显示(0显示 1隐藏)")
+    @TableField("visible")
+    private String visible;
+
+    @ApiModelProperty(value = "是否为外链(0是 1否)")
+    @TableField("is_frame")
+    private String isFrame;
+
+    @ApiModelProperty(value = "是否缓存(0缓存 1不缓存)")
+    @TableField("is_cache")
+    private String isCache;
+
+    @ApiModelProperty(value = "删除标志(0存在 2删除)")
+    @TableField("del_flag")
+    @TableLogic(value = "0", delval = "2")
+    private String delFlag;
+
+    @ApiModelProperty(value = "创建者")
+    @TableField("create_by")
+    private String createBy;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新者")
+    @TableField("update_by")
+    private String updateBy;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "备注")
+    @TableField("remark")
+    private String remark;
+
+    @ApiModelProperty(value = "子菜单列表(用于树形结构)")
+    @TableField(exist = false)
+    private List<LifeSysMenu> children;
+}
+

+ 47 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/UserPoint.java

@@ -0,0 +1,47 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+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
+@TableName("user_point")
+@ApiModel(value = "UserPoint", description = "用户积分表")
+public class UserPoint {
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "用户ID")
+    private Integer userId;
+
+    @ApiModelProperty(value = "用户积分")
+    private Integer userPoint;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+}

+ 20 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/dto/SystemLoginDto.java

@@ -0,0 +1,20 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value="SystemLogin对象", description="web登录信息")
+public class SystemLoginDto {
+
+    @ApiModelProperty(value = "用户名")
+    public String username;
+    @ApiModelProperty(value = "密码")
+    public String password;
+
+}

+ 38 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/dto/SystemRegisterDto.java

@@ -0,0 +1,38 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * 系统用户注册DTO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value = "SystemRegisterDto对象", description = "系统用户注册信息")
+public class SystemRegisterDto {
+
+    @ApiModelProperty(value = "用户名", required = true)
+    private String userName;
+
+    @ApiModelProperty(value = "密码", required = true)
+    private String userPassword;
+
+    @ApiModelProperty(value = "角色id")
+    private String roleId;
+
+    @ApiModelProperty(value = "启用1 禁用 0")
+    private Integer status;
+
+    @ApiModelProperty(value = "备注")
+    private String remark;
+
+}
+
+

+ 47 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/vo/ActivityInviteLogVo.java

@@ -0,0 +1,47 @@
+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 lombok.EqualsAndHashCode;
+import shop.alien.entity.store.ActivityInviteLog;
+
+/**
+ * @author zhangchen
+ * @version 1.0
+ * @date 2025/9/8 10:00
+ */
+@EqualsAndHashCode(callSuper = false)
+@Data
+@JsonInclude
+@ApiModel(value = "ActivityInviteLogVo对象", description = "ActivityInviteLogVo")
+public class ActivityInviteLogVo extends ActivityInviteLog {
+
+    @ApiModelProperty(value = "被邀请人头像")
+    private String userImage;
+
+    @ApiModelProperty(value = "邀请人电话")
+    private String invitePhone;
+
+    @ApiModelProperty(value = "被邀请电话")
+    private String invitedPhone;
+
+    @ApiModelProperty(value = "被邀请人昵称")
+    private String invitedNickName;
+
+    @ApiModelProperty(value = "邀请人奖励信息")
+    private String inviteRewardInfo;
+
+    @ApiModelProperty(value = "被邀请人奖励信息")
+    private String invitedRewardInfo;
+
+    @ApiModelProperty(value = "邀请人真实姓名")
+    private String inviteRealName;
+
+    @ApiModelProperty(value = "被邀请人真实姓名")
+    private String invitedRealName;
+
+    @ApiModelProperty(value = "邀请人昵称")
+    private String inviteNickName;
+}

+ 32 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/vo/SystemLoginVo.java

@@ -0,0 +1,32 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import shop.alien.entity.store.LifeSysMenu;
+
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value="SystemLogin对象", description="web登录信息")
+public class SystemLoginVo {
+
+    @ApiModelProperty(value = "token")
+    public String token;
+
+    @ApiModelProperty(value = "结果")
+    public boolean result;
+
+    @ApiModelProperty(value = "提示信息")
+    public String message;
+
+    @ApiModelProperty(value = "角色id")
+    public String roleId;
+
+    @ApiModelProperty(value = "菜单列表")
+    public List<LifeSysMenu> menuList;
+}

+ 13 - 0
alien-service-entity/src/main/java/shop/alien/mapper/ActivityInviteConfigMapper.java

@@ -0,0 +1,13 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.ActivityInviteConfig;
+
+/**
+ * 邀请活动配置mapper
+ */
+@Mapper
+public interface ActivityInviteConfigMapper extends BaseMapper<ActivityInviteConfig> {
+
+}

+ 53 - 0
alien-service-entity/src/main/java/shop/alien/mapper/ActivityInviteLogMapper.java

@@ -0,0 +1,53 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import shop.alien.entity.store.ActivityInviteLog;
+import shop.alien.entity.store.vo.ActivityInviteLogVo;
+
+/**
+ * 邀请活动记录mapper
+ */
+@Mapper
+public interface ActivityInviteLogMapper extends BaseMapper<ActivityInviteLog> {
+
+    @Select("SELECT " +
+            "  ail.*," +
+            " lud.real_name as invitedRealName," +
+            " lud.user_name as invitedNickName," +
+            " lu.user_phone AS invitePhone," +
+            " lu.real_name as inviteRealName," +
+            " lu.user_name as inviteNickName," +
+            " lu1.user_phone AS invitedPhone," +
+            " lu1.user_image," +
+            " lu1.user_name AS invitedNickName," +
+            " CASE " +
+            " WHEN ail.invite_reward_type = 1 AND lc_invite.id IS NOT NULL THEN " +
+            " CONCAT('优惠券:', lc_invite.name, '(', IFNULL(lc_invite.offprice, lc_invite.price), '元)') " +
+            " WHEN ail.invite_reward_type = 2 AND ail.invite_reward_point IS NOT NULL THEN " +
+            " CONCAT('积分:', ail.invite_reward_point, '分') " +
+            " ELSE '' " +
+            " END AS inviteRewardInfo, " +
+            " CASE " +
+            " WHEN ail.invited_reward_type = 1 AND lc_invited.id IS NOT NULL THEN " +
+            " CONCAT('优惠券:', lc_invited.name, '(', IFNULL(lc_invited.offprice, lc_invited.price), '元)') " +
+            " WHEN ail.invited_reward_type = 2 AND ail.invited_reward_point IS NOT NULL THEN " +
+            " CONCAT('积分:', ail.invited_reward_point, '分') " +
+            " ELSE '' " +
+            " END AS invitedRewardInfo " +
+            " FROM activity_invite_log ail " +
+            " LEFT JOIN life_user lu ON lu.id = ail.invite_user_id " +
+            " AND lu.delete_flag = 0 " +
+            " LEFT JOIN life_user lud ON lud.id = ail.invited_user_id " +
+            " AND lud.delete_flag = 0 " +
+            " LEFT JOIN life_user lu1 ON lu1.id = ail.invited_user_id AND lu1.delete_flag = 0 " +
+            " LEFT JOIN life_coupon lc_invite ON lc_invite.id = ail.invite_reward_coupon AND lc_invite.delete_flag = 0 " +
+            " LEFT JOIN life_coupon lc_invited ON lc_invited.id = ail.invited_reward_coupon AND lc_invited.delete_flag = 0 " +
+            "${ew.customSqlSegment}")
+    IPage<ActivityInviteLogVo> getInviteActivityLogList(IPage<ActivityInviteLogVo> iPage, @Param(Constants.WRAPPER) QueryWrapper<ActivityInviteLogVo> wrapper);
+}

+ 19 - 0
alien-service-entity/src/main/java/shop/alien/mapper/LifeSysMenuMapper.java

@@ -0,0 +1,19 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.LifeSysMenu;
+
+import java.util.List;
+
+/**
+ * 菜单权限表 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface LifeSysMenuMapper extends BaseMapper<LifeSysMenu> {
+    List<LifeSysMenu> getMenuByUserId(Long userId);
+}
+

+ 13 - 0
alien-service-entity/src/main/java/shop/alien/mapper/UserPointMapper.java

@@ -0,0 +1,13 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.UserPoint;
+
+/**
+ * 用户积分mapper
+ */
+@Mapper
+public interface UserPointMapper extends BaseMapper<UserPoint> {
+
+}

+ 11 - 0
alien-service-entity/src/main/java/shop/alien/mapper/delicious/DeliciousComboMapper.java

@@ -0,0 +1,11 @@
+package shop.alien.mapper.delicious;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import shop.alien.entity.delicious.DeliciousCombo;
+
+/**
+ * 美食套餐表映射器
+ */
+public interface DeliciousComboMapper extends BaseMapper<DeliciousCombo> {
+}
+

+ 11 - 0
alien-service-entity/src/main/java/shop/alien/mapper/delicious/DeliciousComboProdMapper.java

@@ -0,0 +1,11 @@
+package shop.alien.mapper.delicious;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import shop.alien.entity.delicious.DeliciousComboProd;
+
+/**
+ * 美食套餐单品绑定表映射器
+ */
+public interface DeliciousComboProdMapper extends BaseMapper<DeliciousComboProd> {
+}
+

+ 11 - 0
alien-service-entity/src/main/java/shop/alien/mapper/delicious/DeliciousCommodityIdxMapper.java

@@ -0,0 +1,11 @@
+package shop.alien.mapper.delicious;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import shop.alien.entity.delicious.DeliciousCommodityIdx;
+
+/**
+ * 美食商品索引表映射器
+ */
+public interface DeliciousCommodityIdxMapper extends BaseMapper<DeliciousCommodityIdx> {
+}
+

+ 11 - 0
alien-service-entity/src/main/java/shop/alien/mapper/delicious/DeliciousProductMapper.java

@@ -0,0 +1,11 @@
+package shop.alien.mapper.delicious;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import shop.alien.entity.delicious.DeliciousProduct;
+
+/**
+ * 美食单品表映射器
+ */
+public interface DeliciousProductMapper extends BaseMapper<DeliciousProduct> {
+}
+

+ 27 - 0
alien-service-entity/src/main/java/shop/alien/mapper/second/LifeUserLogMapper.java

@@ -0,0 +1,27 @@
+package shop.alien.mapper.second;
+
+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.second.LifeUserLog;
+
+import java.util.List;
+
+@Mapper
+public interface LifeUserLogMapper extends BaseMapper<LifeUserLog> {
+
+    /**
+     * 查询时间段内同一个macip地址的登录记录
+     *
+     * @param starDate 开始日期
+     * @param endDate 结束日期
+     * @return 同一个macip地址的登录记录
+     */
+    @Select(" SELECT user_id, user_name, mac_ip FROM life_user_log " +
+            " WHERE created_time BETWEEN DATE_FORMAT(#{starDate}, '%Y-%m-%d %H:%i:%s') " +
+            " AND DATE_FORMAT(#{endDate}, '%Y-%m-%d %H:%i:%s') AND mac_ip = #{macIp} " +
+            " GROUP BY user_id, user_name, mac_ip ")
+    List<LifeUserLog> getLifeUserLogByDate(@Param("starDate") String starDate, @Param("endDate") String endDate, @Param("macIp") String macIp);
+
+}

+ 49 - 0
alien-service-entity/src/main/resources/mapper/LifeSysMenuMapper.xml

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.LifeSysMenuMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.LifeSysMenu">
+        <id column="menu_id" property="menuId" />
+        <result column="menu_name" property="menuName" />
+        <result column="parent_id" property="parentId" />
+        <result column="menu_type" property="menuType" />
+        <result column="menu_sort" property="menuSort" />
+        <result column="path" property="path" />
+        <result column="component" property="component" />
+        <result column="perms" property="perms" />
+        <result column="icon" property="icon" />
+        <result column="status" property="status" />
+        <result column="visible" property="visible" />
+        <result column="is_frame" property="isFrame" />
+        <result column="is_cache" property="isCache" />
+        <result column="del_flag" property="delFlag" />
+        <result column="create_by" property="createBy" />
+        <result column="created_time" property="createdTime" />
+        <result column="update_by" property="updateBy" />
+        <result column="updated_time" property="updatedTime" />
+        <result column="remark" property="remark" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        menu_id, menu_name, parent_id, menu_type, menu_sort, path, component, perms, icon,
+        status, visible, is_frame, is_cache, del_flag, create_by, created_time, update_by,
+        updated_time, remark
+    </sql>
+
+
+    <select id="getMenuByUserId" resultType="shop.alien.entity.store.LifeSysMenu">
+        select lsm.*
+        from life_sys_menu lsm
+                 left join life_sys_role_menu lsrm on lsrm.menu_id = lsm.menu_id
+        where lsm.del_flag = 0
+          and lsrm.role_id in (
+            select lsur.role_id
+            from life_sys_user_role lsur
+            where lsur.user_id = #{userId}
+        )
+    </select>
+
+</mapper>
+

+ 19 - 0
alien-service-entity/src/main/resources/mapper/delicious/DeliciousComboMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.delicious.DeliciousComboMapper">
+    <resultMap id="BaseResultMap" type="shop.alien.entity.delicious.DeliciousCombo">
+        <!--@mbggenerated-->
+        <id column="id" property="id" jdbcType="INTEGER"/>
+        <result column="name" property="name" jdbcType="VARCHAR"/>
+        <result column="price" property="price" jdbcType="DECIMAL"/>
+        <result column="status" property="status" jdbcType="INTEGER"/>
+        <result column="version" property="version" jdbcType="VARCHAR"/>
+        <result column="attributes" property="attributes" jdbcType="LONGVARCHAR"/>
+        <result column="created_time" property="createdTime" jdbcType="TIMESTAMP"/>
+        <result column="updated_time" property="updatedTime" jdbcType="TIMESTAMP"/>
+        <result column="created_user_id" property="createdUserId" jdbcType="INTEGER"/>
+        <result column="updated_user_id" property="updatedUserId" jdbcType="INTEGER"/>
+        <result column="delete_flag" property="deleteFlag" jdbcType="TINYINT"/>
+    </resultMap>
+</mapper>
+

+ 14 - 0
alien-service-entity/src/main/resources/mapper/delicious/DeliciousComboProdMapper.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.delicious.DeliciousComboProdMapper">
+    <resultMap id="BaseResultMap" type="shop.alien.entity.delicious.DeliciousComboProd">
+        <!--@mbggenerated-->
+        <id column="id" property="id" jdbcType="INTEGER"/>
+        <result column="prod_id" property="prodId" jdbcType="INTEGER"/>
+        <result column="combo_id" property="comboId" jdbcType="INTEGER"/>
+        <result column="prod_num" property="prodNum" jdbcType="INTEGER"/>
+        <result column="created_time" property="createdTime" jdbcType="TIMESTAMP"/>
+        <result column="delete_flag" property="deleteFlag" jdbcType="TINYINT"/>
+    </resultMap>
+</mapper>
+

+ 20 - 0
alien-service-entity/src/main/resources/mapper/delicious/DeliciousCommodityIdxMapper.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.delicious.DeliciousCommodityIdxMapper">
+    <resultMap id="BaseResultMap" type="shop.alien.entity.delicious.DeliciousCommodityIdx">
+        <!--@mbggenerated-->
+        <id column="id" property="id" jdbcType="INTEGER"/>
+        <result column="category" property="category" jdbcType="VARCHAR"/>
+        <result column="commodity_id" property="commodityId" jdbcType="INTEGER"/>
+        <result column="table_name" property="tableName" jdbcType="VARCHAR"/>
+        <result column="created_time" property="createdTime" jdbcType="TIMESTAMP"/>
+        <result column="store_id" property="storeId" jdbcType="VARCHAR"/>
+        <result column="status" property="status" jdbcType="INTEGER"/>
+        <result column="updated_time" property="updatedTime" jdbcType="TIMESTAMP"/>
+        <result column="commodity_type" property="commodityType" jdbcType="VARCHAR"/>
+        <result column="created_user_id" property="createdUserId" jdbcType="INTEGER"/>
+        <result column="updated_user_id" property="updatedUserId" jdbcType="INTEGER"/>
+        <result column="delete_flag" property="deleteFlag" jdbcType="TINYINT"/>
+    </resultMap>
+</mapper>
+

+ 19 - 0
alien-service-entity/src/main/resources/mapper/delicious/DeliciousProductMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.delicious.DeliciousProductMapper">
+    <resultMap id="BaseResultMap" type="shop.alien.entity.delicious.DeliciousProduct">
+        <!--@mbggenerated-->
+        <id column="id" property="id" jdbcType="INTEGER"/>
+        <result column="name" property="name" jdbcType="VARCHAR"/>
+        <result column="status" property="status" jdbcType="INTEGER"/>
+        <result column="version" property="version" jdbcType="VARCHAR"/>
+        <result column="attributes" property="attributes" jdbcType="LONGVARCHAR"/>
+        <result column="price" property="price" jdbcType="DECIMAL"/>
+        <result column="created_time" property="createdTime" jdbcType="TIMESTAMP"/>
+        <result column="updated_time" property="updatedTime" jdbcType="TIMESTAMP"/>
+        <result column="created_user_id" property="createdUserId" jdbcType="INTEGER"/>
+        <result column="updated_user_id" property="updatedUserId" jdbcType="INTEGER"/>
+        <result column="delete_flag" property="deleteFlag" jdbcType="TINYINT"/>
+    </resultMap>
+</mapper>
+

+ 11 - 0
alien-service-gateway/README.md

@@ -0,0 +1,11 @@
+# 网管
+| 描述  | 进度                 |
+| :------------- |:-------------------|
+| 名称     | xiaokuihua-gateway |
+| 端口     | 8000               |
+| 框架 | 100%               |
+| 业务代码迁移 | 0%                 | 
+| |                    | 
+| |                    | 
+| |                    | 
+| |                    |

+ 253 - 5
alien-service-gateway/pom.xml

@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>shop.alien</groupId>
@@ -10,11 +9,260 @@
     </parent>
 
     <artifactId>alien-service-gateway</artifactId>
+    <version>1.0.0</version>
+    <name>alien-service-gateway</name>
+    <description>alien-service-gateway</description>
 
     <properties>
-        <maven.compiler.source>8</maven.compiler.source>
-        <maven.compiler.target>8</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     </properties>
 
+    <dependencies>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <!--允许你使用流(Flux和Mono)来处理异步、非阻塞的数据-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-gateway</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+
+        <!--loadBalancer负载均衡-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-annotation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-extension</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>0.9.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+
+        <!-- 验证码 -->
+        <dependency>
+            <groupId>pro.fessional</groupId>
+            <artifactId>kaptcha</artifactId>
+        </dependency>
+
+
+        <!--Swagger Start-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>swagger-annotations</artifactId>
+                    <groupId>io.swagger</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>swagger-models</artifactId>
+                    <groupId>io.swagger</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>swagger-bootstrap-ui</artifactId>
+        </dependency>
+        <!--Swagger End-->
+        <!--token-->
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>3.4.0</version>
+        </dependency>
+
+        <!--redis连接池需要此依赖-->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-service-entity</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-service-util</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+        </dependency>
+
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>3.1.2</version> <!-- 使用最新版本 -->
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <phase>prepare-package</phase> <!-- 在package之前执行 -->
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/lib</outputDirectory> <!-- 指定输出目录 -->
+                            <includeScope>runtime</includeScope> <!-- 仅包含运行时依赖 -->
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>shop.alien.gateway.AlienGatewayApplication</mainClass>
+                    <layout>ZIP</layout>
+                    <includes>
+                        <include>
+                            <groupId>nothing</groupId>
+                            <artifactId>nothing</artifactId>
+                        </include>
+                    </includes>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>8</source>
+                    <target>8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <profiles>
+        <profile>
+            <id>test</id>
+            <properties>
+                <profiles.active>test</profiles.active>
+                <nacos.server-addr>192.168.2.252:8848</nacos.server-addr>
+                <nacos.namespace>0e1e2d77-56e8-422c-8317-6f71d7285e59</nacos.namespace>
+                <nacos.username>nacos</nacos.username>
+                <nacos.password>ngfriend198092</nacos.password>
+            </properties>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+    </profiles>
 </project>

+ 31 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/AlienGatewayApplication.java

@@ -0,0 +1,31 @@
+package shop.alien.gateway;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+
+/**
+ * 网关
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2025/1/21 16:58
+ */
+@ComponentScan({"shop.alien.gateway.*"})
+@MapperScan(basePackages = {"shop.alien.gateway.mapper", "shop.alien.mapper"})
+@EnableFeignClients(basePackages = "shop.alien.gateway.feign")
+@SpringBootApplication
+public class AlienGatewayApplication {
+
+    public static void main(String[] args) {
+        /*因为Spring Boot本身已经集成了日志框架Logback,而在Alibaba Nacos中又集成了一套Logback框架
+        而Alibaba Nacos中的Logback加载要优先于项目自身的Logback框架,在一个项目中其context_name只能定义一次,
+        所以,在项目启动时,Alibaba Nacos的Logback先加载完成后,再加载项目本身的Logback时就出现了冲突,报错误日志 “ Failed to rename context [logback] as [nacos] java.lang.IllegalStateException: Context has been already given a name ” ,但是不影响使用,
+        解决这个,只要禁用掉其中一个Logback就可以了,一般禁用外部框架夹带的Logback即可*/
+        System.setProperty("nacos.logging.default.config.enabled", "false");
+        SpringApplication.run(AlienGatewayApplication.class, args);
+    }
+
+}

+ 247 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/config/BaseRedisService.java

@@ -0,0 +1,247 @@
+package shop.alien.gateway.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.geo.*;
+import org.springframework.data.redis.connection.RedisGeoCommands;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2022/3/14 15:14
+ */
+@Component
+public class BaseRedisService {
+
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 添加List值, 向右
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setListRight(String key, String value) {
+        stringRedisTemplate.opsForList().rightPush(key, value);
+    }
+
+    /**
+     * 添加List, 所有
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setList(String key, List<String> value) {
+        stringRedisTemplate.opsForList().rightPushAll(key, value);
+    }
+
+    /**
+     * 从List左边取值并删除
+     *
+     * @param key 键
+     * @return 删除的值
+     */
+    public String getListLeft(String key) {
+        return stringRedisTemplate.opsForList().leftPop(key);
+    }
+
+    /**
+     * 获取List所有
+     *
+     * @param key 键
+     * @return List<String>
+     */
+    public List<String> getList(String key) {
+        return stringRedisTemplate.opsForList().range(key, 0, -1);
+    }
+
+
+    /**
+     * 添加Set
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setSetList(String key, String value) {
+        stringRedisTemplate.opsForSet().add(key, value);
+    }
+
+    /**
+     * 获取Set
+     *
+     * @param key 键
+     * @return Set<String>
+     */
+    public Set<String> getSetList(String key) {
+        return stringRedisTemplate.opsForSet().members(key);
+    }
+
+    /**
+     * 添加String值, 不设置过期时间
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setString(String key, String value) {
+        set(key, value, null);
+    }
+
+    /**
+     * 添加String值, 并设置过期时间
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     */
+    public void setString(String key, String value, Long timeOut) {
+        set(key, value, timeOut);
+    }
+
+    /**
+     * 设置超时时间
+     *
+     * @param key     键
+     * @param timeOut 时长(秒)
+     */
+    public void setTimeOut(String key, Long timeOut) {
+        stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 传入object对象
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     */
+    private void set(String key, Object value, Long timeOut) {
+        if (value != null) {
+            if (value instanceof String) {
+                String setValue = (String) value;
+                stringRedisTemplate.opsForValue().set(key, setValue);
+            }
+            //设置有效期
+            if (timeOut != null) {
+                stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
+            }
+        }
+    }
+
+    /**
+     * 使用key查找redis信息
+     *
+     * @param key 键
+     * @return
+     */
+    public String getString(String key) {
+        return stringRedisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 使用key删除redis信息
+     *
+     * @param key 键
+     */
+    public void delete(String key) {
+        stringRedisTemplate.delete(key);
+    }
+
+
+    /**
+     * 添加地理信息
+     *
+     * @param point
+     * @param content
+     * @param type
+     * @return
+     */
+    public Long inGeolocation(Point point, String content, String type) {
+        return stringRedisTemplate.opsForGeo().add(type, point, content);
+    }
+
+
+    /**
+     * 计算两个位置距离
+     *
+     * @param content
+     * @param contentII
+     * @param type
+     * @return
+     */
+    public Distance computeDistance(String content, String contentII, String type) {
+        return stringRedisTemplate.opsForGeo().distance(type, content, contentII, Metrics.KILOMETERS);
+
+    }
+
+    /**
+     * 获取成员经纬度
+     *
+     * @param contentArray
+     * @return
+     */
+    public List<Point> positions(String type, String... contentArray) {
+        return stringRedisTemplate.opsForGeo().position(type, contentArray);
+    }
+
+
+    /**
+     * 根据content查询附近商家
+     *
+     * @param content
+     * @param distance 搜索半径(单位:千米)
+     * @param count    获取几条
+     * @param type     name or 主键
+     * @return
+     */
+    public GeoResults<RedisGeoCommands.GeoLocation<String>> radius(String content, double distance, long count, String type) {
+
+        return stringRedisTemplate.opsForGeo()
+                .radius(type,
+                        content,
+                        new Distance(distance, Metrics.KILOMETERS),
+                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
+                                .includeDistance()   // 包含距离信息
+                                .includeCoordinates()// 包含坐标信息
+                                .sortAscending()     // 按距离升序
+                                .limit(count));   // 限制返回数量
+    }
+
+    /**
+     * 根据坐标查询附近商家
+     *
+     * @param point    经度 纬度
+     * @param distance 搜索半径(单位:千米)
+     * @param count    最大返回数量
+     * @return 附近商家地理信息结果集
+     */
+    public GeoResults<RedisGeoCommands.GeoLocation<String>> radius(Point point, double distance, long count, String type) {
+        return stringRedisTemplate.opsForGeo()
+                .radius(type,
+                        new Circle(point, new Distance(distance, Metrics.KILOMETERS)),
+                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
+                                .includeDistance()
+                                .includeCoordinates()
+                                .sortAscending()
+                                .limit(count));
+    }
+
+
+    /**
+     * 删除GEO类型中的成员
+     *
+     * @param type    GEO数据对应的键名(对应你代码中的type参数)
+     * @param members 要删除的成员名称
+     * @return 实际删除的成员数量
+     */
+    public Long removeGeoMember(String type, String... members) {
+        return stringRedisTemplate.opsForZSet().remove(type, (Object[]) members);
+    }
+
+
+}

+ 57 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/config/BeanConfig.java

@@ -0,0 +1,57 @@
+package shop.alien.gateway.config;
+
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Properties;
+
+import static com.google.code.kaptcha.Constants.*;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2025/10/31 14:46
+ */
+@Configuration
+public class BeanConfig {
+
+    @Bean(name = "captchaProducerMath")
+    public DefaultKaptcha getKaptchaBeanMath() {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 边框颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
+        // 验证码文本生成器 使用默认实现
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "shop.alien.gateway.util.KaptchaTextCreator");
+        // 验证码文本字符间距 默认为2
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 验证码噪点颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
+        // 干扰实现类
+        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+
+}

+ 26 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/config/CorsConfig.java

@@ -0,0 +1,26 @@
+package shop.alien.gateway.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.reactive.CorsWebFilter;
+import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
+
+@Slf4j
+@Configuration
+public class CorsConfig {
+
+    @Bean
+    public CorsWebFilter corsWebFilter() {
+        log.info("CORS限制打开");
+        CorsConfiguration config = new CorsConfiguration();
+        config.addAllowedOrigin("*");
+        config.addAllowedHeader("*");
+        config.addAllowedMethod("*");
+        config.setAllowCredentials(true);
+        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
+        configSource.registerCorsConfiguration("/**", config);
+        return new CorsWebFilter(configSource);
+    }
+}

+ 35 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/config/FeignHttpMessageConvertersConfig.java

@@ -0,0 +1,35 @@
+package shop.alien.gateway.config;
+
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Feign HttpMessageConverters 配置
+ * 在 WebFlux 环境中,Spring Boot 可能不会自动配置 HttpMessageConverters,
+ * 但 OpenFeign 仍然需要它来进行消息转换
+ *
+ * @author system
+ * @date 2025-01-XX
+ */
+@Configuration
+public class FeignHttpMessageConvertersConfig {
+
+    /**
+     * 手动创建 HttpMessageConverters bean,确保 OpenFeign 能够正常使用
+     * 这个 bean 会被 OpenFeign 的 SpringDecoder 使用
+     */
+    @Bean
+    public HttpMessageConverters httpMessageConverters() {
+        List<HttpMessageConverter<?>> converters = new ArrayList<>();
+        converters.add(new MappingJackson2HttpMessageConverter());
+        return new HttpMessageConverters(converters);
+    }
+}
+
+

+ 236 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/config/JwtTokenFilter.java

@@ -0,0 +1,236 @@
+package shop.alien.gateway.config;
+
+import com.alibaba.cloud.commons.lang.StringUtils;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.gateway.mapper.LawyerUserGatewayMapper;
+import shop.alien.gateway.mapper.LifeUserGatewayMapper;
+import shop.alien.gateway.mapper.StoreUserGatewayMapper;
+import shop.alien.util.common.JwtUtil;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static org.apache.commons.codec.language.bm.Languages.ANY;
+
+/**
+ * Jwt过滤器
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2025/1/21 16:58
+ */
+@Slf4j
+@Data
+@Component
+public class JwtTokenFilter implements GlobalFilter, Ordered {
+
+    @Value("${jwt.skip-auth-urls}")
+    private String[] skipAuthUrls;
+
+    @Autowired
+    private BaseRedisService baseRedisService;
+
+    @Autowired
+    private StoreUserGatewayMapper storeUserMapper;
+
+    @Autowired
+    private LifeUserGatewayMapper lifeUserMapper;
+
+    @Autowired
+    private LawyerUserGatewayMapper lawyerUserMapper;
+
+    /**
+     * 过滤器
+     *
+     * @param exchange 请求
+     * @param chain    网关
+     * @return Mono
+     */
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        String url = exchange.getRequest().getURI().getPath();
+        log.info("JwtTokenFilter.filter?Path={}", url);
+        if (Objects.equals(exchange.getRequest().getMethod(), HttpMethod.OPTIONS)) {
+            return allowChain(exchange, chain);
+        }
+        //跳过不需要验证的路径
+        if (null != skipAuthUrls && Arrays.asList(skipAuthUrls).contains(url)) {
+            return allowChain(exchange, chain);
+        }
+        if (null != skipAuthUrls && url.contains("/alienStore/socket/")) {
+            return chain.filter(exchange);
+        }
+        if (url.startsWith("/alienStore/webjars") || url.startsWith("/alienSecond/webjars")) {
+            return allowChain(exchange, chain);
+        }
+        //获取token
+        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
+        log.info("JwtTokenFilter.filter?Token={}", token);
+        JSONObject map = new JSONObject();
+        ServerHttpResponse resp = exchange.getResponse();
+        if (StringUtils.isBlank(token)) {
+            //没有token
+            log.error("JwtTokenFilter.filter ERROR 没有Token");
+            return authError(resp, "请登录");
+        } else {
+            //有token
+            try {
+                JwtUtil.checkToken(token);
+                Claims claims = JwtUtil.parseJWT(token);
+                JSONObject tokenData = JSONObject.parseObject(claims.get("sub").toString());
+                String deviceType = tokenData.getString("userType");
+                String phone = tokenData.getString("phone");
+                String redisKey;
+                //区分
+                if ("web".equals(deviceType)) {
+                    //管理端单设备登录
+                    //不限制
+                    return allowChain(exchange, chain);
+                } else {
+                    redisKey = deviceType + "_" + phone;
+                }
+                String redisVal = baseRedisService.getString(redisKey);
+                if (StringUtils.isEmpty(redisVal) || !token.equals(redisVal)) {
+                    if ("store".equals(deviceType) || "storePlatform".equals(deviceType)) {
+                        //判断程序是否为用户禁用
+                        StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getPhone, phone));
+                        if (storeUser.getStatus() == 1) {
+                            map.put("msg", "你的账号已被禁用");
+                            //别问, 问就是约定俗成
+                            map.put("code", 777);
+                        }
+                        if (!redisVal.equals(token)) {
+                            map.put("msg", "账号在别处登录");
+                            //别问, 问就是约定俗成
+                            map.put("code", 666);
+                        }
+                    } else if ("user".equals(deviceType)) {
+                        //判断程序是否为用户禁用
+                        LifeUser lifeUser = lifeUserMapper.selectOne(new LambdaQueryWrapper<LifeUser>().eq(LifeUser::getUserPhone, phone));
+                        //注销标记, 0:未注销, 1:已注销
+                        if (null != lifeUser.getLogoutFlag() && lifeUser.getLogoutFlag() == 1) {
+                            map.put("msg", "你的账号已注销");
+                            //别问, 问就是约定俗成
+                            map.put("code", 777);
+                        }
+                        if (!redisVal.equals(token)) {
+                            map.put("msg", "账号在别处登录");
+                            //别问, 问就是约定俗成
+                            map.put("code", 666);
+                        }
+                    }
+                    else if ("lawyer".equals(deviceType)) {
+                        //判断程序是否为用户禁用
+                        LawyerUser lawyerUser = lawyerUserMapper.selectOne(new LambdaQueryWrapper<LawyerUser>().eq(LawyerUser::getPhone, phone));
+                        //注销标记, 0:未注销, 1:已注销
+                        if (null != lawyerUser.getLogoutFlag() && lawyerUser.getLogoutFlag() == 1) {
+                            map.put("msg", "你的账号已注销");
+                            //别问, 问就是约定俗成
+                            map.put("code", 777);
+                        }
+                        if (!redisVal.equals(token)) {
+                            map.put("msg", "账号在别处登录");
+                            //别问, 问就是约定俗成
+                            map.put("code", 666);
+                        }
+                    }
+                    map.put("success", false);
+                    exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
+                    return exchange.getResponse()
+                            .writeWith(Mono.just(exchange.getResponse()
+                                    .bufferFactory()
+                                    .wrap(map.toJSONString().getBytes())));
+                }
+                return allowChain(exchange, chain);
+            } catch (ExpiredJwtException e) {
+                if (e.getMessage().contains("Allowed clock skew")) {
+                    log.error("JwtTokenFilter.filter ERROR 认证过期");
+                    return authError(resp, "认证过期");
+                } else {
+                    log.error("JwtTokenFilter.filter ERROR 认证失败");
+                    return authError(resp, "认证失败");
+                }
+            } catch (Exception e) {
+                log.error("JwtTokenFilter.filter ERROR 认证失败");
+                return authError(resp, "认证失败");
+            }
+        }
+    }
+
+    private Mono<Void> allowChain(ServerWebExchange exchange, GatewayFilterChain chain) {
+        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
+            exchange.getResponse().getHeaders().entrySet().stream()
+                    .filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1))
+                    .filter(kv -> (kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
+                            || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)
+                            || kv.getKey().equals(HttpHeaders.VARY)))
+                    .forEach(kv -> {
+                        // Vary只需要去重即可
+                        if (kv.getKey().equals(HttpHeaders.VARY))
+                            kv.setValue(kv.getValue().stream().distinct().collect(Collectors.toList()));
+                        else {
+                            List<String> value = new ArrayList<>();
+                            if (kv.getValue().contains(ANY)) {  //如果包含*,则取*
+                                value.add(ANY);
+                                kv.setValue(value);
+                            } else {
+                                value.add(kv.getValue().get(0)); // 否则默认取第一个
+                                kv.setValue(value);
+                            }
+                        }
+                    });
+        }));
+    }
+
+    /**
+     * 认证错误输出
+     *
+     * @param resp    响应对象
+     * @param message 错误信息
+     * @return Mono
+     */
+    private Mono<Void> authError(ServerHttpResponse resp, String message) {
+        resp.setStatusCode(HttpStatus.UNAUTHORIZED);
+        resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
+        JSONObject json = new JSONObject();
+        json.put("code", HttpStatus.UNAUTHORIZED.value());
+        json.put("msg", message);
+        json.put("data", "");
+        log.error("认证错误响应: {}", json.toJSONString());
+        DataBuffer buffer = resp.bufferFactory().wrap(json.toString().getBytes(StandardCharsets.UTF_8));
+        return resp.writeWith(Flux.just(buffer));
+    }
+
+    @Override
+    public int getOrder() {
+        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
+    }
+}

+ 120 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/config/RiskControlProperties.java

@@ -0,0 +1,120 @@
+package shop.alien.gateway.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@RefreshScope
+@ConfigurationProperties(prefix = "risk-control")
+public class RiskControlProperties {
+
+    /**
+     * 洗钱嫌疑规则配置
+     */
+    private MoneyLaundering moneyLaundering = new MoneyLaundering();
+
+    /**
+     * 账号异常规则配置
+     */
+    private AccountAbnormal accountAbnormal = new AccountAbnormal();
+
+    /**
+     * 交易欺诈规则配置
+     */
+    private TradeFraud tradeFraud = new TradeFraud();
+
+    /**
+     * 异常发布规则配置
+     */
+    private AbnormalPublish abnormalPublish = new AbnormalPublish();
+
+    @Data
+    public static class MoneyLaundering {
+        /**
+         * 每天交易次数阈值
+         */
+        private int dailyCount = 5;
+
+        /**
+         * 每笔交易金额阈值(元)
+         */
+        private double amountThreshold = 200.0;
+
+        /**
+         * 风险类型
+         */
+        private String riskType = "洗钱嫌疑";
+
+        /**
+         * 检测指标
+         */
+        private String detectIndicator = "高频高价交易";
+    }
+
+    @Data
+    public static class AccountAbnormal {
+        /**
+         * 24小时内同一设备/mac注册账号数量阈值
+         */
+        private int regCount24h = 3;
+
+        /**
+         * 风险类型
+         */
+        private String riskType = "账号异常";
+
+        /**
+         * 检测指标
+         */
+        private String detectIndicator = "同一设备24小时内注册超过3个账号";
+    }
+
+    @Data
+    public static class TradeFraud {
+        /**
+         * 24小时内发布成功记录次数阈值
+         */
+        private int publishCount24h = 3;
+
+        /**
+         * 时间窗口(小时)
+         */
+        private int timeWindowHours = 24;
+
+        /**
+         * 风险类型
+         */
+        private String riskType = "交易欺诈";
+
+        /**
+         * 检测指标
+         */
+        private String detectIndicator = "用户频繁修改商品";
+    }
+
+    @Data
+    public static class AbnormalPublish {
+        /**
+         * 24小时内发布同类商品数量阈值
+         */
+        private int sameCategoryCount24h = 10;
+        
+        /**
+         * 时间窗口(小时)
+         */
+        private int timeWindowHours = 24;
+
+        /**
+         * 风险类型
+         */
+        private String riskType = "异常发布";
+
+        /**
+         * 检测指标
+         */
+        private String detectIndicator = "短时间大量发布同类商品";
+    }
+}

+ 70 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/controller/CaptchaImageController.java

@@ -0,0 +1,70 @@
+package shop.alien.gateway.controller;
+
+import com.google.code.kaptcha.Producer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.FastByteArrayOutputStream;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+import shop.alien.entity.result.R;
+import shop.alien.gateway.config.BaseRedisService;
+import shop.alien.gateway.util.Base64;
+
+import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2025/10/31 14:12
+ */
+@RestController
+@RequestMapping("/captcha")
+public class CaptchaImageController {
+
+    @Resource(name = "captchaProducerMath")
+    private Producer captchaProducerMath;
+
+    @Autowired
+    private BaseRedisService redisCache;
+
+    /**
+     * 5分钟
+     */
+    private Long CAPTCHA_EXPIRATION = 5 * 60 * 1000L;
+
+    /**
+     * 生成验证码
+     */
+    @GetMapping("/captchaImage")
+    public Mono<R<Map<String, String>>> getCode() {
+        Map<String, String> resultMap = new HashMap<>();
+        // 保存验证码信息
+        String uuid = UUID.randomUUID().toString();
+        String verifyKey = "captcha_codes:" + uuid;
+        String capStr, code;
+        BufferedImage image;
+        String capText = captchaProducerMath.createText();
+        capStr = capText.substring(0, capText.lastIndexOf("@"));
+        code = capText.substring(capText.lastIndexOf("@") + 1);
+        image = captchaProducerMath.createImage(capStr);
+        redisCache.setString(verifyKey, code, CAPTCHA_EXPIRATION);
+        // 转换流信息写出
+        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
+        try {
+            ImageIO.write(image, "jpg", os);
+        } catch (IOException e) {
+            return Mono.just(R.fail("验证码生成失败"));
+        }
+        resultMap.put("uuid", uuid);
+        resultMap.put("img", "data:image/gif;base64," + Base64.encode(os.toByteArray()));
+        return Mono.just(R.data(resultMap));
+    }
+
+}

+ 101 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/controller/LawyerUserLogInController.java

@@ -0,0 +1,101 @@
+package shop.alien.gateway.controller;//package shop.alien.gateway.controller;
+//
+//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+//import io.swagger.annotations.Api;
+//import io.swagger.annotations.ApiOperation;
+//import io.swagger.annotations.ApiOperationSupport;
+//import io.swagger.annotations.ApiSort;
+//import lombok.RequiredArgsConstructor;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.web.bind.annotation.*;
+//import shop.alien.entity.result.R;
+//import shop.alien.entity.store.LawyerUser;
+//import shop.alien.entity.store.dto.LawyerUserDto;
+//import shop.alien.entity.store.vo.LawyerUserVo;
+//import shop.alien.gateway.mapper.LawyerUserMapper;
+//
+//import java.util.Objects;
+//import java.util.Optional;
+//
+///**
+// * 门店用户 前端控制器
+// *
+// * @author ssk
+// * @since 2024-12-11
+// */
+//@Slf4j
+//@Api(tags = {"律师用户"})
+//@ApiSort(8)
+//@CrossOrigin
+//@RestController
+//@RequestMapping("/lawyer/user")
+//@RequiredArgsConstructor
+//public class LawyerUserLogInController {
+//
+//    private final LawyerUserLogInService lawyerUserService;
+//    private final LawyerUserMapper lawyerUserMapper;
+//
+//    @ApiOperation("律师用户注册")
+//    @ApiOperationSupport(order = 1)
+//    @PostMapping("/register")
+//    public R<LawyerUserVo> register(@RequestBody LawyerUserDto lawyerUserDto) {
+//        log.info("LawyerUserLogInController.login?lawyerUserDto={}", lawyerUserDto);
+//        return lawyerUserService.logIn(lawyerUserDto);
+//    }
+//
+//    @ApiOperation("律师用户验证码校验")
+//    @ApiOperationSupport(order = 2)
+//    @PostMapping("/checkMsgCode")
+//    public R checkMsgCode(@RequestBody LawyerUserDto lawyerUserDto) {
+//        log.info("LawyerUserLogInController.checkMsgCode?lawyerUserDto={}", lawyerUserDto);
+//        return lawyerUserService.checkMsgCode(lawyerUserDto);
+//    }
+//
+//    @ApiOperation("律师用户登录")
+//    @ApiOperationSupport(order = 3)
+//    @GetMapping("/login")
+//    public R<LawyerUserVo> login(@RequestBody LawyerUserDto lawyerUserDto) {
+//        log.info("LawyerUserLogInController.login?lawyerUserDto={}", lawyerUserDto);
+//        LawyerUser lawyerUser = lawyerUserMapper.selectOne(new LambdaQueryWrapper<LawyerUser>()
+//                .eq(LawyerUser::getPhone, lawyerUserDto.getPhone()));
+//        if (null == lawyerUser) {
+//            return R.fail("当前账号不存在,请先去注册账号!");
+//        }
+//        if (lawyerUser.getStatus() == 0) {
+//            return R.fail("账号被禁用");
+//        }
+//        return Optional.ofNullable(lawyerUser).
+//                map(user -> lawyerUserDto.getIsPassword() ? checkPassword(user, lawyerUserDto.getPassword()) : lawyerUserService.createToKen(user)).
+//                orElseGet(() -> R.fail("手机号不存在"));
+//    }
+//
+//    @ApiOperation("律师用户修改密码")
+//    @ApiOperationSupport(order = 4)
+//    @PostMapping("/updatePassWord")
+//    public R updatePassWord(@RequestBody LawyerUserDto lawyerUserDto) {
+//        log.info("LawyerUserLogInController.updatePassWord?lawyerUserDto={}", lawyerUserDto);
+//        try {
+//            LawyerUser lawyerUser = lawyerUserMapper.selectOne(new LambdaQueryWrapper<LawyerUser>()
+//                    .eq(LawyerUser::getPhone, lawyerUserDto.getPhone()));
+//            if (null == lawyerUser) {
+//                return R.fail("当前账号不存在,请先去注册账号!");
+//            }
+//            if (lawyerUser.getStatus() == 0) {
+//                return R.fail("账号被禁用");
+//            }
+//            LawyerUser update = new LawyerUser();
+//            update.setPassword(lawyerUserDto.getPassword());
+//            update.setId(lawyerUser.getId());
+//            lawyerUserMapper.updateById(update);
+//            return R.success("修改成功");
+//        }catch (Exception e){
+//            return R.fail("修改密码失败");
+//        }
+//    }
+//
+//    private R<LawyerUserVo> checkPassword(LawyerUser lawyerUser, String password) {
+//        return Objects.equals(password, lawyerUser.getPassword())
+//                ? lawyerUserService.createToKen(lawyerUser)
+//                : R.fail("密码错误");
+//    }
+//}

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

@@ -0,0 +1,72 @@
+package shop.alien.gateway.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.LifeUserVo;
+import shop.alien.gateway.config.BaseRedisService;
+import shop.alien.gateway.service.ActivityInviteConfigService;
+import shop.alien.gateway.service.LifeUserService;
+
+/**
+ * 用户
+ */
+@Api(tags = {"一期-用户"})
+@Slf4j
+@CrossOrigin
+@RestController
+@RequestMapping("/user")
+@RequiredArgsConstructor
+public class LifeUserController {
+
+    private final LifeUserService lifeUserService;
+
+    private final BaseRedisService baseRedisService;
+
+    private final ActivityInviteConfigService activityInviteConfigService;
+
+    @ApiOperation("用户登录")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phoneNum", value = "手机号", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "code", value = "验证码", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "inviteCode", value = "邀请码", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "inviteCode", value = "邀请码", dataType = "String", paramType = "query", required = false)
+    })
+    @GetMapping("/userLogin")
+    public R<LifeUserVo> userLogin(@RequestParam("phoneNum") String phoneNum,
+                                   @RequestParam("code") String code,
+                                   @RequestParam(value = "macIp", required = false) String macIp,
+                                   @RequestParam(value = "inviteCode", required = false) String inviteCode) {
+        log.info("LifeUserController.userLogin?phoneNum={}&code={}", phoneNum, code);
+        LifeUserVo userVo = lifeUserService.userLogin(phoneNum, inviteCode, macIp);
+        if (null == userVo) {
+            return R.fail("登录失败");
+        }
+
+        if (1 == userVo.getIsBanned()) {
+            return R.fail("账号已被封禁, 无法登录");
+        }
+
+        // 2025-11-04 验证码-用户端登录
+        String cacheCode = baseRedisService.getString("verification_user_login_" + phoneNum);
+        if (null == cacheCode) {
+            return R.fail("当前验证码过期或未发送");
+        }
+        if (!cacheCode.trim().equals(code.trim())) {
+            return R.fail("验证码错误");
+        }
+
+        if(StringUtils.isNotBlank(inviteCode)){
+           String bindResult =  activityInviteConfigService.bindInviteCode(userVo.getId(), inviteCode);
+           if(StringUtils.isNotBlank(bindResult) && !bindResult.equals("绑定成功")){
+               return R.fail(bindResult);
+           }
+        }
+        return R.data(userVo);
+    }
+
+}

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

@@ -0,0 +1,90 @@
+package shop.alien.gateway.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.vo.StoreUserVo;
+import shop.alien.gateway.config.BaseRedisService;
+import shop.alien.gateway.mapper.StoreUserGatewayMapper;
+import shop.alien.gateway.service.StoreUserService;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * 门店用户 前端控制器
+ *
+ * @author ssk
+ * @since 2024-12-11
+ */
+@Slf4j
+@Api(tags = {"二期-门店用户"})
+@ApiSort(8)
+@CrossOrigin
+@RestController
+@RequestMapping("/store/user")
+@RequiredArgsConstructor
+public class StoreUserController {
+
+    private final StoreUserService storeUserService;
+
+    private final StoreUserGatewayMapper storeUserMapper;
+
+    private final BaseRedisService baseRedisService;
+
+    @ApiOperation("门店用户登录")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "password", value = "密码", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "isPassword", value = "是否密码登录", dataType = "Boolean", paramType = "query", required = true),
+            @ApiImplicitParam(name = "code", value = "验证码", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/login")
+    public R<StoreUserVo> login(
+            @RequestParam("phone") String phone,
+            @RequestParam("password") String password,
+            @RequestParam("isPassword") Boolean isPassword,
+            @RequestParam("code") String code) {
+        log.info("StoreUserController.login?phone={}&password={}&isPassword={}&code={}", phone, password, isPassword, code);
+        if (!isPassword) {
+            // 2025-11-04 验证码-商户端登录
+            String cacheCode = baseRedisService.getString("verification_store_login_" + phone);
+            if (null == cacheCode) {
+                return R.fail("验证码过期或未发送");
+            }
+            if (!cacheCode.trim().equals(code.trim())) {
+                return R.fail("验证码错误");
+            }
+        }
+        StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>()
+                .eq(StoreUser::getPhone, phone));
+        if (null == storeUser) {
+            return R.fail("当前账号不存在,请先去注册账号!");
+        }
+        if (storeUser.getStatus() == 1) {
+            return R.fail("账号被禁用");
+        }
+        return Optional.ofNullable(storeUser).
+                map(user -> isPassword ? checkPassword(user, password) : storeUserService.createToKen(user)).
+                orElseGet(() -> R.fail("手机号不存在"));
+    }
+
+    /**
+     * checkPwd
+     *
+     * @param user
+     * @param password
+     * @return
+     */
+    private R<StoreUserVo> checkPassword(StoreUser user, String password) {
+        return Objects.equals(password, user.getPassword())
+                ? storeUserService.createToKen(user)
+                : R.fail("密码错误");
+    }
+
+}

+ 484 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/controller/SystemController.java

@@ -0,0 +1,484 @@
+package shop.alien.gateway.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeSys;
+import shop.alien.entity.store.UserLoginInfo;
+import shop.alien.entity.store.dto.SystemLoginDto;
+import shop.alien.entity.store.dto.SystemRegisterDto;
+import shop.alien.entity.store.vo.SystemLoginVo;
+import shop.alien.gateway.service.SystemService;
+
+import java.util.List;
+
+/**
+ * 商家会员记录 前端控制器
+ *
+ * @author ssk
+ * @since 2025-02-20
+ */
+@Slf4j
+@Api(tags = {"数据中台基础服务接口类"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/sys")
+@RequiredArgsConstructor
+public class SystemController {
+
+    private final SystemService systemService;
+
+    @ApiOperation("web中台登录")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userName", value = "用户名", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "password", value = "密码", dataType = "String", paramType = "query"),
+    })
+    @PostMapping(value = "/login")
+    public R<SystemLoginVo> getStoreVipLogPage(@RequestBody SystemLoginDto systemLogin) {
+        return R.data(systemService.login(systemLogin.getUsername(), systemLogin.getPassword()));
+    }
+
+    /**
+     * web中台退出登录
+     * <p>
+     * 退出登录时会执行以下操作:
+     * 1. 参数校验:验证用户登录信息是否有效
+     * 2. 删除Redis中的token缓存
+     * 3. 记录退出登录日志
+     * </p>
+     *
+     * @param userLoginInfo 用户登录信息(从token中解析,包含userId、userName等信息)
+     * @return R<SystemLoginVo> 退出登录结果,包含退出状态信息
+     * @throws RuntimeException 当用户信息为空或退出登录失败时抛出异常
+     */
+    @ApiOperation(value = "web中台退出登录", notes = "用户退出登录,清除Redis中的token缓存")
+    @ApiOperationSupport(order = 2)
+    @PostMapping(value = "/logout")
+    public R<SystemLoginVo> logout(@RequestBody UserLoginInfo userLoginInfo) {
+        log.info("SystemController.logout?userId={}, userName={}", 
+                userLoginInfo != null ? userLoginInfo.getUserId() : null,
+                userLoginInfo != null ? userLoginInfo.getUserName() : null);
+        
+        try {
+            SystemLoginVo result = systemService.logout(userLoginInfo);
+            log.info("SystemController.logout成功?userId={}, userName={}", 
+                    userLoginInfo != null ? userLoginInfo.getUserId() : null,
+                    userLoginInfo != null ? userLoginInfo.getUserName() : null);
+            return R.data(result, "退出成功");
+        } catch (Exception e) {
+            log.error("SystemController.logout失败?userId={}, userName={}, error={}", 
+                    userLoginInfo != null ? userLoginInfo.getUserId() : null,
+                    userLoginInfo != null ? userLoginInfo.getUserName() : null,
+                    e.getMessage(), e);
+            return R.fail("退出登录失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("web中台分配律师账号")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userName", value = "用户名", dataType = "String", paramType = "body", required = true),
+            @ApiImplicitParam(name = "userPassword", value = "密码", dataType = "String", paramType = "body", required = true),
+            @ApiImplicitParam(name = "roleId", value = "角色id", dataType = "Integer", paramType = "body", required = true),
+            @ApiImplicitParam(name = "status", value = "启用 禁用", dataType = "Integer", paramType = "body", required = true),
+            @ApiImplicitParam(name = "remark", value = "备注", dataType = "String", paramType = "body", required = true),
+    })
+    @PostMapping(value = "/register")
+    public R<LifeSys> register(@RequestBody SystemRegisterDto registerDto) {
+        log.info("SystemController.register?userName={},roleId={},phone={}", registerDto.getUserName(), registerDto.getRoleId());
+        return systemService.register(registerDto.getUserName(), registerDto.getUserPassword(), registerDto.getRoleId(), registerDto.getStatus(), registerDto.getRemark());
+    }
+
+    @ApiOperation("web中台编辑账号")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userName", value = "用户名", dataType = "String", paramType = "body", required = true),
+            @ApiImplicitParam(name = "userPassword", value = "密码", dataType = "String", paramType = "body", required = true)
+    })
+    @PostMapping(value = "/updateAccounInfo")
+    public int updateAccounInfo(@RequestBody LifeSys lifeSys) {
+        log.info("SystemController.register?lifeSys={}", lifeSys);
+        return systemService.updateAccounInfo(lifeSys);
+    }
+
+    @ApiOperation("根据ID查询系统用户")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "用户ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping(value = "/getById")
+    public R<LifeSys> getById(@RequestParam("id") Integer id) {
+        log.info("SystemController.getById?id={}", id);
+        return systemService.getById(id);
+    }
+
+    @ApiOperation("查询系统用户列表")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userName", value = "用户名", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "status", value = "账号状态", dataType = "Integer", paramType = "query", required = false)
+    })
+    @GetMapping(value = "/list")
+    public R<List<LifeSys>> list(@RequestParam(value = "userName", required = false) String userName,
+                                  @RequestParam(value = "status", required = false) Integer status) {
+        log.info("SystemController.list?userName={},status={}", userName, status);
+        return systemService.list(userName, status);
+    }
+
+    @ApiOperation("删除系统用户")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "用户ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @DeleteMapping(value = "/delete")
+    public R<String> deleteById(@RequestParam("id") Integer id) {
+        log.info("SystemController.deleteById?id={}", id);
+        return systemService.deleteById(id);
+    }
+
+    @ApiOperation("中台路由配置")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type", value = "路由类型", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping(value = "/routingInfo")
+    public String routingInfo(@RequestParam(value = "type") Integer type) {
+        log.info("SystemController.list?type={}", type);
+        //type 是 1 为admin登录
+        if(type == 1){
+            String strInfo = "{\n" +
+                    "  \"code\": 200,\n" +
+                    "  \"data\": [\n" +
+                    "    {\n" +
+                    "      \"path\": \"/home/index\",\n" +
+                    "      \"name\": \"home\",\n" +
+                    "      \"component\": \"/home/index\",\n" +
+                    "      \"meta\": {\n" +
+                    "        \"icon\": \"List\",\n" +
+                    "        \"title\": \"首页\",\n" +
+                    "        \"isLink\": \"\",\n" +
+                    "        \"isHide\": false,\n" +
+                    "        \"isFull\": false,\n" +
+                    "        \"isAffix\": false,\n" +
+                    "        \"isKeepAlive\": false\n" +
+                    "      }\n" +
+                    "    },\n" +
+                    "    {\n" +
+                    "      \"path\": \"/lawyerManagement\",\n" +
+                    "      \"name\": \"lawyerManagement\",\n" +
+                    "      \"component\": \"/lawyerManagement/lawFirm/index\",\n" +
+                    "      \"meta\": {\n" +
+                    "        \"icon\": \"Opportunity\",\n" +
+                    "        \"title\": \"律师管理\",\n" +
+                    "        \"isLink\": \"\",\n" +
+                    "        \"isHide\": false,\n" +
+                    "        \"isFull\": false,\n" +
+                    "        \"isAffix\": false,\n" +
+                    "        \"isKeepAlive\": false\n" +
+                    "      },\n" +
+                    "      \"children\": [\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/lawFirm\",\n" +
+                    "          \"name\": \"lawFirm\",\n" +
+                    "          \"component\": \"/lawyerManagement/lawFirm/index\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"律所信息\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": false,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        },\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/lawyer\",\n" +
+                    "          \"name\": \"lawyer\",\n" +
+                    "          \"component\": \"/lawyerManagement/lawyer/index\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"律师信息\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": false,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        },\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/legalScene\",\n" +
+                    "          \"name\": \"legalScene\",\n" +
+                    "          \"component\": \"/lawyerManagement/legalScene/index\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"法律场景\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": true,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        },\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/professionalField\",\n" +
+                    "          \"name\": \"professionalField\",\n" +
+                    "          \"component\": \"/lawyerManagement/professionalField/index\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"专业领域\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": true,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        },\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/reconciliation\",\n" +
+                    "          \"name\": \"reconciliation\",\n" +
+                    "          \"component\": \"/lawyerManagement/reconciliation/index\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"对账总览\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": false,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        },\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/reconciliation/lawyerDetail\",\n" +
+                    "          \"name\": \"lawyerDetail\",\n" +
+                    "          \"component\": \"/lawyerManagement/reconciliation/lawyerDetail\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"对账总览\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": true,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        }\n" +
+                    "      ]\n" +
+                    "    },\n" +
+                    "    {\n" +
+                    "      \"path\": \"/userManagement\",\n" +
+                    "      \"name\": \"userManagement\",\n" +
+                    "      \"component\": \"/userManagement/index\",\n" +
+                    "      \"meta\": {\n" +
+                    "        \"icon\": \"UserFilled\",\n" +
+                    "        \"title\": \"用户管理\",\n" +
+                    "        \"isLink\": \"\",\n" +
+                    "        \"isHide\": true,\n" +
+                    "        \"isFull\": false,\n" +
+                    "        \"isAffix\": false,\n" +
+                    "        \"isKeepAlive\": false\n" +
+                    "      }\n" +
+                    "    },\n" +
+                    "    {\n" +
+                    "      \"path\": \"/refund\",\n" +
+                    "      \"name\": \"refund\",\n" +
+                    "      \"component\": \"/refund/index\",\n" +
+                    "      \"meta\": {\n" +
+                    "        \"icon\": \"UserFilled\",\n" +
+                    "        \"title\": \"举报审核\",\n" +
+                    "        \"isLink\": \"\",\n" +
+                    "        \"isHide\": true,\n" +
+                    "        \"isFull\": false,\n" +
+                    "        \"isAffix\": false,\n" +
+                    "        \"isKeepAlive\": false\n" +
+                    "      }\n" +
+                    "    },\n" +
+                    "    {\n" +
+                    "      \"path\": \"/appeal\",\n" +
+                    "      \"name\": \"appeal\",\n" +
+                    "      \"component\": \"/appeal/index\",\n" +
+                    "      \"meta\": {\n" +
+                    "        \"icon\": \"UserFilled\",\n" +
+                    "        \"title\": \"申诉审核\",\n" +
+                    "        \"isLink\": \"\",\n" +
+                    "        \"isHide\": true,\n" +
+                    "        \"isFull\": false,\n" +
+                    "        \"isAffix\": false,\n" +
+                    "        \"isKeepAlive\": false\n" +
+                    "      }\n" +
+                    "    }\n" +
+                    "  ],\n" +
+                    "  \"msg\": \"成功\"\n" +
+                    "}";
+            return strInfo;
+        }else{
+            String strInfo = "{\n" +
+                    "  \"code\": 200,\n" +
+                    "  \"data\": [\n" +
+                    "    {\n" +
+                    "      \"path\": \"/home/index\",\n" +
+                    "      \"name\": \"home\",\n" +
+                    "      \"component\": \"/home/index\",\n" +
+                    "      \"meta\": {\n" +
+                    "        \"icon\": \"List\",\n" +
+                    "        \"title\": \"首页\",\n" +
+                    "        \"isLink\": \"\",\n" +
+                    "        \"isHide\": false,\n" +
+                    "        \"isFull\": false,\n" +
+                    "        \"isAffix\": false,\n" +
+                    "        \"isKeepAlive\": false\n" +
+                    "      }\n" +
+                    "    },\n" +
+                    "    {\n" +
+                    "      \"path\": \"/lawyerManagement\",\n" +
+                    "      \"name\": \"lawyerManagement\",\n" +
+                    "      \"component\": \"/lawyerManagement/lawFirm/index\",\n" +
+                    "      \"meta\": {\n" +
+                    "        \"icon\": \"Opportunity\",\n" +
+                    "        \"title\": \"律师管理\",\n" +
+                    "        \"isLink\": \"\",\n" +
+                    "        \"isHide\": false,\n" +
+                    "        \"isFull\": false,\n" +
+                    "        \"isAffix\": false,\n" +
+                    "        \"isKeepAlive\": false\n" +
+                    "      },\n" +
+                    "      \"children\": [\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/lawFirm\",\n" +
+                    "          \"name\": \"lawFirm\",\n" +
+                    "          \"component\": \"/lawyerManagement/lawFirm/index\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"律所信息\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": false,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        },\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/lawyer\",\n" +
+                    "          \"name\": \"lawyer\",\n" +
+                    "          \"component\": \"/lawyerManagement/lawyer/index\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"律师信息\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": false,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        },\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/legalScene\",\n" +
+                    "          \"name\": \"legalScene\",\n" +
+                    "          \"component\": \"/lawyerManagement/legalScene/index\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"法律场景\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": true,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        },\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/professionalField\",\n" +
+                    "          \"name\": \"professionalField\",\n" +
+                    "          \"component\": \"/lawyerManagement/professionalField/index\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"专业领域\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": true,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        },\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/reconciliation\",\n" +
+                    "          \"name\": \"reconciliation\",\n" +
+                    "          \"component\": \"/lawyerManagement/reconciliation/index\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"对账总览\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": true,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        },\n" +
+                    "        {\n" +
+                    "          \"path\": \"/lawyerManagement/reconciliation/lawyerDetail\",\n" +
+                    "          \"name\": \"lawyerDetail\",\n" +
+                    "          \"component\": \"/lawyerManagement/reconciliation/lawyerDetail\",\n" +
+                    "          \"meta\": {\n" +
+                    "            \"icon\": \"Briefcase\",\n" +
+                    "            \"title\": \"对账总览\",\n" +
+                    "            \"isLink\": \"\",\n" +
+                    "            \"isHide\": false,\n" +
+                    "            \"isFull\": false,\n" +
+                    "            \"isAffix\": false,\n" +
+                    "            \"isKeepAlive\": false\n" +
+                    "          }\n" +
+                    "        }\n" +
+                    "      ]\n" +
+                    "    },\n" +
+                    "    {\n" +
+                    "      \"path\": \"/userManagement\",\n" +
+                    "      \"name\": \"userManagement\",\n" +
+                    "      \"component\": \"/userManagement/index\",\n" +
+                    "      \"meta\": {\n" +
+                    "        \"icon\": \"UserFilled\",\n" +
+                    "        \"title\": \"用户管理\",\n" +
+                    "        \"isLink\": \"\",\n" +
+                    "        \"isHide\": true,\n" +
+                    "        \"isFull\": false,\n" +
+                    "        \"isAffix\": false,\n" +
+                    "        \"isKeepAlive\": false\n" +
+                    "      }\n" +
+                    "    },\n" +
+                    "    {\n" +
+                    "      \"path\": \"/refund\",\n" +
+                    "      \"name\": \"refund\",\n" +
+                    "      \"component\": \"/refund/index\",\n" +
+                    "      \"meta\": {\n" +
+                    "        \"icon\": \"UserFilled\",\n" +
+                    "        \"title\": \"举报审核\",\n" +
+                    "        \"isLink\": \"\",\n" +
+                    "        \"isHide\": true,\n" +
+                    "        \"isFull\": false,\n" +
+                    "        \"isAffix\": false,\n" +
+                    "        \"isKeepAlive\": false\n" +
+                    "      }\n" +
+                    "    },\n" +
+                    "    {\n" +
+                    "      \"path\": \"/appeal\",\n" +
+                    "      \"name\": \"appeal\",\n" +
+                    "      \"component\": \"/appeal/index\",\n" +
+                    "      \"meta\": {\n" +
+                    "        \"icon\": \"UserFilled\",\n" +
+                    "        \"title\": \"申诉审核\",\n" +
+                    "        \"isLink\": \"\",\n" +
+                    "        \"isHide\": true,\n" +
+                    "        \"isFull\": false,\n" +
+                    "        \"isAffix\": false,\n" +
+                    "        \"isKeepAlive\": false\n" +
+                    "      }\n" +
+                    "    }\n" +
+                    "  ],\n" +
+                    "  \"msg\": \"成功\"\n" +
+                    "}";
+            return strInfo;
+        }
+
+    }
+
+}

+ 36 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/feign/SecondServiceFeign.java

@@ -0,0 +1,36 @@
+package shop.alien.gateway.feign;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@FeignClient(name = "alien-second", url = "${feign.alienSecond.url}")
+public interface SecondServiceFeign {
+    
+    /**
+     * 记录风控数据
+     *
+     * @param userId     用户ID
+     * @param ruleType   规则类型 1:洗钱嫌疑 2:账号异常 3:交易欺诈 4:异常发布
+     * @param ruleName   规则名称
+     * @param businessId 业务ID
+     * @param detailInfo 详细信息(JSON格式)
+     */
+    @PostMapping("/riskControl/record")
+    void recordRiskControlData(@RequestParam("userId") Integer userId,
+                               @RequestParam("ruleType") Integer ruleType,
+                               @RequestParam("ruleName") String ruleName,
+                               @RequestParam("businessId") String businessId,
+                               @RequestParam("detailInfo") String detailInfo);
+
+    /**
+     * 用户积分数据
+     *
+     * @param userId     用户ID
+     * @param initialPoints   用户积分
+     */
+    @PostMapping("/userPoints/create")
+    void createPointsRecord(@RequestParam("userId") Integer userId,
+                            @RequestParam("initialPoints") Integer initialPoints,
+                            @RequestParam("pointsType") Integer pointsType);
+}

+ 22 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/feign/StoreServiceFeign.java

@@ -0,0 +1,22 @@
+package shop.alien.gateway.feign;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeSysMenu;
+
+import java.util.List;
+
+@FeignClient(name = "alien-store", url = "${feign.alienStore.url}")
+public interface StoreServiceFeign {
+    /**
+     * 根据用户ID查询用户菜单树形结构
+     *
+     * @param userId 用户ID
+     * @param ifTree 是否返回树形结构(0否 1是)
+     * @return 用户菜单树形结构
+     */
+    @GetMapping(value = "/sys/menu/getMenuByUserId")
+    R<List<LifeSysMenu>> getMenuByUserId(@RequestParam("userId") Long userId, @RequestParam(name = "ifTree",required = false) Long ifTree);
+}

+ 30 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/mapper/LawyerServiceAreaMapper.java

@@ -0,0 +1,30 @@
+package shop.alien.gateway.mapper;//package shop.alien.gateway.mapper;
+//
+//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+//import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+//import com.baomidou.mybatisplus.core.toolkit.Constants;
+//import org.apache.ibatis.annotations.Mapper;
+//import org.apache.ibatis.annotations.Param;
+//import org.apache.ibatis.annotations.Select;
+//import shop.alien.entity.store.LawyerServiceArea;
+//
+//import java.util.List;
+//import java.util.Map;
+//
+///**
+// * <p>
+// * 律师服务领域关联 Mapper 接口
+// * </p>
+// *
+// * @author system
+// * @since 2025-01-XX
+// */
+//@Mapper
+//public interface LawyerServiceAreaMapper extends BaseMapper<LawyerServiceArea> {
+//
+//    @Select("select lsa.lawyer_user_id, llps.name from lawyer_service_area lsa \n" +
+//            "left join lawyer_legal_problem_scenario llps on llps.id = lsa.problem_scenario_id \n" +
+//            " ${ew.customSqlSegment}")
+//    List<Map<String, Object>> getLawyerLegalProblemScenarioList(@Param(Constants.WRAPPER) QueryWrapper<LawyerServiceArea> queryWrapper);
+//}
+//

+ 89 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/mapper/LawyerUserGatewayMapper.java

@@ -0,0 +1,89 @@
+package shop.alien.gateway.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Update;
+import shop.alien.entity.store.LawyerUser;
+
+/**
+ * <p>
+ * 律师用户 Mapper 接口
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface LawyerUserGatewayMapper extends BaseMapper<LawyerUser> {
+
+
+@Update("<script>" +
+        "UPDATE lawyer_user " +
+        "<set>" +
+        "<if test='firmId != null'>firm_id = #{firmId},</if>" +
+        "<if test='phone != null'>phone = #{phone},</if>" +
+        "<if test='name != null'>name = #{name},</if>" +
+        "<if test='idCard != null'>id_card = #{idCard},</if>" +
+        "<if test='password != null'>password = #{password},</if>" +
+        "<if test='payPassword != null'>pay_password = #{payPassword},</if>" +
+        "<if test='money != null'>money = #{money},</if>" +
+        "<if test='status != null'>status = #{status},</if>" +
+        "<if test='passType != null'>pass_type = #{passType},</if>" +
+        "<if test='deleteFlag != null'>delete_flag = #{deleteFlag},</if>" +
+        "<if test='createdTime != null'>created_time = #{createdTime},</if>" +
+        "<if test='createdUserId != null'>created_user_id = #{createdUserId},</if>" +
+        "<if test='updatedTime != null'>updated_time = #{updatedTime},</if>" +
+        "<if test='updatedUserId != null'>updated_user_id = #{updatedUserId},</if>" +
+        "<if test='logoutFlag != null'>logout_flag = #{logoutFlag},</if>" +
+        "<if test='logoutReason != null'>logout_reason = #{logoutReason},</if>" +
+        "<if test='logoutTime != null'>logout_time = #{logoutTime},</if>" +
+        "<if test='logoutCode != null'>logout_code = #{logoutCode},</if>" +
+        "<if test='nickName != null'>nick_name = #{nickName},</if>" +
+        "<if test='accountBlurb != null'>account_blurb = #{accountBlurb},</if>" +
+        "<if test='headImg != null'>head_img = #{headImg},</if>" +
+        "<if test='alipayAccount != null'>alipay_account = #{alipayAccount},</if>" +
+        "<if test='lawyerCertificateNo != null'>lawyer_certificate_no = #{lawyerCertificateNo},</if>" +
+        "<if test='lawFirm != null'>law_firm = #{lawFirm},</if>" +
+        "<if test='practiceYears != null'>practice_years = #{practiceYears},</if>" +
+        "<if test='practiceStartDate != null'>practice_start_date = #{practiceStartDate},</if>" +
+        "<if test='specialtyFields != null'>specialty_fields = #{specialtyFields},</if>" +
+        "<if test='certificationStatus != null'>certification_status = #{certificationStatus},</if>" +
+        "<if test='certificationFailReason != null'>certification_fail_reason = #{certificationFailReason},</if>" +
+        "<if test='certificationTime != null'>certification_time = #{certificationTime},</if>" +
+        "<if test='certificationReviewerId != null'>certification_reviewer_id = #{certificationReviewerId},</if>" +
+        "<if test='certificateImage != null'>certificate_image = #{certificateImage},</if>" +
+        "<if test='idCardFrontImage != null'>id_card_front_image = #{idCardFrontImage},</if>" +
+        "<if test='idCardBackImage != null'>id_card_back_image = #{idCardBackImage},</if>" +
+        "<if test='serviceScore != null'>service_score = #{serviceScore},</if>" +
+        "<if test='serviceCount != null'>service_count = #{serviceCount},</if>" +
+        "<if test='goodReviewCount != null'>good_review_count = #{goodReviewCount},</if>" +
+        "<if test='mediumReviewCount != null'>medium_review_count = #{mediumReviewCount},</if>" +
+        "<if test='badReviewCount != null'>bad_review_count = #{badReviewCount},</if>" +
+        "<if test='consultationFee != null'>consultation_fee = #{consultationFee},</if>" +
+        "<if test='agencyFee != null'>agency_fee = #{agencyFee},</if>" +
+        "<if test='province != null'>province = #{province},</if>" +
+        "<if test='city != null'>city = #{city},</if>" +
+        "<if test='district != null'>district = #{district},</if>" +
+        "<if test='address != null'>address = #{address},</if>" +
+        "<if test='email != null'>email = #{email},</if>" +
+        "<if test='gender != null'>gender = #{gender},</if>" +
+        "<if test='birthday != null'>birthday = #{birthday},</if>" +
+        "<if test='personalIntroduction != null'>personal_introduction = #{personalIntroduction},</if>" +
+        "<if test='educationBackground != null'>education_background = #{educationBackground},</if>" +
+        "<if test='workExperience != null'>work_experience = #{workExperience},</if>" +
+        "<if test='expertiseCases != null'>expertise_cases = #{expertiseCases},</if>" +
+        "<if test='isOnline != null'>is_online = #{isOnline},</if>" +
+        "<if test='lastOnlineTime != null'>last_online_time = #{lastOnlineTime},</if>" +
+        "<if test='isRecommended != null'>is_recommended = #{isRecommended},</if>" +
+        "<if test='recommendSort != null'>recommend_sort = #{recommendSort},</if>" +
+        "<if test='orderReceivingStatus != null'>order_receiving_status = #{orderReceivingStatus},</if>" +
+        "<if test='lawyerExpertiseAreaId != null'>lawyer_expertise_area_id = #{lawyerExpertiseAreaId},</if>" +
+        "<if test='commissionRate != null'>commission_rate = #{commissionRate},</if>" +
+        "<if test='paymentNum != null'>payment_num = #{paymentNum},</if>" +
+        "</set>" +
+        "WHERE id = #{id}" +
+        "</script>")
+Integer updateLawyerUser(LawyerUser user);
+
+}
+

+ 12 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/mapper/LifeSysGatewayMapper.java

@@ -0,0 +1,12 @@
+package shop.alien.gateway.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.LifeSys;
+
+/**
+ * 系统用户
+ */
+@Mapper
+public interface LifeSysGatewayMapper extends BaseMapper<LifeSys> {
+}

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

@@ -0,0 +1,13 @@
+package shop.alien.gateway.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.LifeUser;
+
+/**
+ * 用户
+ */
+@Mapper
+public interface LifeUserGatewayMapper extends BaseMapper<LifeUser> {
+
+}

+ 27 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/mapper/LifeUserLogGatewayMapper.java

@@ -0,0 +1,27 @@
+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.second.LifeUserLog;
+
+import java.util.List;
+
+@Mapper
+public interface LifeUserLogGatewayMapper extends BaseMapper<LifeUserLog> {
+
+    /**
+     * 查询时间段内同一个macip地址的登录记录
+     *
+     * @param starDate 开始日期
+     * @param endDate 结束日期
+     * @return 同一个macip地址的登录记录
+     */
+    @Select("SELECT user_id, user_name, mac_ip FROM life_user_log " +
+            " WHERE created_time BETWEEN DATE_FORMAT(#{starDate}, '%Y-%m-%d %H:%i:%s') " +
+            " AND DATE_FORMAT(#{endDate}, '%Y-%m-%d %H:%i:%s') AND mac_ip = #{macIp} " +
+            " GROUP BY user_id, user_name, mac_ip ")
+    List<LifeUserLog> getLifeUserLogByDate(@Param("starDate") String starDate, @Param("endDate") String endDate, @Param("macIp") String macIp);
+
+}

+ 15 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/mapper/StoreInfoGatewayMapper.java

@@ -0,0 +1,15 @@
+package shop.alien.gateway.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreInfo;
+
+/**
+ * 门店信息 Mapper 接口
+ *
+ * @author ssk
+ * @since 2024-12-05
+ */
+@Mapper
+public interface StoreInfoGatewayMapper extends BaseMapper<StoreInfo> {
+}

+ 17 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/mapper/StoreUserGatewayMapper.java

@@ -0,0 +1,17 @@
+package shop.alien.gateway.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreUser;
+
+/**
+ * <p>
+ * 门店用户 Mapper 接口
+ * </p>
+ *
+ * @author ssk
+ * @since 2024-12-11
+ */
+@Mapper
+public interface StoreUserGatewayMapper extends BaseMapper<StoreUser> {
+}

+ 15 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/service/ActivityInviteConfigService.java

@@ -0,0 +1,15 @@
+package shop.alien.gateway.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.ActivityInviteConfig;
+
+/**
+ * @author zhangchen
+ * @version 1.0
+ * @date 2025/09/05 10:00
+ */
+public interface ActivityInviteConfigService extends IService<ActivityInviteConfig> {
+
+    String bindInviteCode(Integer invitedUserId, String inviteCode);
+
+}

+ 52 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/service/LifeUserLogTransactionService.java

@@ -0,0 +1,52 @@
+package shop.alien.gateway.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.second.LifeUserLog;
+import shop.alien.gateway.mapper.LifeUserLogGatewayMapper;
+
+import java.util.List;
+
+/**
+ * 用户登录日志事务处理服务
+ * 独立的事务服务类,用于处理需要新事务的数据库操作
+ * 解决同类方法调用时 Spring AOP 代理不生效的问题
+ *
+ * @author alien-cloud
+ * @since 2024-01-01
+ */
+@Service
+@RequiredArgsConstructor
+public class LifeUserLogTransactionService {
+
+    private final LifeUserLogGatewayMapper lifeUserLogMapper;
+
+    /**
+     * 在新事务中插入用户登录日志
+     * 使用 REQUIRES_NEW 确保插入操作立即提交,便于后续查询
+     *
+     * @param lifeUserLog 用户登录日志对象
+     * @return int 插入成功的记录数
+     */
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    public int insertLifeUserLog(LifeUserLog lifeUserLog) {
+        return lifeUserLogMapper.insert(lifeUserLog);
+    }
+
+    /**
+     * 在新事务中查询指定时间段内的用户登录日志
+     * 使用 REQUIRES_NEW 确保能读取到已提交的最新数据
+     *
+     * @param startDate 开始时间
+     * @param endDate 结束时间
+     * @param macIp MAC地址/IP
+     * @return List<LifeUserLog> 用户登录日志列表
+     */
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    public List<LifeUserLog> getLifeUserLogByDate(String startDate, String endDate, String macIp) {
+        return lifeUserLogMapper.getLifeUserLogByDate(startDate, endDate, macIp);
+    }
+}
+

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

@@ -0,0 +1,240 @@
+package shop.alien.gateway.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.second.LifeUserLog;
+import shop.alien.entity.second.SecondRiskControlRecord;
+import shop.alien.entity.second.SecondUserCredit;
+import shop.alien.entity.second.SecondUserCreditRecord;
+import shop.alien.entity.second.enums.SecondUserCreditScoreEnum;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.vo.LifeUserVo;
+import shop.alien.gateway.config.BaseRedisService;
+import shop.alien.gateway.config.RiskControlProperties;
+import shop.alien.gateway.feign.SecondServiceFeign;
+import shop.alien.gateway.mapper.LifeUserGatewayMapper;
+import shop.alien.gateway.mapper.LifeUserLogGatewayMapper;
+import shop.alien.mapper.second.SecondRiskControlRecordMapper;
+import shop.alien.mapper.second.SecondUserCreditMapper;
+import shop.alien.mapper.second.SecondUserCreditRecordMapper;
+import shop.alien.util.common.JwtUtil;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 用户
+ */
+@Service
+@RequiredArgsConstructor
+public class LifeUserService extends ServiceImpl<LifeUserGatewayMapper, LifeUser> {
+
+    private final LifeUserGatewayMapper lifeUserMapper;
+
+    private final LifeUserLogGatewayMapper lifeUserLogMapper;
+
+    private final BaseRedisService baseRedisService;
+
+    private final SecondServiceFeign alienSecondFeign;
+
+    private final ActivityInviteConfigService activityInviteConfigService;
+
+    private final SecondRiskControlRecordMapper secondRiskControlRecordMapper;
+
+    private final LifeUserLogTransactionService lifeUserLogTransactionService;
+
+    @Autowired
+    private final SecondUserCreditMapper secondUserCreditMapper;
+
+    @Autowired
+    private final SecondUserCreditRecordMapper secondUserCreditRecordMapper;
+
+    @Autowired
+    private RiskControlProperties riskControlProperties;
+
+    @Value("${jwt.expiration-time}")
+    private String effectiveTime;
+
+    public LifeUserVo userLogin(String phoneNum, String inviteCode, String macIp) {
+        LifeUser user = getUserByPhone(phoneNum);
+        if (user == null) {
+            LifeUser lifeUser = new LifeUser();
+            lifeUser.setUserPhone(phoneNum);
+            lifeUser.setUserName(phoneNum);
+            lifeUser.setRealName(phoneNum);
+            lifeUser.setCreatedTime(new Date());
+            int ret = lifeUserMapper.insert(lifeUser);
+            if (ret == 1) {
+                LifeUser user2 = getUserByPhone(phoneNum);
+                LifeUserVo userVo = new LifeUserVo();
+                BeanUtils.copyProperties(user2, userVo);
+                Map<String, String> tokenMap = new HashMap<>();
+                tokenMap.put("phone", phoneNum);
+                tokenMap.put("userName", lifeUser.getUserName());
+                tokenMap.put("userId", lifeUser.getId().toString());
+                tokenMap.put("userType", "user");
+//                userVo.setToken(JWTUtils1.createToken(tokenMap));
+                String token = getToken(phoneNum, userVo.getUserName(), tokenMap);
+                userVo.setToken(token);
+                baseRedisService.setString("user_" + phoneNum, token);
+                // 二手平台登录log,同一个macip登录多账号记录
+                addLifeUserLogInfo(user2, macIp);
+
+                return userVo;
+            } else {
+                return null;
+            }
+        } else {
+            LifeUserVo userVo = new LifeUserVo();
+            BeanUtils.copyProperties(user, userVo);
+            Map<String, String> tokenMap = new HashMap<>();
+            tokenMap.put("phone", phoneNum);
+            tokenMap.put("userName", user.getUserName());
+            tokenMap.put("userId", user.getId().toString());
+            tokenMap.put("userType", "user");
+            String token = getToken(phoneNum, user.getUserName(), tokenMap);
+            userVo.setToken(token);
+            baseRedisService.setString("user_" + phoneNum, token);
+            // 二手平台登录log,同一个macip登录多账号记录
+            addLifeUserLogInfo(user, macIp);
+
+            return userVo;
+        }
+    }
+
+    private String getToken(String phoneNum, String userVo, Map<String, String> tokenMap) {
+        int effectiveTimeInt = Integer.parseInt(effectiveTime.substring(0, effectiveTime.length() - 1));
+        String effectiveTimeUnit = effectiveTime.substring(effectiveTime.length() - 1);
+        long effectiveTimeIntLong = 0L;
+        switch (effectiveTimeUnit) {
+            case "s": {
+                effectiveTimeIntLong = effectiveTimeInt * 1000L;
+                break;
+            }
+            case "m": {
+                effectiveTimeIntLong = effectiveTimeInt * 60L * 1000L;
+                break;
+            }
+            case "h": {
+                effectiveTimeIntLong = effectiveTimeInt * 60L * 60L * 1000L;
+                break;
+            }
+            case "d": {
+                effectiveTimeIntLong = effectiveTimeInt * 24L * 60L * 60L * 1000L;
+                break;
+            }
+        }
+        String token = JwtUtil.createJWT("user_" + phoneNum, userVo, JSONObject.toJSONString(tokenMap), effectiveTimeIntLong);
+        return token;
+    }
+
+    public LifeUser getUserByPhone(String phoneNum) {
+        LambdaQueryWrapper<LifeUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(LifeUser::getUserPhone, phoneNum);
+        return this.getOne(lambdaQueryWrapper);
+    }
+
+
+    /**
+     * 用户登录log存放(加入mac地址)
+     * 记录用户登录信息并进行风控检查
+     */
+    @Transactional
+    public void addLifeUserLogInfo(LifeUser user, String macIp) {
+        try {
+            LifeUserLog lifeUserLog = new LifeUserLog();
+            lifeUserLog.setUserId(user.getId());
+            lifeUserLog.setUserName(user.getUserName());
+            lifeUserLog.setMacIp(macIp);
+            lifeUserLog.setCreatedTime(new Date());
+            // 通过独立服务类调用,确保事务代理生效,插入操作立即提交
+            int count = lifeUserLogTransactionService.insertLifeUserLog(lifeUserLog);
+            if (count > 0) {
+                String startDate = LocalDateTime.now().minusHours(24L).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+                String endDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+                // 通过独立服务类查询,确保能看到刚才插入并提交的数据
+                List<LifeUserLog> lsit = lifeUserLogTransactionService.getLifeUserLogByDate(startDate, endDate, macIp);
+
+                if (lsit.size() > riskControlProperties.getAccountAbnormal().getRegCount24h() && !isViolation(startDate, endDate, macIp, user.getId())) {
+
+                    for (LifeUserLog row: lsit) {
+                        List<SecondUserCreditRecord> num = secondUserCreditRecordMapper.selectList(new LambdaQueryWrapper<SecondUserCreditRecord>()
+                                .eq(SecondUserCreditRecord::getUserId, row.getUserId())
+                                .eq(SecondUserCreditRecord::getPointsType, SecondUserCreditScoreEnum.ABNORMAL_ACCOUNTV.getCode())
+                                .ge(SecondUserCreditRecord::getCreatedTime, startDate)
+                                .le(SecondUserCreditRecord::getCreatedTime, endDate));
+
+                        if (num.size() <= 0) {
+                            LambdaQueryWrapper<SecondUserCredit> queryWrapper = new LambdaQueryWrapper<>();
+                            queryWrapper.eq(SecondUserCredit::getUserId, row.getUserId())
+                                    .orderByDesc(SecondUserCredit::getCreatedTime).last("LIMIT 1");
+                            SecondUserCredit secondUserCredit = secondUserCreditMapper.selectOne(queryWrapper);
+                            int score = secondUserCredit.getUserPoints() + SecondUserCreditScoreEnum.ABNORMAL_ACCOUNTV.getScore();
+                            secondUserCredit.setUserPoints(score);
+                            secondUserCreditMapper.updateById(secondUserCredit);
+
+                            SecondUserCreditRecord record = new SecondUserCreditRecord();
+                            record.setUserId(row.getUserId());
+                            record.setPointsType(SecondUserCreditScoreEnum.ABNORMAL_ACCOUNTV.getCode());
+                            record.setPoints(SecondUserCreditScoreEnum.ABNORMAL_ACCOUNTV.getScore());
+                            record.setCurrentScoreCount(score);
+                            record.setCreatedTime(new Date());
+                            secondUserCreditRecordMapper.insert(record);
+
+                            // 小于100分封禁用户
+                            if (score < 100) {
+                                LifeUser user1 = new LifeUser();
+                                user1.setId(row.getUserId());
+                                user1.setIsBanned(1);
+                                lifeUserMapper.updateById(user1);
+                            }
+                        }
+                    }
+
+//                    String detailInfo = lsit.stream()
+//                            .map(row -> row.getUserId().toString())
+//                            .collect(Collectors.joining(","));
+//                    alienSecondFeign.recordRiskControlData(user.getId(), 2, "账号异常", macIp, detailInfo);
+                }
+            }
+            // 第一次登录,添加用户基础积分
+            alienSecondFeign.createPointsRecord(user.getId(), 500, 1);
+        } catch (Exception e) {
+            log.error("用户登录log存放异常:{}", e);
+        }
+
+    }
+
+    /**
+     * 判断是否已存在违规记录
+     * 检查指定时间段内是否已经记录过该用户的风控信息
+     *
+     * @param startDate 开始时间
+     * @param endDate 结束时间
+     * @param macIp MAC地址/IP
+     * @param userId 用户ID
+     * @return boolean true-已存在违规记录,false-不存在
+     */
+    public boolean isViolation(String startDate, String endDate, String macIp, Integer userId) {
+        List<SecondRiskControlRecord> list = secondRiskControlRecordMapper.selectByBusinessId(startDate, endDate, macIp);
+        for (SecondRiskControlRecord record : list) {
+            // 将数组转换为包含整数的列表
+            List<Integer> userIdList = Arrays.stream(record.getDetailInfo().split(","))
+                    .map(Integer::parseInt)
+                    .collect(Collectors.toList());
+            if (userIdList.contains(userId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 16 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/service/StoreInfoService.java

@@ -0,0 +1,16 @@
+package shop.alien.gateway.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.StoreInfo;
+
+/**
+ * 二期-门店信息 服务类
+ *
+ * @author ssk
+ * @since 2024-12-05
+ */
+@Service
+public interface StoreInfoService extends IService<StoreInfo> {
+
+}

+ 22 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/service/StoreUserService.java

@@ -0,0 +1,22 @@
+package shop.alien.gateway.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.vo.StoreUserVo;
+
+/**
+ * 二期-门店用户 服务类
+ *
+ * @author ssk
+ * @since 2024-12-11
+ */
+public interface StoreUserService extends IService<StoreUser> {
+
+    /**
+     * 创建token
+     * @return
+     */
+    R<StoreUserVo> createToKen(StoreUser storeUser);
+
+}

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

@@ -0,0 +1,79 @@
+package shop.alien.gateway.service;
+
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeSys;
+import shop.alien.entity.store.UserLoginInfo;
+import shop.alien.entity.store.vo.SystemLoginVo;
+
+import java.util.List;
+
+/**
+ * 数据中台基本服务接口
+ *
+ * @author YinDP
+ * @since 2025-02-27
+ */
+public interface SystemService {
+
+    public SystemLoginVo login(String username, String password);
+
+    /**
+     * 退出登录
+     * <p>
+     * 退出登录时会执行以下操作:
+     * 1. 参数校验:验证用户登录信息是否有效
+     * 2. 删除Redis中的token缓存(key格式:web_用户名)
+     * 3. 返回退出登录结果
+     * </p>
+     *
+     * @param userLoginInfo 用户登录信息,包含userId、userName等信息
+     * @return SystemLoginVo 退出登录结果,包含退出状态信息
+     * @throws IllegalArgumentException 当用户信息为空或用户名为空时抛出
+     */
+    SystemLoginVo logout(UserLoginInfo userLoginInfo);
+
+    /**
+     * 系统用户注册
+     *
+     * @param userName 用户名
+     * @param password 密码
+     * @param roleId   角色id
+     * @param status 启用1 禁用0
+     * @param remark   角色id
+     * @return R<LifeSys>
+     */
+    R<LifeSys> register(String userName, String password, String roleId, Integer status, String remark);
+
+    /**
+     * 编辑账户
+     *
+     * @param lifeSys
+     */
+    int updateAccounInfo(LifeSys lifeSys);
+
+    /**
+     * 根据ID查询系统用户
+     *
+     * @param id 用户ID
+     * @return R<LifeSys>
+     */
+    R<LifeSys> getById(Integer id);
+
+    /**
+     * 查询系统用户列表
+     *
+     * @param userName 用户名(可选,模糊查询)
+     * @param status 账号状态(可选,启用1 禁用0)
+     * @return R<List<LifeSys>>
+     */
+    R<List<LifeSys>> list(String userName, Integer status);
+
+    /**
+     * 删除系统用户(逻辑删除)
+     *
+     * @param id 用户ID
+     * @return R<String>
+     */
+    R<String> deleteById(Integer id);
+
+}

+ 15 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/service/UserPointService.java

@@ -0,0 +1,15 @@
+package shop.alien.gateway.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.UserPoint;
+
+/**
+ * @author zhangchen
+ * @version 1.0
+ * @date 2025/09/05 10:00
+ */
+public interface UserPointService extends IService<UserPoint> {
+
+    UserPoint addPoint(Integer userId, Integer point);
+
+}

+ 120 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/ActivityInviteConfigServiceImpl.java

@@ -0,0 +1,120 @@
+package shop.alien.gateway.service.impl;
+
+import com.alibaba.nacos.common.utils.CollectionUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.ActivityInviteConfig;
+import shop.alien.entity.store.ActivityInviteLog;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.gateway.service.ActivityInviteConfigService;
+import shop.alien.gateway.service.UserPointService;
+import shop.alien.mapper.ActivityInviteConfigMapper;
+import shop.alien.mapper.ActivityInviteLogMapper;
+import shop.alien.mapper.LifeUserMapper;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Date;
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ActivityInviteConfigServiceImpl extends ServiceImpl<ActivityInviteConfigMapper, ActivityInviteConfig> implements ActivityInviteConfigService {
+
+    private final UserPointService userPointService;
+
+    private final ActivityInviteConfigMapper activityInviteConfigMapper;
+
+    private final LifeUserMapper lifeUserMapper;
+
+    private final ActivityInviteLogMapper activityInviteLogMapper;
+
+
+    @Override
+    public String bindInviteCode(Integer invitedUserId, String inviteCode) {
+        if (StringUtils.isNotBlank(inviteCode) && invitedUserId != null) {
+            // 根据邀请码查询邀请用户
+            LambdaQueryWrapper<LifeUser> lifeUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
+            lifeUserLambdaQueryWrapper.eq(LifeUser::getDeleteFlag, 0);
+            lifeUserLambdaQueryWrapper.apply("BINARY invite_code = {0}", inviteCode);
+            List<LifeUser> lifeUserList = lifeUserMapper.selectList(lifeUserLambdaQueryWrapper);
+            if (CollectionUtils.isEmpty(lifeUserList)) {
+                return "邀请码异常或邀请用户已注销";
+            }
+            LifeUser originalLifeUser = lifeUserList.get(0);
+            int inviteUserId = originalLifeUser.getId();
+            LifeUser invitedLifeUser = lifeUserMapper.selectById(invitedUserId);
+            if (invitedLifeUser == null) {
+                return "被邀请用户异常";
+            } else if (StringUtils.isNotBlank(invitedLifeUser.getBindInviteCode())) {
+                return "已经绑定邀请码不能重复绑定";
+            } else if (StringUtils.isNotBlank(invitedLifeUser.getInviteCode()) && invitedLifeUser.getInviteCode().equals(inviteCode)) {
+                return "不能绑定自己的邀请码";
+            } else {
+                LambdaQueryWrapper<ActivityInviteConfig> activityInviteConfigLambdaQueryWrapper = new LambdaQueryWrapper<>();
+                activityInviteConfigLambdaQueryWrapper.eq(ActivityInviteConfig::getDeleteFlag, 0);
+                List<ActivityInviteConfig> activityInviteConfigList = activityInviteConfigMapper.selectList(activityInviteConfigLambdaQueryWrapper);
+                if (CollectionUtils.isNotEmpty(activityInviteConfigList)) {
+                    ActivityInviteConfig activityInviteConfig = activityInviteConfigList.get(0);
+
+                    // 判断是否达到当天邀请最大次数
+                    Integer maxInviteNum = activityInviteConfig.getMaxInviteNum();
+                    if (maxInviteNum != null) {
+                        LambdaQueryWrapper<ActivityInviteLog> activityInviteLogLambdaQueryWrapper = new LambdaQueryWrapper<>();
+                        activityInviteLogLambdaQueryWrapper.eq(ActivityInviteLog::getInviteUserId, inviteUserId);
+                        activityInviteLogLambdaQueryWrapper.eq(ActivityInviteLog::getDeleteFlag, 0);
+                        // 使用 Date 对象而不是字符串,确保类型匹配
+                        LocalDate today = LocalDate.now();
+                        LocalDateTime startOfDay = today.atStartOfDay();
+                        LocalDateTime endOfDay = today.atTime(23, 59, 59);
+                        Date startDate = Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant());
+                        Date endDate = Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
+                        activityInviteLogLambdaQueryWrapper.between(ActivityInviteLog::getInviteTime, startDate, endDate);
+                        int todayInviteCount = activityInviteLogMapper.selectCount(activityInviteLogLambdaQueryWrapper);
+                        if (todayInviteCount > maxInviteNum) {
+                            return "绑定数量已超过当天最大绑定数量";
+                        }
+                    }
+
+                    ActivityInviteLog activityInviteLog = new ActivityInviteLog();
+                    activityInviteLog.setInviteUserId(inviteUserId);
+                    activityInviteLog.setInvitedUserId(invitedUserId);
+                    activityInviteLog.setActivityId(activityInviteConfig.getId());
+
+                    // 邀请奖励
+                    activityInviteLog.setInviteRewardType(activityInviteConfig.getInviteRewardType());
+                    activityInviteLog.setInviteRewardCoupon(activityInviteConfig.getInviteRewardCoupon());
+                    activityInviteLog.setInviteRewardPoint(activityInviteConfig.getInviteRewardPoint());
+                    // 被邀请奖励
+                    activityInviteLog.setInvitedRewardType(activityInviteConfig.getInvitedRewardType());
+                    activityInviteLog.setInvitedRewardCoupon(activityInviteConfig.getInvitedRewardCoupon());
+                    activityInviteLog.setInvitedRewardPoint(activityInviteConfig.getInvitedRewardPoint());
+                    activityInviteLog.setInviteTime(Date.from(Instant.now()));
+                    activityInviteLogMapper.insert(activityInviteLog);
+
+                    if (activityInviteConfig.getInviteRewardType() == 2) {
+                        userPointService.addPoint(inviteUserId, activityInviteConfig.getInviteRewardPoint());
+                    }
+                    if (activityInviteConfig.getInvitedRewardType() == 2) {
+                        userPointService.addPoint(invitedUserId, activityInviteConfig.getInvitedRewardPoint());
+                    }
+                    LifeUser updateLifeUser = new LifeUser();
+                    updateLifeUser.setId(invitedLifeUser.getId());
+                    updateLifeUser.setBindInviteCode(inviteCode);
+                    lifeUserMapper.updateById(updateLifeUser);
+                    return "绑定成功";
+                } else {
+                    return "活动未开始或已结束";
+                }
+            }
+        }
+        return "参数异常";
+    }
+}

+ 150 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/LawyerUserLogInServiceImpl.java

@@ -0,0 +1,150 @@
+package shop.alien.gateway.service.impl;//package shop.alien.gateway.service.impl;
+//
+//import com.alibaba.fastjson.JSONObject;
+//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+//import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+//import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+//import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+//import lombok.RequiredArgsConstructor;
+//import org.springframework.beans.BeanUtils;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.stereotype.Service;
+//import org.springframework.transaction.annotation.Transactional;
+//import shop.alien.entity.result.R;
+//import shop.alien.entity.store.LawFirm;
+//import shop.alien.entity.store.LawyerServiceArea;
+//import shop.alien.entity.store.LawyerUser;
+//import shop.alien.entity.store.dto.LawyerUserDto;
+//import shop.alien.entity.store.vo.LawyerUserVo;
+//import shop.alien.gateway.config.BaseRedisService;
+//import shop.alien.gateway.mapper.LawyerServiceAreaMapper;
+//import shop.alien.gateway.mapper.LawyerUserMapper;
+//import shop.alien.util.common.JwtUtil;
+//
+//import java.util.*;
+//import java.util.stream.Collectors;
+//
+///**
+// * 二期-门店用户 服务实现类
+// *
+// * @author ssk
+// * @since 2024-12-11
+// */
+//@Transactional
+//@Service
+//@RequiredArgsConstructor
+//public class LawyerUserLogInServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerUser> implements LawyerUserLogInService {
+//
+//    @Value("${jwt.expiration-time}")
+//    private String effectiveTime;
+//
+//    /**
+//     * 设定初始化默认密码
+//     */
+//
+//    private final BaseRedisService baseRedisService;
+//    private final LawyerUserMapper lawyerUserMapper;
+//    private final LawyerServiceAreaMapper lawyerServiceAreaMapper;
+//    private final LawFirmMapper lawFirmMapper;
+//
+//    /**
+//     * token
+//     *
+//     * @param lawyerUser
+//     * @return
+//     */
+//    @Override
+//    public R<LawyerUserVo> createToKen(LawyerUser lawyerUser) {
+//        int effectiveTimeInt = Integer.parseInt(effectiveTime.substring(0, effectiveTime.length() - 1));
+//        String effectiveTimeUnit = effectiveTime.substring(effectiveTime.length() - 1);
+//        long effectiveTimeIntLong = 0L;
+//        switch (effectiveTimeUnit) {
+//            case "s": {
+//                effectiveTimeIntLong = effectiveTimeInt * 1000L;
+//                break;
+//            }
+//            case "m": {
+//                effectiveTimeIntLong = effectiveTimeInt * 60L * 1000L;
+//                break;
+//            }
+//            case "h": {
+//                effectiveTimeIntLong = effectiveTimeInt * 60L * 60L * 1000L;
+//                break;
+//            }
+//            case "d": {
+//                effectiveTimeIntLong = effectiveTimeInt * 24L * 60L * 60L * 1000L;
+//                break;
+//            }
+//        }
+//
+//        LawyerUserVo lawyerUserVo = new LawyerUserVo();
+//        BeanUtils.copyProperties(lawyerUser, lawyerUserVo);
+//        Map<String, String> tokenMap = new HashMap<>();
+//        tokenMap.put("phone", lawyerUser.getPhone());
+//        tokenMap.put("userName", lawyerUser.getName());
+//        tokenMap.put("userId", lawyerUser.getId().toString());
+//        tokenMap.put("userType", "lawyer");
+//        tokenMap.put("passType", lawyerUser.getPassType().toString());
+//        lawyerUserVo.setToken(JwtUtil.createJWT("lawyer_" + lawyerUser.getPhone(), lawyerUser.getName(), JSONObject.toJSONString(tokenMap), effectiveTimeIntLong));
+//        baseRedisService.setString("lawyer_" + lawyerUser.getPhone(), lawyerUserVo.getToken());
+//        return R.data(lawyerUserVo);
+//    }
+//
+//    @Override
+//    public R<LawyerUserVo> logIn(LawyerUserDto lawyerUserDto) {
+//        LawyerUser lawyerUser = lawyerUserMapper.selectOne(new LambdaQueryWrapper<LawyerUser>()
+//                .eq(LawyerUser::getPhone, lawyerUserDto.getPhone()).last("limit 1"));
+//        if (ObjectUtils.isNotEmpty(lawyerUser)) {
+//            if (lawyerUser.getStatus() == 0) {
+//                return R.fail("账号被禁用");
+//            }
+//            lawyerUser.setPassType(1);
+//            return createToKen(lawyerUser);
+//        }else {
+//            LawyerUser user = new LawyerUser();
+//            BeanUtils.copyProperties(lawyerUserDto, user);
+//            user.setLogoutFlag(0);
+//            user.setStatus(1);
+//            user.setDeleteFlag(0);
+//            user.setIsOnline(1);
+//            user.setIsRecommended(0);
+//            user.setOrderReceivingStatus(0);
+//            lawyerUserMapper.insert(user);
+//            lawyerUserDto.getProblemScenarioIds().forEach(item -> {
+//                LawyerServiceArea lawyerServiceArea = new LawyerServiceArea();
+//                lawyerServiceArea.setLawyerUserId(user.getId());
+//                lawyerServiceArea.setProblemScenarioId(item);
+//                lawyerServiceArea.setCreatedTime(new Date());
+//                lawyerServiceAreaMapper.insert(lawyerServiceArea);
+//            });
+//            LambdaQueryWrapper<LawyerUser> lawFirmLambdaQueryWrapper = new LambdaQueryWrapper<>();
+//            lawFirmLambdaQueryWrapper.eq(LawyerUser::getFirmId, lawyerUserDto.getFirmId());
+//            List<LawyerUser> lawyerUsers = lawyerUserMapper.selectList(lawFirmLambdaQueryWrapper);
+//            if (ObjectUtils.isNotEmpty(lawyerUsers)) {
+//                String lawFirmIds = lawyerUsers.stream().map(u -> String.valueOf(u.getFirmId())).filter(Objects::nonNull).distinct().collect(Collectors.joining(","));
+//                LawFirm lawFirm = new LawFirm();
+//                lawFirm.setId(lawyerUserDto.getFirmId());
+//                lawFirmMapper.updateById(lawFirm);
+//            }
+//            user.setPassType(0);
+//            return createToKen(user);
+//        }
+//    }
+//
+//    @Override
+//    public R checkMsgCode(LawyerUserDto lawyerUserDto) {
+//        String string = baseRedisService.getString("lawyer_" + lawyerUserDto.getPhone());
+//        if (StringUtils.isNotEmpty(string)) {
+//            if (lawyerUserDto.getMsgCode().equals(string)) {
+//                LawyerUser lawyerUser = lawyerUserMapper.selectOne(new LambdaQueryWrapper<LawyerUser>()
+//                        .eq(LawyerUser::getPhone, lawyerUserDto.getPhone()));
+//                return R.data(lawyerUser);
+//            }else {
+//                return R.fail("验证码错误");
+//            }
+//        }else {
+//            return R.fail("验证码已过期");
+//        }
+//    }
+//
+//}

+ 22 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/StoreInfoServiceImpl.java

@@ -0,0 +1,22 @@
+package shop.alien.gateway.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.StoreInfo;
+import shop.alien.gateway.mapper.StoreInfoGatewayMapper;
+import shop.alien.gateway.service.StoreInfoService;
+
+/**
+ * 二期-门店信息 服务实现类
+ *
+ * @author ssk
+ * @since 2024-12-05
+ */
+@Service
+@RequiredArgsConstructor
+@Transactional
+public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoGatewayMapper, StoreInfo> implements StoreInfoService {
+
+}

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

@@ -0,0 +1,92 @@
+package shop.alien.gateway.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreInfo;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.vo.StoreUserVo;
+import shop.alien.gateway.config.BaseRedisService;
+import shop.alien.gateway.mapper.StoreInfoGatewayMapper;
+import shop.alien.gateway.mapper.StoreUserGatewayMapper;
+import shop.alien.gateway.service.StoreUserService;
+import shop.alien.util.common.JwtUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 二期-门店用户 服务实现类
+ *
+ * @author ssk
+ * @since 2024-12-11
+ */
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class StoreUserServiceImpl extends ServiceImpl<StoreUserGatewayMapper, StoreUser> implements StoreUserService {
+
+    @Value("${jwt.expiration-time}")
+    private String effectiveTime;
+
+    /**
+     * 设定初始化默认密码
+     */
+
+    private final StoreInfoGatewayMapper storeInfoMapper;
+
+    private final BaseRedisService baseRedisService;
+
+    /**
+     * token
+     *
+     * @param storeUser
+     * @return
+     */
+    @Override
+    public R<StoreUserVo> createToKen(StoreUser storeUser) {
+        int effectiveTimeInt = Integer.parseInt(effectiveTime.substring(0, effectiveTime.length() - 1));
+        String effectiveTimeUnit = effectiveTime.substring(effectiveTime.length() - 1);
+        long effectiveTimeIntLong = 0L;
+        switch (effectiveTimeUnit) {
+            case "s": {
+                effectiveTimeIntLong = effectiveTimeInt * 1000L;
+                break;
+            }
+            case "m": {
+                effectiveTimeIntLong = effectiveTimeInt * 60L * 1000L;
+                break;
+            }
+            case "h": {
+                effectiveTimeIntLong = effectiveTimeInt * 60L * 60L * 1000L;
+                break;
+            }
+            case "d": {
+                effectiveTimeIntLong = effectiveTimeInt * 24L * 60L * 60L * 1000L;
+                break;
+            }
+        }
+
+        StoreUserVo storeUserVo = new StoreUserVo();
+        BeanUtils.copyProperties(storeUser, storeUserVo);
+        Map<String, String> tokenMap = new HashMap<>();
+        tokenMap.put("phone", storeUser.getPhone());
+        tokenMap.put("userName", storeUser.getName());
+        tokenMap.put("userId", storeUser.getId().toString());
+        tokenMap.put("userType", "store");
+        storeUserVo.setToken(JwtUtil.createJWT("store_" + storeUser.getPhone(), storeUser.getName(), JSONObject.toJSONString(tokenMap), effectiveTimeIntLong));
+        baseRedisService.setString("store_" + storeUser.getPhone(), storeUserVo.getToken());
+        StoreInfo storeInfo = storeInfoMapper.selectById(storeUser.getStoreId());
+        if (storeInfo != null) {
+            storeUserVo.setBusinessSection(storeInfo.getBusinessSection());
+            storeUserVo.setBusinessTypesName(storeInfo.getBusinessTypesName());
+        }
+        return R.data(storeUserVo);
+    }
+
+}

+ 303 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/SystemServiceImpl.java

@@ -0,0 +1,303 @@
+package shop.alien.gateway.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeSys;
+import shop.alien.entity.store.LifeSysMenu;
+import shop.alien.entity.store.UserLoginInfo;
+import shop.alien.entity.store.vo.SystemLoginVo;
+import shop.alien.gateway.config.BaseRedisService;
+import shop.alien.gateway.feign.StoreServiceFeign;
+import shop.alien.gateway.mapper.LifeSysGatewayMapper;
+import shop.alien.gateway.service.SystemService;
+import shop.alien.util.common.JwtUtil;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ * 商家会员记录 服务实现类
+ * </p>
+ *
+ * @author ssk
+ * @since 2025-02-20
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SystemServiceImpl implements SystemService {
+
+    private final LifeSysGatewayMapper lifeSysMapper;
+
+    @Autowired
+    private final StoreServiceFeign storeServiceFeign;
+
+    private final BaseRedisService baseRedisService;
+
+    @Value("${jwt.expiration-time}")
+    private String effectiveTime;
+
+    @Override
+    public SystemLoginVo login(String username, String password) {
+        int effectiveTimeInt = Integer.parseInt(effectiveTime.substring(0, effectiveTime.length() - 1));
+        String effectiveTimeUnit = effectiveTime.substring(effectiveTime.length() - 1);
+        long effectiveTimeIntLong = 0L;
+        switch (effectiveTimeUnit) {
+            case "s": {
+                effectiveTimeIntLong = effectiveTimeInt * 1000L;
+                break;
+            }
+            case "m": {
+                effectiveTimeIntLong = effectiveTimeInt * 60L * 1000L;
+                break;
+            }
+            case "h": {
+                effectiveTimeIntLong = effectiveTimeInt * 60L * 60L * 1000L;
+                break;
+            }
+            case "d": {
+                effectiveTimeIntLong = effectiveTimeInt * 24L * 60L * 60L * 1000L;
+                break;
+            }
+        }
+        SystemLoginVo result = new SystemLoginVo();
+        //给密码加密MD5,查询用户是否存在
+        LifeSys lifeSys = lifeSysMapper.selectOne(
+                new LambdaQueryWrapper<LifeSys>()
+                        .eq(LifeSys::getUserName, username)
+        );
+        if (lifeSys != null && lifeSys.getUserPassword().equals(password)) {
+            Map<String, String> tokenMap = new HashMap<>();
+            tokenMap.put("phone", "123456");
+            tokenMap.put("userName", lifeSys.getUserName());
+            tokenMap.put("userId", String.valueOf(lifeSys.getId()));
+            tokenMap.put("userType", "web");
+            R<List<LifeSysMenu>> menuByUserId = storeServiceFeign.getMenuByUserId(lifeSys.getId().longValue(), 1L);
+            List<LifeSysMenu> data = menuByUserId.getData();
+            tokenMap.put("menuList", JSONObject.toJSONString(data));
+            //存入token
+            result.setToken(JwtUtil.createJWT("web_" + lifeSys.getId(), lifeSys.getUserName(), JSONObject.toJSONString(tokenMap), effectiveTimeIntLong));
+            baseRedisService.setString("web_" + lifeSys.getUserName(), result.getToken());
+            //登录结果
+            result.setResult(true);
+            //角色Id
+            result.setRoleId(lifeSys.getRoleId());
+            //菜单列表
+            result.setMenuList(data);
+            //登录结果
+            result.setMessage("登录成功!!");
+        } else {
+            result.setResult(false);
+            result.setMessage("用户名或者密码错误,请确认!!");
+        }
+        return result;
+    }
+
+    /**
+     * 退出登录
+     * <p>
+     * 退出登录时会执行以下操作:
+     * 1. 参数校验:验证用户登录信息是否有效
+     * 2. 删除Redis中的token缓存(key格式:web_用户名)
+     * 3. 返回退出登录结果
+     * </p>
+     *
+     * @param userLoginInfo 用户登录信息,包含userId、userName等信息
+     * @return SystemLoginVo 退出登录结果,包含退出状态信息
+     * @throws IllegalArgumentException 当用户信息为空或用户名为空时抛出
+     */
+    @Override
+    public SystemLoginVo logout(UserLoginInfo userLoginInfo) {
+        // 参数校验
+        if (userLoginInfo == null) {
+            log.warn("退出登录失败:用户登录信息为空");
+            throw new IllegalArgumentException("用户登录信息不能为空");
+        }
+        
+        if (!StringUtils.hasText(userLoginInfo.getUserName())) {
+            log.warn("退出登录失败:用户名为空,userId={}", userLoginInfo.getUserId());
+            throw new IllegalArgumentException("用户名不能为空");
+        }
+        
+        log.info("开始退出登录,userId={}, userName={}", userLoginInfo.getUserId(), userLoginInfo.getUserName());
+        
+        try {
+            // 删除Redis中的token缓存
+            String redisKey = "web_" + userLoginInfo.getUserName();
+            String existingToken = baseRedisService.getString(redisKey);
+            
+            if (StringUtils.hasText(existingToken)) {
+                baseRedisService.delete(redisKey);
+                log.info("删除Redis token成功,redisKey={}, userId={}, userName={}", 
+                        redisKey, userLoginInfo.getUserId(), userLoginInfo.getUserName());
+            } else {
+                log.warn("Redis中不存在token,可能已过期或已删除,redisKey={}, userId={}, userName={}", 
+                        redisKey, userLoginInfo.getUserId(), userLoginInfo.getUserName());
+            }
+            
+            // 构建返回结果
+            SystemLoginVo result = new SystemLoginVo();
+            result.setResult(true);
+            result.setMessage("退出登录成功");
+            
+            log.info("退出登录成功,userId={}, userName={}", userLoginInfo.getUserId(), userLoginInfo.getUserName());
+            return result;
+        } catch (Exception e) {
+            log.error("退出登录失败,userId={}, userName={}, error={}", 
+                    userLoginInfo.getUserId(), userLoginInfo.getUserName(), e.getMessage(), e);
+            throw new RuntimeException("退出登录失败:" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public R<LifeSys> register(String userName, String password, String roleId, Integer status, String remark) {
+        // 校验必填字段
+        if (!StringUtils.hasText(userName)) {
+            return R.fail("用户名不能为空");
+        }
+        if (!StringUtils.hasText(password)) {
+            return R.fail("密码不能为空");
+        }
+
+        // 校验用户名是否已存在
+        LambdaQueryWrapper<LifeSys> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LifeSys::getUserName, userName);
+        LifeSys existingUser = lifeSysMapper.selectOne(queryWrapper);
+        if (existingUser != null) {
+            // 如果是 admin 用户,给出特殊提示
+            if ("admin".equalsIgnoreCase(userName)) {
+                return R.fail("admin 用户已存在,不允许重复添加");
+            }
+            return R.fail("该用户名已存在,请更换其他用户名");
+        }
+
+        // 额外校验:如果尝试注册 admin 用户,检查数据库中是否已存在 admin(防止大小写变体)
+        if ("admin".equalsIgnoreCase(userName)) {
+            LambdaQueryWrapper<LifeSys> adminQueryWrapper = new LambdaQueryWrapper<>();
+            adminQueryWrapper.eq(LifeSys::getUserName, "admin");
+            LifeSys adminUser = lifeSysMapper.selectOne(adminQueryWrapper);
+            if (adminUser != null) {
+                return R.fail("admin 用户已存在,不允许重复添加");
+            }
+        }
+
+        // 创建新用户
+        LifeSys lifeSys = new LifeSys();
+        lifeSys.setUserName(userName);
+        lifeSys.setUserPassword(password);
+        lifeSys.setRoleId(roleId);
+        lifeSys.setStatus(status);
+        lifeSys.setDeleteFlag(0);
+        lifeSys.setRemark(remark);
+        lifeSys.setCreatedTime(new Date());
+
+        // 密码使用MD5加密后存储
+//        lifeSys.setUserPassword(encryptToMD5(password));
+//        if (StringUtils.hasText(roleId)) {
+//            lifeSys.setRoleId(roleId);
+//        }
+
+        // 保存用户
+        int result = lifeSysMapper.insert(lifeSys);
+        if (result > 0) {
+            return R.data(lifeSys, "账号添加成功");
+        }
+        return R.fail("注册失败,请稍后重试");
+    }
+
+    @Override
+    public int updateAccounInfo(LifeSys lifeSys) {
+        return lifeSysMapper.updateById(lifeSys);
+    }
+
+    @Override
+    public R<LifeSys> getById(Integer id) {
+        if (id == null) {
+            return R.fail("用户ID不能为空");
+        }
+        LifeSys lifeSys = lifeSysMapper.selectById(id);
+        if (lifeSys == null) {
+            return R.fail("用户不存在");
+        }
+        return R.data(lifeSys);
+    }
+
+    @Override
+    public R<List<LifeSys>> list(String userName, Integer status) {
+        LambdaQueryWrapper<LifeSys> queryWrapper = new LambdaQueryWrapper<>();
+        if (StringUtils.hasText(userName)) {
+            queryWrapper.like(LifeSys::getUserName, userName);
+        }
+        if (status != null) {
+            queryWrapper.eq(LifeSys::getStatus, status);
+        }
+        queryWrapper.orderByDesc(LifeSys::getCreatedTime);
+        List<LifeSys> list = lifeSysMapper.selectList(queryWrapper);
+        
+        // 如果用户名是 "admin",则不返回 roleId
+        for (LifeSys lifeSys : list) {
+            if ("admin".equalsIgnoreCase(lifeSys.getUserName())) {
+                lifeSys.setRoleId(null);
+            }
+        }
+        
+        return R.data(list);
+    }
+
+    @Override
+    public R<String> deleteById(Integer id) {
+        if (id == null) {
+            return R.fail("用户ID不能为空");
+        }
+        // 先查询用户是否存在
+        LifeSys lifeSys = lifeSysMapper.selectById(id);
+        if (lifeSys == null) {
+            return R.fail("用户不存在");
+        }
+        // 使用 MyBatis-Plus 的逻辑删除,会自动将 deleteFlag 设置为 1
+        int result = lifeSysMapper.deleteById(id);
+        if (result > 0) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    public static String encryptToMD5(String input) {
+        try {
+            // 获取 MD5 算法的 MessageDigest 实例
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            // 将输入的字符串转换为字节数组,并进行 MD5 计算
+            byte[] messageDigest = md.digest(input.getBytes());
+
+            // 创建一个 StringBuilder 用于存储十六进制字符串
+            StringBuilder hexString = new StringBuilder();
+            for (byte b : messageDigest) {
+                // 将字节转换为十六进制字符串
+                String hex = Integer.toHexString(0xFF & b);
+                if (hex.length() == 1) {
+                    // 如果十六进制字符串长度为 1,则在前面补 0
+                    hexString.append('0');
+                }
+                hexString.append(hex);
+            }
+            // 返回最终的 MD5 加密后的十六进制字符串
+            return hexString.toString();
+        } catch (NoSuchAlgorithmException e) {
+            // 若指定的算法(MD5)不可用,抛出运行时异常
+            throw new RuntimeException(e);
+        }
+    }
+
+}

+ 50 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/service/impl/UserPointServiceImpl.java

@@ -0,0 +1,50 @@
+package shop.alien.gateway.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import shop.alien.entity.store.UserPoint;
+import shop.alien.gateway.service.UserPointService;
+import shop.alien.mapper.UserPointMapper;
+
+import java.util.List;
+
+/**
+ * 签到类型活动服务
+ *
+ * @author zhangchen
+ * @since 2025-9-8
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class UserPointServiceImpl extends ServiceImpl<UserPointMapper, UserPoint> implements UserPointService {
+
+    private final UserPointMapper userPointMapper;
+
+    @Override
+    public UserPoint addPoint(Integer userId, Integer point) {
+        UserPoint userPoint = new UserPoint();
+        if (userId != null && point != null) {
+            LambdaQueryWrapper<UserPoint> userPointLambdaQueryWrapper = new LambdaQueryWrapper<>();
+            userPointLambdaQueryWrapper.eq(UserPoint::getUserId, userId);
+            userPointLambdaQueryWrapper.eq(UserPoint::getDeleteFlag, 0);
+            List<UserPoint> userPointList = userPointMapper.selectList(userPointLambdaQueryWrapper);
+            if (!CollectionUtils.isEmpty(userPointList)) {
+                userPoint = userPointList.get(0);
+                int totalPoint = point + userPoint.getUserPoint();
+                userPoint.setUserPoint(totalPoint);
+                userPointMapper.updateById(userPoint);
+            } else {
+                userPoint.setUserId(userId);
+                userPoint.setUserPoint(point);
+                userPointMapper.insert(userPoint);
+            }
+        }
+        return userPoint;
+    }
+
+}

+ 253 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/util/Base64.java

@@ -0,0 +1,253 @@
+package shop.alien.gateway.util;
+
+/**
+ * Base64工具类
+ *
+ * @author ssk
+ */
+public final class Base64 {
+    static private final int BASELENGTH = 128;
+    static private final int LOOKUPLENGTH = 64;
+    static private final int TWENTYFOURBITGROUP = 24;
+    static private final int EIGHTBIT = 8;
+    static private final int SIXTEENBIT = 16;
+    static private final int FOURBYTE = 4;
+    static private final int SIGN = -128;
+    static private final char PAD = '=';
+    static final private byte[] base64Alphabet = new byte[BASELENGTH];
+    static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
+
+    static {
+        for (int i = 0; i < BASELENGTH; ++i) {
+            base64Alphabet[i] = -1;
+        }
+        for (int i = 'Z'; i >= 'A'; i--) {
+            base64Alphabet[i] = (byte) (i - 'A');
+        }
+        for (int i = 'z'; i >= 'a'; i--) {
+            base64Alphabet[i] = (byte) (i - 'a' + 26);
+        }
+
+        for (int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i - '0' + 52);
+        }
+
+        base64Alphabet['+'] = 62;
+        base64Alphabet['/'] = 63;
+
+        for (int i = 0; i <= 25; i++) {
+            lookUpBase64Alphabet[i] = (char) ('A' + i);
+        }
+
+        for (int i = 26, j = 0; i <= 51; i++, j++) {
+            lookUpBase64Alphabet[i] = (char) ('a' + j);
+        }
+
+        for (int i = 52, j = 0; i <= 61; i++, j++) {
+            lookUpBase64Alphabet[i] = (char) ('0' + j);
+        }
+        lookUpBase64Alphabet[62] = (char) '+';
+        lookUpBase64Alphabet[63] = (char) '/';
+    }
+
+    private static boolean isWhiteSpace(char octect) {
+        return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
+    }
+
+    private static boolean isPad(char octect) {
+        return (octect == PAD);
+    }
+
+    private static boolean isData(char octect) {
+        return (octect < BASELENGTH && base64Alphabet[octect] != -1);
+    }
+
+    /**
+     * Encodes hex octects into Base64
+     *
+     * @param binaryData Array containing binaryData
+     * @return Encoded Base64 array
+     */
+    public static String encode(byte[] binaryData) {
+        if (binaryData == null) {
+            return null;
+        }
+
+        int lengthDataBits = binaryData.length * EIGHTBIT;
+        if (lengthDataBits == 0) {
+            return "";
+        }
+
+        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
+        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
+        int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
+        char encodedData[] = null;
+
+        encodedData = new char[numberQuartet * 4];
+
+        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
+
+        int encodedIndex = 0;
+        int dataIndex = 0;
+
+        for (int i = 0; i < numberTriplets; i++) {
+            b1 = binaryData[dataIndex++];
+            b2 = binaryData[dataIndex++];
+            b3 = binaryData[dataIndex++];
+
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+            byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
+
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
+        }
+
+        // form integral number of 6-bit groups
+        if (fewerThan24bits == EIGHTBIT) {
+            b1 = binaryData[dataIndex];
+            k = (byte) (b1 & 0x03);
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
+            encodedData[encodedIndex++] = PAD;
+            encodedData[encodedIndex++] = PAD;
+        } else if (fewerThan24bits == SIXTEENBIT) {
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
+            encodedData[encodedIndex++] = PAD;
+        }
+        return new String(encodedData);
+    }
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param encoded string containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public static byte[] decode(String encoded) {
+        if (encoded == null) {
+            return null;
+        }
+
+        char[] base64Data = encoded.toCharArray();
+        // remove white spaces
+        int len = removeWhiteSpace(base64Data);
+
+        if (len % FOURBYTE != 0) {
+            return null;// should be divisible by four
+        }
+
+        int numberQuadruple = (len / FOURBYTE);
+
+        if (numberQuadruple == 0) {
+            return new byte[0];
+        }
+
+        byte decodedData[] = null;
+        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
+        char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
+
+        int i = 0;
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        decodedData = new byte[(numberQuadruple) * 3];
+
+        for (; i < numberQuadruple - 1; i++) {
+
+            if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
+                    || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) {
+                return null;
+            } // if found "no data" just return null
+
+            b1 = base64Alphabet[d1];
+            b2 = base64Alphabet[d2];
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+        }
+
+        if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
+            return null;// if found "no data" just return null
+        }
+
+        b1 = base64Alphabet[d1];
+        b2 = base64Alphabet[d2];
+
+        d3 = base64Data[dataIndex++];
+        d4 = base64Data[dataIndex++];
+        if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters
+            if (isPad(d3) && isPad(d4)) {
+                if ((b2 & 0xf) != 0)// last 4 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 1];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                return tmp;
+            } else if (!isPad(d3) && isPad(d4)) {
+                b3 = base64Alphabet[d3];
+                if ((b3 & 0x3) != 0)// last 2 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 2];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+                tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+                return tmp;
+            } else {
+                return null;
+            }
+        } else { // No PAD e.g 3cQl
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+
+        }
+        return decodedData;
+    }
+
+    /**
+     * remove WhiteSpace from MIME containing encoded Base64 data.
+     *
+     * @param data the byte array of base64 data (with WS)
+     * @return the new length
+     */
+    private static int removeWhiteSpace(char[] data) {
+        if (data == null) {
+            return 0;
+        }
+
+        // count characters that's not whitespace
+        int newSize = 0;
+        int len = data.length;
+        for (int i = 0; i < len; i++) {
+            if (!isWhiteSpace(data[i])) {
+                data[newSize++] = data[i];
+            }
+        }
+        return newSize;
+    }
+}

+ 56 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/util/KaptchaTextCreator.java

@@ -0,0 +1,56 @@
+package shop.alien.gateway.util;
+
+import com.google.code.kaptcha.text.impl.DefaultTextCreator;
+
+import java.util.Random;
+
+/**
+ * 验证码文本生成器
+ *
+ * @author ruoyi
+ */
+public class KaptchaTextCreator extends DefaultTextCreator {
+    private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
+
+    @Override
+    public String getText() {
+        Integer result = 0;
+        Random random = new Random();
+        int x = random.nextInt(10);
+        int y = random.nextInt(10);
+        StringBuilder suChinese = new StringBuilder();
+        int randomoperands = random.nextInt(3);
+        if (randomoperands == 0) {
+            result = x * y;
+            suChinese.append(CNUMBERS[x]);
+            suChinese.append("*");
+            suChinese.append(CNUMBERS[y]);
+        } else if (randomoperands == 1) {
+            if ((x != 0) && y % x == 0) {
+                result = y / x;
+                suChinese.append(CNUMBERS[y]);
+                suChinese.append("/");
+                suChinese.append(CNUMBERS[x]);
+            } else {
+                result = x + y;
+                suChinese.append(CNUMBERS[x]);
+                suChinese.append("+");
+                suChinese.append(CNUMBERS[y]);
+            }
+        } else {
+            if (x >= y) {
+                result = x - y;
+                suChinese.append(CNUMBERS[x]);
+                suChinese.append("-");
+                suChinese.append(CNUMBERS[y]);
+            } else {
+                result = y - x;
+                suChinese.append(CNUMBERS[y]);
+                suChinese.append("-");
+                suChinese.append(CNUMBERS[x]);
+            }
+        }
+        suChinese.append("=?@" + result);
+        return suChinese.toString();
+    }
+}

+ 108 - 0
alien-service-gateway/src/main/java/shop/alien/gateway/util/SmsUtil.java

@@ -0,0 +1,108 @@
+package shop.alien.gateway.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+/**
+ * 短信发送工具类
+ * 
+ * 注意:如果需要实际发送短信,需要在pom.xml中添加阿里云短信SDK依赖:
+ * <dependency>
+ *     <groupId>com.aliyun</groupId>
+ *     <artifactId>dysmsapi20170525</artifactId>
+ *     <version>3.0.0</version>
+ * </dependency>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Component
+public class SmsUtil {
+
+    @Value("${ali.sms.accessKeyId:}")
+    private String accessKeyId;
+
+    @Value("${ali.sms.accessKeySecret:}")
+    private String accessKeySecret;
+
+    @Value("${ali.sms.endPoint:}")
+    private String endPoint;
+
+    @Value("${ali.sms.signName:}")
+    private String signName;
+
+    @Value("${ali.sms.templateCode:}")
+    private String templateCode;
+
+    /**
+     * 发送账号密码短信
+     * 短信内容:您的账号{userName},密码是{password}
+     *
+     * @param phone    手机号
+     * @param userName 账号(用户名)
+     * @param password 密码
+     * @return 是否发送成功
+     */
+    public boolean sendAccountPasswordSms(String phone, String userName, String password) {
+        if (!StringUtils.hasText(phone)) {
+            log.warn("手机号为空,无法发送短信");
+            return false;
+        }
+
+        log.info("SmsUtil.sendAccountPasswordSms?phone={},userName={}", phone, userName);
+
+        try {
+            // 构建短信内容:您的账号{userName},密码是{password}
+            String messageContent = String.format("您的账号%s,密码是%s", userName, password);
+
+            // 如果配置为空,只记录日志(测试环境)
+            if (!StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
+                log.info("短信配置未设置,仅记录日志。短信内容:{}", messageContent);
+                return true; // 测试环境返回成功
+            }
+
+            // TODO: 如果需要实际发送短信,请取消下面的注释并添加阿里云短信SDK依赖
+            /*
+            Config config = new Config()
+                    .setEndpoint(endPoint)
+                    .setAccessKeyId(accessKeyId)
+                    .setAccessKeySecret(accessKeySecret);
+
+            // 构建发送请求
+            // 注意:这里使用模板方式,如果阿里云有专门的账号密码通知模板,请使用对应的templateCode
+            // 如果没有,可以使用通用模板,将内容作为参数传递
+            SendSmsRequest sendSmsRequest = new SendSmsRequest()
+                    .setSignName(signName)
+                    .setTemplateCode(templateCode)
+                    .setPhoneNumbers(phone)
+                    // 根据实际模板参数调整,这里假设模板参数为 {"account":"账号","password":"密码"}
+                    .setTemplateParam("{\"account\":\"" + userName + "\",\"password\":\"" + password + "\"}");
+
+            RuntimeOptions runtime = new RuntimeOptions();
+            Client client = new Client(config);
+            SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime);
+
+            if ("OK".equals(sendSmsResponse.getBody().getCode())) {
+                log.info("账号密码短信发送成功,phone={}, userName={}", phone, userName);
+                return true;
+            } else {
+                log.error("账号密码短信发送失败,phone={}, userName={}, error={}",
+                        phone, userName, sendSmsResponse.getBody().getMessage());
+                return false;
+            }
+            */
+            
+            // 当前实现:仅记录日志
+            log.info("账号密码短信内容:{},发送至:{}", messageContent, phone);
+            return true;
+            
+        } catch (Exception e) {
+            log.error("发送账号密码短信异常,phone={}, userName={}, error={}", phone, userName, e.getMessage(), e);
+            return false;
+        }
+    }
+}
+

+ 20 - 0
alien-service-gateway/src/main/resources/bootstrap-dev.yml

@@ -0,0 +1,20 @@
+spring:
+  application:
+    name: alien-gateway
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: 192.168.2.252:8848
+        username: nacos
+        password: ngfriend198092
+
+      #配置中心
+      config:
+        enabled: true
+        server-addr: 192.168.2.252:8848
+        username: nacos
+        password: ngfriend198092
+        group: DEFAULT_GROUP
+        file-extension: yml

+ 21 - 0
alien-service-gateway/src/main/resources/bootstrap-localhost.yml

@@ -0,0 +1,21 @@
+spring:
+  application:
+    name: alien-gateway
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: 192.168.2.252:8848
+        username: nacos
+        password: ngfriend198092
+        namespace: 599eda62-5d81-47f5-922c-65b7f39735e7
+
+      #配置中心
+      config:
+        enabled: true
+        server-addr: 192.168.2.252:8848
+        username: nacos
+        password: ngfriend198092
+        file-extension: yml
+        namespace: 599eda62-5d81-47f5-922c-65b7f39735e7

+ 22 - 0
alien-service-gateway/src/main/resources/bootstrap-prod.yml

@@ -0,0 +1,22 @@
+spring:
+  application:
+    name: alien-gateway
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: localhost:8848
+        username: nacos
+        password: ngfriend198092
+        namespace: 3cbb802a-b56e-47f7-b658-b5012ecafb1f
+
+      #配置中心
+      config:
+        enabled: true
+        server-addr: localhost:8848
+        username: nacos
+        password: ngfriend198092
+        group: DEFAULT_GROUP
+        file-extension: yml
+        namespace: 3cbb802a-b56e-47f7-b658-b5012ecafb1f

+ 22 - 0
alien-service-gateway/src/main/resources/bootstrap-test.yml

@@ -0,0 +1,22 @@
+spring:
+  application:
+    name: alien-gateway
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: 192.168.2.252:8848
+        username: nacos
+        password: ngfriend198092
+        namespace: 0e1e2d77-56e8-422c-8317-6f71d7285e59
+
+      #配置中心
+      config:
+        enabled: true
+        server-addr: 192.168.2.252:8848
+        username: nacos
+        password: ngfriend198092
+        group: DEFAULT_GROUP
+        file-extension: yml
+        namespace: 0e1e2d77-56e8-422c-8317-6f71d7285e59

+ 22 - 0
alien-service-gateway/src/main/resources/bootstrap-uat.yml

@@ -0,0 +1,22 @@
+spring:
+  application:
+    name: alien-gateway
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: localhost:8848
+        username: nacos
+        password: ngfriend198092
+        namespace: 79060c39-10ad-4098-9022-5e8a47796f8f
+
+      #配置中心
+      config:
+        enabled: true
+        server-addr: localhost:8848
+        username: nacos
+        password: ngfriend198092
+        group: DEFAULT_GROUP
+        file-extension: yml
+        namespace: 79060c39-10ad-4098-9022-5e8a47796f8f

+ 3 - 0
alien-service-gateway/src/main/resources/bootstrap.yml

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

+ 173 - 0
alien-service-gateway/src/main/resources/logback-spring.xml

@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
+<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
+<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
+<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
+<!-- 该信息是由于设置了当配置文件变化时重新加载,所以每当达到扫描时间的时候就会检查配置文件是否错误。但是由于一般配置文件都放在了JAR包中,
+    而扫描的时候无法扫描JAR包内,因此会提示没有可以检查的文件,所以每隔一段时间就输出一次-->
+<configuration scan="false" scanPeriod="60 seconds" debug="true">
+    <contextName>logback-spring</contextName>
+
+    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使“${}”来使用变量。 -->
+    <!-- 定义全局参数常量 -->
+    <property name="log.level" value="debug"/>
+    <property name="log.maxHistory" value="30"/><!-- 30表示30个 -->
+    <springProperty scope="context" name="logging.path" source="logging.path"/>
+    <!--输出文件前缀-->
+    <property name="FILENAME" value="xiaokuihua_gateway"/>
+
+    <!--0. 日志格式和颜色渲染 -->
+    <!-- 彩色日志依赖的渲染类 -->
+    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
+    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
+    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
+
+    <!-- 文件输出格式 -->
+    <property name="FILE_LOG_PATTERN" value="[%d{MM/dd HH:mm:ss.SSS}][%-10.10thread][%-5level][%-40.40c{1}:%5line]:[%15method] || %m%n"/>
+    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+
+    <!--1. 输出到控制台-->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>${log.level}</level>
+        </filter>
+        <encoder>
+            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+            <!-- 设置字符集 -->
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <!--2. 输出到文档-->
+    <!-- DEBUG 日志 -->
+    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 当前的日志文件存放路径 -->
+        <file>${logging.path}/DEBUG.log</file>
+        <!-- 日志滚动策略 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 历史日志文件的存放路径和名称 -->
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_DEBUG.log.gz</fileNamePattern>
+            <!-- 日志文件最大的保存历史 数量-->
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <!--日志文件最大的大小-->
+        <!--        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">-->
+        <!--            <MaxFileSize>10MB</MaxFileSize>-->
+        <!--        </triggeringPolicy>-->
+        <!-- 此日志文档只记录debug级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>DEBUG</level>
+            <onMatch>ACCEPT</onMatch>  <!-- 用过滤器,只接受DEBUG级别的日志信息,其余全部过滤掉 -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- INFO 日志 -->
+    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logging.path}/INFO.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_INFO.log.gz</fileNamePattern>
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- WARN 日志 -->
+    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logging.path}/WARN.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_WARN.log.gz</fileNamePattern>
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>WARN</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logging.path}/ERROR.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_ERROR.log.gz</fileNamePattern>
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--
+      <logger>用来设置某一个包或者具体的某一个类的日志打印级别、
+      以及指定<appender>。<logger>仅有一个name属性,
+      一个可选的level和一个可选的addtivity属性。
+      name:用来指定受此logger约束的某一个包或者具体的某一个类。
+      level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+         还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
+         如果未设置此属性,那么当前logger将会继承上级的级别。
+      addtivity:是否向上级logger传递打印信息。默认是true。
+      <logger name="org.springframework.web" level="info"/>
+      <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
+    -->
+
+    <!--
+      使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
+      第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
+      第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
+      【logging.level.org.mybatis=debug logging.level.dao=debug】
+     -->
+    <!-- mybatis显示sql,修改此处扫描包名 -->
+
+
+    <!--
+      root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
+      level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+      不能设置为INHERITED或者同义词NULL。默认是DEBUG
+      可以包含零个或多个元素,标识这个appender将会添加到这个logger。
+    -->
+
+    <!-- 4. 最终的策略 -->
+    <!-- 4.1 开发环境:打印控制台-->
+    <!--打印sql-->
+    <!--    <logger name="com.veryhappy.music.dao" level="debug"/>-->
+
+    <!--打印log-->
+    <root level="info">
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="DEBUG_FILE"/>
+        <appender-ref ref="INFO_FILE"/>
+        <appender-ref ref="WARN_FILE"/>
+        <appender-ref ref="ERROR_FILE"/>
+    </root>
+
+    <!--   4.2 生产环境:输出到文档-->
+    <springProfile name="pro">
+        <root level="info">
+            <appender-ref ref="CONSOLE"/>
+            <appender-ref ref="DEBUG_FILE"/>
+            <appender-ref ref="INFO_FILE"/>
+            <appender-ref ref="ERROR_FILE"/>
+            <appender-ref ref="WARN_FILE"/>
+        </root>
+    </springProfile>
+</configuration>

+ 11 - 0
alien-service-gateway/src/main/resources/mapper/StoreUserGatewayMapper.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.gateway.mapper.StoreUserGatewayMapper">
+
+    <!-- 查询已删除的用户 -->
+    <select id="getRemoveUser" resultType="shop.alien.entity.store.StoreUser">
+        select concat(nick_name,"(账号已注销)") as name,phone FROM store_user where id = #{id}
+    </select>
+</mapper>