소스 검색

Merge branch 'sit' of http://8.152.195.41:3000/alien/alien_cloud into secondReport

# Conflicts:
#	alien-gateway/src/main/java/shop/alien/gateway/controller/LifeUserController.java
#	alien-store/src/main/java/shop/alien/store/controller/LifeUserController.java
qrs 1 개월 전
부모
커밋
c1614e9c1a
38개의 변경된 파일1258개의 추가작업 그리고 435개의 파일을 삭제
  1. 2 0
      README.md
  2. 1 1
      alien-entity/src/main/java/shop/alien/entity/store/ActivitySignInConfig.java
  3. 6 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/ActivityInviteLogVo.java
  4. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeAppealManageVo.java
  5. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeUserOrderVo.java
  6. 5 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeUserViolationVo.java
  7. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeUserVo.java
  8. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreIncomeDetailsRecordVo.java
  9. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreUserVo.java
  10. 26 4
      alien-entity/src/main/java/shop/alien/mapper/ActivityInviteLogMapper.java
  11. 16 0
      alien-entity/src/main/java/shop/alien/mapper/LifeCouponMapper.java
  12. 92 1
      alien-entity/src/main/java/shop/alien/mapper/LifeUserViolationMapper.java
  13. 38 2
      alien-entity/src/main/java/shop/alien/mapper/StoreCommentMapper.java
  14. 3 1
      alien-entity/src/main/resources/mapper/LifeAppealManageMapper.xml
  15. 13 1
      alien-entity/src/main/resources/mapper/LifeUserOrderMapper.xml
  16. 5 4
      alien-entity/src/main/resources/mapper/PlatformLifeUserMapper.xml
  17. 7 0
      alien-gateway/pom.xml
  18. 57 0
      alien-gateway/src/main/java/shop/alien/gateway/config/BeanConfig.java
  19. 70 0
      alien-gateway/src/main/java/shop/alien/gateway/controller/CaptchaImageController.java
  20. 20 5
      alien-gateway/src/main/java/shop/alien/gateway/controller/LifeUserController.java
  21. 25 5
      alien-gateway/src/main/java/shop/alien/gateway/controller/StoreUserController.java
  22. 253 0
      alien-gateway/src/main/java/shop/alien/gateway/util/Base64.java
  23. 56 0
      alien-gateway/src/main/java/shop/alien/gateway/util/KaptchaTextCreator.java
  24. 35 13
      alien-store/src/main/java/shop/alien/store/controller/AliController.java
  25. 13 3
      alien-store/src/main/java/shop/alien/store/controller/LifeUserController.java
  26. 20 14
      alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java
  27. 72 91
      alien-store/src/main/java/shop/alien/store/controller/StoreUserController.java
  28. 35 13
      alien-store/src/main/java/shop/alien/store/service/LifeUserOrderService.java
  29. 9 2
      alien-store/src/main/java/shop/alien/store/service/impl/ActivityInviteConfigServiceImpl.java
  30. 15 12
      alien-store/src/main/java/shop/alien/store/service/impl/LifeCouponServiceImpl.java
  31. 149 87
      alien-store/src/main/java/shop/alien/store/service/impl/LifeUserViolationServiceImpl.java
  32. 6 0
      alien-store/src/main/java/shop/alien/store/service/impl/PlatformStoreCouponServiceImpl.java
  33. 45 43
      alien-store/src/main/java/shop/alien/store/service/impl/StoreIncomeDetailsRecordServiceImpl.java
  34. 93 127
      alien-store/src/main/java/shop/alien/store/service/impl/StoreUserServiceImpl.java
  35. 2 0
      alien-store/src/main/java/shop/alien/store/util/FunctionMagic.java
  36. 2 1
      alien-store/src/main/java/shop/alien/store/util/ali/AliApi.java
  37. 44 5
      alien-store/src/main/java/shop/alien/store/util/ali/AliSms.java
  38. 7 0
      pom.xml

+ 2 - 0
README.md

@@ -0,0 +1,2 @@
+## 测试发版记录
+2025-11-05

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

@@ -59,10 +59,10 @@ public class ActivitySignInConfig {
     @TableField("delete_flag")
     @TableLogic
     private Integer deleteFlag;
-
     @ApiModelProperty(value = "方案配置类型1,默认方案,2.特殊方案")
     private Integer planConfigType;
 
+
     @ApiModelProperty(value = "返回错误信息")
     @TableField(exist = false)
     private String errorMessage;

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

@@ -29,4 +29,10 @@ public class ActivityInviteLogVo extends ActivityInviteLog {
 
     @ApiModelProperty(value = "被邀请人昵称")
     private String invitedNickName;
+
+    @ApiModelProperty(value = "邀请人奖励信息")
+    private String inviteRewardInfo;
+
+    @ApiModelProperty(value = "被邀请人奖励信息")
+    private String invitedRewardInfo;
 }

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

@@ -30,4 +30,7 @@ public class LifeAppealManageVo extends LifeAppealManage {
     @ApiModelProperty(value = "业务状态名称")
     private String appealTypeName;
 
+    @ApiModelProperty(value = "用户电话")
+    private String userPhone;
+
 }

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

@@ -190,4 +190,7 @@ public class LifeUserOrderVo {
     @ApiModelProperty(value = "订单评价 0:未评价 1:已评价")
     @TableField("order_appraise")
     private Integer orderAppraise;
+    
+    @ApiModelProperty(value = "订单状态值")
+    private String orderStatusValue;
 }

+ 5 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LifeUserViolationVo.java

@@ -22,4 +22,9 @@ public class LifeUserViolationVo extends LifeUserViolation {
 
     //举报结果通知
     private String reportResultNotification;
+
+    
+    private String phone;
+
+    private String nickName;
 }

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

@@ -59,4 +59,7 @@ public class LifeUserVo extends LifeUser {
 
     @ApiModelProperty(value = "简介")
     private String blurb;
+
+    @ApiModelProperty(value = "验证码")
+    private String verificationCode;
 }

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

@@ -1,5 +1,6 @@
 package shop.alien.entity.store.vo;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import io.swagger.annotations.ApiModel;
@@ -71,5 +72,8 @@ public class StoreIncomeDetailsRecordVo extends StoreIncomeDetailsRecord {
     @ApiModelProperty(value = "退款金额")
     BigDecimal refundMoney;
 
+    @ApiModelProperty(value = "抽成比例")
+    private String commissionRate;
+
     List<StoreIncomeDetailsRecordVo> incomeDetailsRecordVoList;
 }

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

@@ -36,4 +36,7 @@ public class StoreUserVo extends StoreUser {
 
     @ApiModelProperty(value = "倒计时")
     private long countdown;
+
+    @ApiModelProperty(value = "手机号")
+    private String phone;
 }

+ 26 - 4
alien-entity/src/main/java/shop/alien/mapper/ActivityInviteLogMapper.java

@@ -16,9 +16,31 @@ import shop.alien.entity.store.vo.ActivityInviteLogVo;
 @Mapper
 public interface ActivityInviteLogMapper extends BaseMapper<ActivityInviteLog> {
 
-    @Select("select ail.*, lu.user_phone as invitePhone, lu1.user_phone as invitedPhone, lu1.user_image, lu1.user_name as invitedNickName  " +
-            "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 lu1 on lu1.id = ail.invited_user_id and lu1.delete_flag = 0 ${ew.customSqlSegment}")
+    @Select("SELECT " +
+            "    ail.*, " +
+            "    lu.user_phone AS invitePhone, " +
+            "    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 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);
 }

+ 16 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeCouponMapper.java

@@ -8,6 +8,7 @@ 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 org.apache.ibatis.annotations.Update;
 import shop.alien.entity.store.LifeCoupon;
 import shop.alien.entity.store.vo.LifeCouponVo;
 
@@ -64,4 +65,19 @@ public interface LifeCouponMapper extends BaseMapper<LifeCoupon> {
 
     @Select("SELECT * FROM life_coupon ${ew.customSqlSegment}")
     List<LifeCoupon> getList(@Param(Constants.WRAPPER) LambdaQueryWrapper<LifeCoupon> lambdaQueryWrapper);
+
+    @Update(" UPDATE life_coupon\n" +
+            "    SET \n" +
+            "        single_qty = single_qty - #{buyCount},\n" +
+            "        status = CASE WHEN (single_qty - #{buyCount}) = 0 \n" +
+            "                     THEN #{soldOutStatus} \n" +
+            "                     ELSE status END\n" +
+            "    WHERE \n" +
+            "        id = #{couponId} \n" +
+            "        AND single_qty >= #{buyCount}")
+    int deductInventoryAtomically(
+            @Param("couponId") Integer couponId,
+            @Param("buyCount") int buyCount,
+            @Param("soldOutStatus") int soldOutStatus
+    );
 }

+ 92 - 1
alien-entity/src/main/java/shop/alien/mapper/LifeUserViolationMapper.java

@@ -1,7 +1,15 @@
 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.Param;
+import org.apache.ibatis.annotations.Select;
 import shop.alien.entity.store.LifeUserViolation;
+import shop.alien.entity.store.vo.LifeUserViolationVo;
+
+import java.util.List;
 
 /**
  * <p>
@@ -12,5 +20,88 @@ import shop.alien.entity.store.LifeUserViolation;
  * @since 2025-04-29
  */
 public interface LifeUserViolationMapper extends BaseMapper<LifeUserViolation> {
-
+    
+    /**
+     * 分页查询用户举报信息
+     * 
+     * @param page 分页对象
+     * @param queryWrapper 查询条件包装器
+     * @return 分页结果
+     */
+    @Select("<script>" +
+            "WITH userInfo AS (" +
+            "    SELECT " +
+            "        su.phone, " +
+            "        su.id, " +
+            "        CASE su.delete_flag " +
+            "            WHEN 1 THEN CONCAT(su.nick_name, '(账号已注销)') " +
+            "            ELSE su.nick_name " +
+            "        END AS nick_name, " +
+            "        '1' AS type " +
+            "    FROM store_user su " +
+            "    UNION ALL " +
+            "    SELECT " +
+            "        lu.user_phone AS phone, " +
+            "        lu.id, " +
+            "        CASE lu.delete_flag " +
+            "            WHEN 1 THEN CONCAT(lu.user_name, '(账号已注销)') " +
+            "            ELSE lu.user_name " +
+            "        END AS nick_name, " +
+            "        '2' AS type " +
+            "    FROM life_user lu " +
+            ") " +
+            "SELECT " +
+            "    luv.*, " +
+            "    ui.nick_name AS nickname, " +
+            "    ui.phone " +
+            "FROM life_user_violation luv " +
+            "LEFT JOIN userInfo ui ON ui.type = luv.reporting_user_type " +
+            "    AND ui.id = luv.reporting_user_id " +
+            "    ${ew.customSqlSegment}" +
+            "</script>")
+    IPage<LifeUserViolationVo> getViolationPage(
+            IPage<LifeUserViolationVo> page, 
+            @Param(Constants.WRAPPER) QueryWrapper<LifeUserViolationVo> queryWrapper
+    );
+    
+    /**
+     * 查询用户举报信息列表
+     * 
+     * @param queryWrapper 查询条件包装器
+     * @return 举报信息列表
+     */
+    @Select("<script>" +
+            "WITH userInfo AS (" +
+            "    SELECT " +
+            "        su.phone, " +
+            "        su.id, " +
+            "        CASE su.delete_flag " +
+            "            WHEN 1 THEN CONCAT(su.nick_name, '(账号已注销)') " +
+            "            ELSE su.nick_name " +
+            "        END AS nick_name, " +
+            "        '1' AS type " +
+            "    FROM store_user su " +
+            "    UNION ALL " +
+            "    SELECT " +
+            "        lu.user_phone AS phone, " +
+            "        lu.id, " +
+            "        CASE lu.delete_flag " +
+            "            WHEN 1 THEN CONCAT(lu.user_name, '(账号已注销)') " +
+            "            ELSE lu.user_name " +
+            "        END AS nick_name, " +
+            "        '2' AS type " +
+            "    FROM life_user lu " +
+            ") " +
+            "SELECT " +
+            "    luv.*, " +
+            "    ui.nick_name AS nickname, " +
+            "    ui.phone " +
+            "FROM life_user_violation luv " +
+            "LEFT JOIN userInfo ui ON ui.type = luv.reporting_user_type " +
+            "    AND ui.id = luv.reporting_user_id " +
+            "    ${ew.customSqlSegment}" +
+            "</script>")
+    List<LifeUserViolationVo> getViolationList(
+            @Param(Constants.WRAPPER) QueryWrapper<LifeUserViolationVo> queryWrapper
+    );
 }

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

@@ -129,7 +129,43 @@ public interface StoreCommentMapper extends BaseMapper<StoreComment> {
             "AND store_id = #{storeId}")
     StoreInfoScoreVo getCommentCountAndScoreInfo(@Param("storeId") Integer storeId);
 
-
+/*
+    with total_coupon as (
+select lgbm.id,lgbm.group_type,lgbm.group_name,lgbm.image_id,2 coupon_type
+from life_group_buy_main lgbm
+union all
+select lc.id,0 group_type,lc.name group_name,0 image_id,1 coupon_type
+from life_coupon lc
+)
+SELECT
+	luo.*,
+	tc.group_type groupBuyType,
+	tc.group_name groupBuyName,
+	tc.image_id groupBuyImgId,
+	store.business_section storeType
+FROM
+	life_user_order luo
+LEFT JOIN order_coupon_middle ocm ON
+	luo.id = ocm.order_id
+LEFT JOIN total_coupon tc ON
+	ocm.coupon_id = tc.id
+	and tc.coupon_type = luo.coupon_type
+LEFT JOIN store_info store on
+	luo.store_id = store.id
+WHERE
+	luo.`status` in (2, 7)
+	AND luo.delete_flag = 0
+	AND luo.user_id = 142
+	AND luo.id NOT IN (
+	SELECT
+		business_id
+	FROM
+		store_comment
+	WHERE
+		business_type = 5
+		AND user_id = 142
+		AND delete_flag = 0)
+ */
     @Select("SELECT " +
             "luo.*,lgbm.group_type groupBuyType,lgbm.group_name groupBuyName,lgbm.image_id groupBuyImgId,store.business_section storeType " +
             "FROM life_user_order luo LEFT JOIN order_coupon_middle ocm ON luo.id = ocm.order_id LEFT JOIN life_group_buy_main lgbm ON ocm.coupon_id = lgbm.id LEFT JOIN store_info store on luo.store_id = store.id " +
@@ -137,7 +173,7 @@ public interface StoreCommentMapper extends BaseMapper<StoreComment> {
             "AND luo.coupon_type = 2 " +
             "AND luo.delete_flag = 0 " +
             "AND luo.user_id = #{userId} " +
-            "AND luo.id NOT IN (SELECT business_id FROM store_comment WHERE business_type = 5 AND user_id = #{userId} AND delete_flag = 0)")
+            "AND luo.id NOT IN (SELECT business_id FROM store_comment WHERE business_type = 5 AND user_id = #{userId})")
     IPage<LifeUserOrderCommentVo> getCommentOrderWPJPage(IPage<LifeUserOrderCommentVo> page, @Param("userId") String userId);
 
     @Select("SELECT " +

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

@@ -18,9 +18,11 @@
     <select id="getAppealManagement" resultType="shop.alien.entity.store.vo.LifeAppealManageVo">
         SELECT
         appeal.id,
+        appeal.store_id,
         store.store_name,
         su.`name` AS store_contact,
-        lu.user_phone AS store_phone,
+        lu.user_phone AS user_phone,
+        store.store_tel AS store_phone,
         `comment`.comment_content AS customer_report,
         appeal.appeal_reason,
         appeal.appeal_status AS appeal_type,

+ 13 - 1
alien-entity/src/main/resources/mapper/LifeUserOrderMapper.xml

@@ -30,6 +30,7 @@
         <result column="img_url" property="imgUrl"/>
         <result column="user_name" property="userName"/>
         <result column="order_status" property="status"/>
+        <result column="order_status_value" property="orderStatusValue"/>
         <result column="dist" property="dist"/>
         <result column="store_address" property="storeAddress"/>
         <result column="effective_date_type" property="effectiveDateType"/>
@@ -87,7 +88,18 @@
         ELSE '0.00'
         END as final_price,
         luo.order_str,
-        luo.pay_method,luo.buy_time,luo.cancel_time,luo.pay_time,luo.finish_time,luo.status order_status,luo.quan_id,luo.refund_time total_refund_time,luo.expert_order_id,
+        luo.pay_method,luo.buy_time,luo.cancel_time,luo.pay_time,luo.finish_time,luo.status order_status,
+        CASE luo.status
+        WHEN 0 THEN '待支付'
+        WHEN 1 THEN '已支付/待使用'
+        WHEN 2 THEN '已核销'
+        WHEN 3 THEN '已过期'
+        WHEN 4 THEN '已取消'
+        WHEN 5 THEN '已退款'
+        WHEN 6 THEN '退款失败'
+        WHEN 7 THEN '已完成'
+        END AS order_status_value,
+        luo.quan_id,luo.refund_time total_refund_time,luo.expert_order_id,
         tc.*,
         ldc.nominal_value,ldc.type,
         lu.user_phone,lu.user_name,lu.id user_id,

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

@@ -28,10 +28,11 @@
         ifnull(orders.consume_num, 0) consume_num,
         ifnull(orders.consume_price, 0) consume_price,
         IFNULL(comment.bad_num, 0) AS bad_num,
-        (
-        IFNULL((SELECT SUM(like_count) FROM store_comment WHERE user_id = user.id), 0) +
-        IFNULL((SELECT SUM(dianzan_count) FROM life_user_dynamics WHERE phone_id = user.user_phone), 0)
-        ) AS likesNumber,
+--         (
+--         IFNULL((SELECT SUM(like_count) FROM store_comment WHERE user_id = user.id), 0) +
+        IFNULL((SELECT SUM(dianzan_count) FROM life_user_dynamics WHERE phone_id = phoneId), 0)
+--         )
+            AS likesNumber,
         COUNT(lf.id) AS fans_count,
         lue.id as expert_id,
         lue.expert_code as expert_code

+ 7 - 0
alien-gateway/pom.xml

@@ -103,6 +103,13 @@
             <artifactId>fastjson</artifactId>
         </dependency>
 
+        <!-- 验证码 -->
+        <dependency>
+            <groupId>pro.fessional</groupId>
+            <artifactId>kaptcha</artifactId>
+        </dependency>
+
+
         <!--Swagger Start-->
         <dependency>
             <groupId>io.springfox</groupId>

+ 57 - 0
alien-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;
+    }
+
+}

+ 70 - 0
alien-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));
+    }
+
+}

+ 20 - 5
alien-gateway/src/main/java/shop/alien/gateway/controller/LifeUserController.java

@@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
 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.LifeUserService;
 
 /**
@@ -19,16 +20,30 @@ import shop.alien.gateway.service.LifeUserService;
 @RequiredArgsConstructor
 public class LifeUserController {
 
-    private final LifeUserService service;
+    private final LifeUserService lifeUserService;
+
+    private final BaseRedisService baseRedisService;
 
     @ApiOperation("用户登录")
     @ApiOperationSupport(order = 1)
-    @ApiImplicitParams({@ApiImplicitParam(name = "phoneNum", value = "手机号", dataType = "String", paramType = "query", required = true)})
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phoneNum", value = "手机号", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "code", value = "验证码", dataType = "String", paramType = "query", required = true)
+    })
     @GetMapping("/userLogin")
     public R<LifeUserVo> userLogin(@RequestParam("phoneNum") String phoneNum,
-                                    @RequestParam(value = "macIp",required = false) String macIp) {
-        log.info("LifeUserController.userLogin?phoneNum={},macIp={}", phoneNum, macIp);
-        LifeUserVo userVo = service.userLogin(phoneNum, macIp);
+                                   @RequestParam("code") String code,
+                                   @RequestParam(value = "macIp",required = false) String macIp) {
+        log.info("LifeUserController.userLogin?phoneNum={}&code={}", phoneNum, code);
+        // 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("验证码错误");
+        }
+        LifeUserVo userVo = lifeUserService.userLogin(phoneNum, macIp);
         if (null == userVo) {
             return R.fail("登录失败");
         }

+ 25 - 5
alien-gateway/src/main/java/shop/alien/gateway/controller/StoreUserController.java

@@ -8,6 +8,7 @@ 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.StoreUserMapper;
 import shop.alien.gateway.service.StoreUserService;
 
@@ -30,17 +31,36 @@ import java.util.Optional;
 public class StoreUserController {
 
     private final StoreUserService storeUserService;
+
     private final StoreUserMapper storeUserMapper;
 
+    private final BaseRedisService baseRedisService;
+
     @ApiOperation("门店用户登录")
     @ApiOperationSupport(order = 1)
-    @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true),
+    @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 = "isPassword", value = "是否密码登录", dataType = "Boolean", paramType = "query", required = true),
+            @ApiImplicitParam(name = "code", value = "验证码", dataType = "String", paramType = "query", required = true)
+    })
     @GetMapping("/login")
-    public R<StoreUserVo> login(String phone, String password,
-                                @RequestParam(defaultValue = "true", required = false) Boolean isPassword) {
-        log.info("StoreUserController.login?phone={}&password={}&isPassword={}", phone, password, isPassword);
+    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) {

+ 253 - 0
alien-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-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();
+    }
+}

+ 35 - 13
alien-store/src/main/java/shop/alien/store/controller/AliController.java

@@ -1,6 +1,7 @@
 package shop.alien.store.controller;
 
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -8,7 +9,10 @@ import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.StoreAliPayLog;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.service.AliService;
+import shop.alien.store.service.StoreUserService;
 import shop.alien.store.util.ali.AliApi;
 import shop.alien.store.util.ali.AliSms;
 import shop.alien.util.ali.AliOSSUtil;
@@ -19,8 +23,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.text.SimpleDateFormat;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.List;
 
 /**
  * @author ssk
@@ -45,8 +48,13 @@ public class AliController {
     private final AlipayTradeRefund alipayTradeRefund;
 
     private final AliOSSUtil aliOSSUtil;
+
     private final AliApi aliApi;
 
+    private final BaseRedisService baseRedisService;
+
+    private final StoreUserService storeUserService;
+
     @ApiOperation("阿里回调")
     @ApiOperationSupport(order = 1)
     @GetMapping("/notify")
@@ -62,12 +70,18 @@ public class AliController {
     @GetMapping("/getIdInfo")
     public R getIdInfo(String name, String idCard) {
         log.info("AliController.getIdInfo?name={}&idCard={}", name, idCard);
+
+        List<StoreUser> list = storeUserService.list(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getIdCard, idCard).eq(StoreUser::getName, name));
+        if (!list.isEmpty()) {
+            return R.fail("该身份证已实名认证过");
+        }
         if (aliPayConfig.getIdInfo(name, idCard)) {
             return R.success("身份验证成功");
         }
-        Map map = new HashMap();
-        map.put("msg","身份验证失败");
-        return R.data(map,"身份验证失败");
+//        Map map = new HashMap();
+//        map.put("msg","身份验证失败");
+//        return R.data(map,"身份验证失败");
+        return R.fail("身份证号与姓名不一致,请检查后重新填写");
     }
 
     @ApiOperation("单笔转账接口")
@@ -88,13 +102,21 @@ public class AliController {
 
     @ApiOperation("发送短信")
     @ApiOperationSupport(order = 4)
-    @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true)})
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "appType", value = "端区分(0:用户, 1:商家)", dataType = "Integer", paramType = "query", required = true, defaultValue = "0"),
+            @ApiImplicitParam(name = "businessType", value = "业务类型 (0:登录, 1:修改密码, 2:注册, 3:修改手机号, 4:注销店铺, 5:注销账号, 6:忘记密码)", dataType = "Integer", paramType = "query", required = true, defaultValue = "0")
+    })
     @GetMapping("/sendSms")
-    public R sendSms(String phone) {
-        Integer code = aliSmsConfig.sendSms(phone);
-        log.info("AliController.sendSms?phone={}&code={}", phone, code);
+    public R sendSms(
+            @RequestParam("phone") String phone,
+            @RequestParam("appType") Integer appType,
+            @RequestParam("businessType") Integer businessType
+    ) {
+        Integer code = aliSmsConfig.sendSms(phone, appType, businessType);
+        log.info("AliController.sendSms?phone={}&code={}&businessType={}", phone, code, businessType);
         if (code != null) {
-            return R.data(code);
+            return R.data("短信发送成功");
         }
         return R.fail("短信发送失败");
     }
@@ -224,9 +246,9 @@ public class AliController {
     })
     @GetMapping("/processRefund")
     public String processRefund(@RequestParam(value = "outTradeNo") String outTradeNo,
-                                       @RequestParam(value = "refundAmount") String refundAmount,
-                                       @RequestParam(value = "refundReason") String refundReason,
-                                       @RequestParam(value = "partialRefundCode") String partialRefundCode) {
+                                @RequestParam(value = "refundAmount") String refundAmount,
+                                @RequestParam(value = "refundReason") String refundReason,
+                                @RequestParam(value = "partialRefundCode") String partialRefundCode) {
         return aliApi.processRefund(outTradeNo, refundAmount, refundReason, partialRefundCode);
     }
 }

+ 13 - 3
alien-store/src/main/java/shop/alien/store/controller/LifeUserController.java

@@ -12,6 +12,7 @@ import shop.alien.entity.store.LifeFans;
 import shop.alien.entity.store.LifeUser;
 import shop.alien.entity.store.vo.LifeUserVo;
 import shop.alien.mapper.LifeUserMapper;
+import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.service.LifeUserService;
 import shop.alien.util.common.FileUpload;
 import shop.alien.util.common.RandomCreateUtil;
@@ -38,11 +39,12 @@ public class LifeUserController {
 
     private final LifeUserMapper lifeUserMapper;
 
+    private final BaseRedisService baseRedisService;
 
     @ApiOperation("用户登录")
     @ApiOperationSupport(order = 1)
     @ApiImplicitParams({@ApiImplicitParam(name = "phoneNum", 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(required = false) String inviteCode,
                                    @RequestParam(value = "macIp",required = false) String macIp) {
@@ -171,8 +173,16 @@ public class LifeUserController {
      */
     @ApiOperation("用户端注销用户")
     @PostMapping("/liftCancelAccount")
-    public R<Boolean> liftCancelAccount(@RequestBody LifeUser user) {
-        log.info("StoreUserController.liftCancelAccount?LifeUser={}", user);
+    public R<Boolean> liftCancelAccount(@RequestBody LifeUserVo user) {
+        log.info("StoreUserController.liftCancelAccount?LifeUserVo={}", user.toString());
+        // 2025-11-04 验证码-用户端注销账号
+        String cacheCode = baseRedisService.getString("verification_user_cancel_account_" + user.getUserPhone());
+        if (null == cacheCode) {
+            return R.fail("验证码过期或未发送");
+        }
+        if (!cacheCode.trim().equals(user.getVerificationCode().trim())) {
+            return R.fail("验证码错误");
+        }
         service.liftCancelAccount(user);
         return R.success("注销成功");
     }

+ 20 - 14
alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java

@@ -6,16 +6,15 @@ import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.ResponseEntity;
-import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartRequest;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.StoreInfoDto;
 import shop.alien.entity.store.vo.*;
-import shop.alien.mapper.StoreImgMapper;
 import shop.alien.mapper.TagsMainMapper;
 import shop.alien.mapper.WebAuditMapper;
+import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.service.StoreInfoService;
 
 import java.io.IOException;
@@ -40,12 +39,12 @@ public class StoreInfoController {
 
     private final StoreInfoService storeInfoService;
 
-    private final StoreImgMapper storeImgMapper;
-
     private final TagsMainMapper tagsMainMapper;
 
     private final WebAuditMapper webAuditMapper;
 
+    private final BaseRedisService baseRedisService;
+
     @ApiOperation("获取所有门店")
     @ApiOperationSupport(order = 1)
     @GetMapping("/getAll")
@@ -68,9 +67,9 @@ public class StoreInfoController {
     @PostMapping("/saveOrUpdate")
     public R<StoreInfo> saveOrUpdate(@RequestBody StoreInfoDto storeInfo) {
         log.info("StoreInfoController.saveOrUpdate?storeInfo={}", storeInfo);
-        try{
+        try {
             int num = storeInfoService.saveOrUpdateStoreInfo(storeInfo);
-            if (num>0) {
+            if (num > 0) {
                 return R.success("成功");
             }
             return R.fail("失败");
@@ -241,8 +240,7 @@ public class StoreInfoController {
             @RequestParam(required = false) String weidu,
             @RequestParam(required = false) String renewContractStatus,
             @RequestParam(required = false) String foodLicenceStatus,
-            @RequestParam(required = false) String foodLicenceWhetherExpiredStatus)
-    {
+            @RequestParam(required = false) String foodLicenceWhetherExpiredStatus) {
         log.info("StoreInfoController.getStoresPage?pageNum={},pageSize={},storeName={},storeContact={},id={},storePhone={},storeType={},expiredState={},storeApplicationStatus={},storeStatus={},businessSection={},jingdu={},weidu={},renewContractStatus={},foodLicenceStatus={},foodLicenceWhetherExpiredStatus={}",
                 pageNum, pageSize, storeName, storeContact, id, storePhone, storeType, expiredState, storeApplicationStatus, storeStatus, businessSection, jingdu, weidu, renewContractStatus, foodLicenceStatus, foodLicenceWhetherExpiredStatus);
         return R.data(storeInfoService.getStorePage(pageNum, pageSize, storeName, storeContact, id, storePhone, storeType, expiredState, storeApplicationStatus, storeStatus, businessSection, jingdu, weidu, renewContractStatus, foodLicenceStatus, foodLicenceWhetherExpiredStatus));
@@ -424,6 +422,14 @@ public class StoreInfoController {
     @PostMapping("/logoutStore")
     public R logoutStore(@RequestBody StoreInfoVo storeInfo) {
         log.info("StoreInfoController.logoutStore?storeInfo={}", storeInfo);
+        // 2025-11-04 验证码-商家端注销店铺
+        String cacheCode = baseRedisService.getString("verification_store_cancel_store_" + storeInfo.getStorePhone());
+        if (null == cacheCode) {
+            return R.fail("当验证码过期或未发送");
+        }
+        if (!cacheCode.trim().equals(storeInfo.getVerificationCode().trim())) {
+            return R.fail("验证码错误");
+        }
         try {
             storeInfoService.logoutStore(storeInfo);
         } catch (Exception e) {
@@ -699,8 +705,8 @@ public class StoreInfoController {
                 boolean flag = storeInfoService.updateById(storeInfo);
                 if (flag) {
                     //待审核状态变为已审核
-                    WebAudit webAudit = webAuditMapper.selectOne(new LambdaQueryWrapper<WebAudit>().eq(WebAudit::getStoreInfoId,storeInfo.getId()).eq(WebAudit::getDeleteFlag,0).eq(WebAudit::getType,"7"));
-                    if(webAudit != null){
+                    WebAudit webAudit = webAuditMapper.selectOne(new LambdaQueryWrapper<WebAudit>().eq(WebAudit::getStoreInfoId, storeInfo.getId()).eq(WebAudit::getDeleteFlag, 0).eq(WebAudit::getType, "7"));
+                    if (webAudit != null) {
                         webAudit.setStatus("1");
                         webAuditMapper.updateById(webAudit);
                     }
@@ -714,16 +720,16 @@ public class StoreInfoController {
             boolean flag = storeInfoService.updateById(storeInfo);
             if (flag) {
                 int num = storeInfoService.foodLicenceType(storeInfoDto.getId());
-                if(num > 0){
+                if (num > 0) {
                     //待审核状态变为已审核
-                    WebAudit webAudit = webAuditMapper.selectOne(new LambdaQueryWrapper<WebAudit>().eq(WebAudit::getStoreInfoId,storeInfo.getId()).eq(WebAudit::getDeleteFlag,0).eq(WebAudit::getType,"7"));
-                    if(webAudit != null){
+                    WebAudit webAudit = webAuditMapper.selectOne(new LambdaQueryWrapper<WebAudit>().eq(WebAudit::getStoreInfoId, storeInfo.getId()).eq(WebAudit::getDeleteFlag, 0).eq(WebAudit::getType, "7"));
+                    if (webAudit != null) {
                         webAudit.setStatus("1");
                         webAuditMapper.updateById(webAudit);
                     }
                 }
                 return R.success("审核通过成功");
-            }else{
+            } else {
                 return R.fail("审核失败");
             }
         }

+ 72 - 91
alien-store/src/main/java/shop/alien/store/controller/StoreUserController.java

@@ -1,7 +1,6 @@
 package shop.alien.store.controller;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import io.swagger.annotations.*;
@@ -15,13 +14,12 @@ import shop.alien.entity.store.vo.StoreInfoVo;
 import shop.alien.entity.store.vo.StoreUserVo;
 import shop.alien.mapper.StoreImgMapper;
 import shop.alien.mapper.StoreUserMapper;
+import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.service.StoreInfoService;
 import shop.alien.store.service.StoreUserService;
 
 import java.io.IOException;
 import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
 
 /**
  * 门店用户 前端控制器
@@ -39,31 +37,14 @@ import java.util.Optional;
 public class StoreUserController {
 
     private final StoreUserService storeUserService;
+
     private final StoreUserMapper storeUserMapper;
+
     private final StoreInfoService storeInfoService;
+
     private final StoreImgMapper storeImgMapper;
 
-    @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)})
-    @GetMapping("/login")
-    public R<StoreUserVo> login(String phone, String password,
-                                @RequestParam(defaultValue = "true", required = false) Boolean isPassword) {
-        log.info("StoreUserController.login?phone={}&password={}&isPassword={}", phone, password, isPassword);
-        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("手机号不存在"));
-    }
+    private final BaseRedisService baseRedisService;
 
     /**
      * 校验商户端账号是否禁用
@@ -76,46 +57,52 @@ public class StoreUserController {
     @ApiImplicitParams({@ApiImplicitParam(name = "accountId", value = "账号id", dataType = "Integer", paramType = "query", required = true)})
     @GetMapping("/checkAccount")
     private R<StoreUserVo> checkAccount(Integer accountId) {
-        return storeUserService.getById(accountId).getStatus() != 1 ? R.success("账号正常")
-                : 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("密码错误");
+        return storeUserService.getById(accountId).getStatus() != 1 ? R.success("账号正常") : R.fail("账号禁用");
     }
 
     @ApiOperation("修改密码/忘记密码/更换绑定手机号")
     @ApiOperationSupport(order = 2)
-    @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true),
-            @ApiImplicitParam(name = "newPhone", value = "新手机号", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "oldPassword", value = "旧密码", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "newPassword", value = "新密码", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "confirmNewPassword", value = "新密码确认", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "verificationCode", value = "验证码", dataType = "String", paramType = "query", required = true),
-            @ApiImplicitParam(name = "type", value = "类型:0:忘记密码,1:修改密码,2:更换绑定手机号", dataType = "Integer", paramType = "query", required = true)})
+    @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true), @ApiImplicitParam(name = "newPhone", value = "新手机号", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "oldPassword", value = "旧密码", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "newPassword", value = "新密码", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "confirmNewPassword", value = "新密码确认", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "verificationCode", value = "验证码", dataType = "String", paramType = "query", required = true), @ApiImplicitParam(name = "type", value = "类型:0:忘记密码,1:修改密码,2:更换绑定手机号", dataType = "Integer", paramType = "query", required = true)})
     @GetMapping("/updatePassword")
-    public R<String> updatePassword(String phone,String newPhone, String oldPassword, String newPassword, String confirmNewPassword,String verificationCode, Integer type) {
-        log.info("StoreUserController.updatePassword?phone={}&newPhone&oldPassword={}&newPassword={}&confirmNewPassword={}&verificationCode={}&type={}", phone, newPhone, oldPassword, newPassword, confirmNewPassword, verificationCode, type);
-        return  storeUserService.forgetOrModifyPassword(phone, newPhone, oldPassword, newPassword, confirmNewPassword, verificationCode, type);
+    public R<String> updatePassword(String phone, String newPhone, String oldPassword, String newPassword, String confirmNewPassword, String verificationCode, Integer type) {
+        log.info("StoreUserController.updatePassword?phone={}&newPhone={}&oldPassword={}&newPassword={}&confirmNewPassword={}&verificationCode={}&type={}", phone, newPhone, oldPassword, newPassword, confirmNewPassword, verificationCode, type);
+        // 2025-11-04 验证码-商家端修改密码/忘记密码/更换绑定手机号
+        String businessType = "";
+        switch (type) {
+            case 0:
+                businessType = "forget_password";
+                break;
+            case 1:
+                businessType = "modify_password";
+                break;
+            case 2:
+                businessType = "modify_phone";
+                break;
+        }
+        String cacheCode = baseRedisService.getString("verification_store_" + businessType + "_" + phone);
+        if (null == cacheCode) {
+            return R.fail("当验证码过期或未发送");
+        }
+        if (!cacheCode.trim().equals(verificationCode.trim())) {
+            return R.fail("验证码错误");
+        }
+        return storeUserService.forgetOrModifyPassword(phone, newPhone, oldPassword, newPassword, confirmNewPassword, verificationCode, type);
     }
 
     @ApiOperation("更换绑定手机号效验原密码或验证码")
     @ApiOperationSupport(order = 2)
-    @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true),
-            @ApiImplicitParam(name = "oldPassword", value = "旧密码", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "verificationCode", value = "验证码", dataType = "String", paramType = "query")})
+    @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true), @ApiImplicitParam(name = "oldPassword", value = "旧密码", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "verificationCode", value = "验证码", dataType = "String", paramType = "query")})
     @GetMapping("/changePhoneVerification")
     public R<Map<String, String>> changePhoneVerification(String phone, String oldPassword, String verificationCode) {
-        log.info("StoreUserController.changePhoneVerification?phone={}&oldPassword={}&verificationCode={}", phone,  oldPassword,  verificationCode);
+        log.info("StoreUserController.changePhoneVerification?phone={}&oldPassword={}&verificationCode={}", phone, oldPassword, verificationCode);
+        // 2025-11-04 验证码-商家端更换绑定手机号效验原密码或验证码
+        String cacheCode = baseRedisService.getString("verification_store_modify_phone_" + phone);
+        if (null == cacheCode) {
+            return R.fail("当验证码过期或未发送");
+        }
+        if (!cacheCode.trim().equals(verificationCode.trim())) {
+            return R.fail("验证码错误");
+        }
         try {
             return R.data(storeUserService.changePhoneVerification(phone, oldPassword, verificationCode));
         } catch (Exception e) {
@@ -160,10 +147,7 @@ public class StoreUserController {
 
     @ApiOperation("修改用户状态")
     @ApiOperationSupport(order = 6)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true),
-            @ApiImplicitParam(name = "type", value = "修改类型 (pass:密码状态改为1, face:人脸状态改为2)", dataType = "String", paramType = "query", required = true)
-    })
+    @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true), @ApiImplicitParam(name = "type", value = "修改类型 (pass:密码状态改为1, face:人脸状态改为2)", dataType = "String", paramType = "query", required = true)})
     @GetMapping("/updateUserStatus")
     public R<StoreUser> updateUserStatus(String phone, String type) {
         log.info("StoreUserController.updateUserStatus?phone={}&type={}", phone, type);
@@ -180,9 +164,7 @@ public class StoreUserController {
         log.info("StoreUserController.setStoreUserInfo?storeInfo={}", storeInfo);
 
         // 查询店铺图片信息
-        LambdaQueryWrapper<StoreImg> queryWrapper = new LambdaQueryWrapper<StoreImg>()
-                .eq(StoreImg::getImgType, 10)
-                .eq(StoreImg::getStoreId, storeInfo.getId());
+        LambdaQueryWrapper<StoreImg> queryWrapper = new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getImgType, 10).eq(StoreImg::getStoreId, storeInfo.getId());
         StoreImg storeImg = storeImgMapper.selectOne(queryWrapper);
 
         // 如果查询到店铺图片信息,则更新图片 URL
@@ -224,19 +206,8 @@ public class StoreUserController {
     @ApiOperation("web端查询用户列表")
     @ApiOperationSupport(order = 7)
     @GetMapping("/getStoreUserList")
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "pageNum", value = "页数", dataType = "int", paramType = "query", required = true),
-            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
-            @ApiImplicitParam(name = "storeName", value = "门店名称", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "storeContact", value = "门店联系人", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "storePhone", value = "门店电话", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "storeType", value = "门店类型", dataType = "String", paramType = "query")
-    })
-    public R<IPage<StoreUserVo>> getStoreUserList(@RequestParam(defaultValue = "1") int pageNum,
-                                                  @RequestParam(defaultValue = "10") int pageSize,
-                                                  @RequestParam(required = false) String id,
-                                                  @RequestParam(required = false) String phone,
-                                                  @RequestParam(required = false) Integer status) {
+    @ApiImplicitParams({@ApiImplicitParam(name = "pageNum", value = "页数", dataType = "int", paramType = "query", required = true), @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true), @ApiImplicitParam(name = "storeName", value = "门店名称", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "storeContact", value = "门店联系人", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "storePhone", value = "门店电话", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "storeType", value = "门店类型", dataType = "String", paramType = "query")})
+    public R<IPage<StoreUserVo>> getStoreUserList(@RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "10") int pageSize, @RequestParam(required = false) String id, @RequestParam(required = false) String phone, @RequestParam(required = false) Integer status) {
         log.info("StoreInfoController.getStoreUserList?pageNum={},pageSize={},id={},phone={},status={}", pageNum, pageSize, id, phone, status);
         R<IPage<StoreUserVo>> storeUserVoR = storeUserService.getStoreUserList(pageNum, pageSize, id, phone, status);
         return storeUserVoR;
@@ -304,15 +275,9 @@ public class StoreUserController {
     @ApiOperation(value = "web端导出商家端账号相关信息")
     @ApiOperationSupport(order = 6)
     @GetMapping("/exportExcel")
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "id", value = "用户id", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "phone", value = "联系电话", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "status", value = "状态", dataType = "String", paramType = "query"),
-    })
+    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "用户id", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "phone", value = "联系电话", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "status", value = "状态", dataType = "String", paramType = "query"),})
     @ResponseBody
-    public R exportExcel(@RequestParam(value = "id", required = false) String id
-            , @RequestParam(value = "phone", required = false) String phone
-            , @RequestParam(value = "status", required = false) String status) throws IOException {
+    public R exportExcel(@RequestParam(value = "id", required = false) String id, @RequestParam(value = "phone", required = false) String phone, @RequestParam(value = "status", required = false) String status) throws IOException {
         log.info("StoreInfoController.exportExcel");
         String excelPath = storeUserService.exportExcel(id, phone, status);
         return R.data(excelPath);
@@ -328,10 +293,18 @@ public class StoreUserController {
      */
     @ApiOperation("门店用户注册")
     @ApiOperationSupport(order = 7)
-    @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true),
-            @ApiImplicitParam(name = "password", value = "密码", dataType = "String", paramType = "query", required = true)})
+    @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true), @ApiImplicitParam(name = "password", value = "密码", dataType = "String", paramType = "query", required = true), @ApiImplicitParam(name = "code", value = "验证码", dataType = "String", paramType = "query", required = true)})
     @GetMapping("/register")
-    public R<Boolean> register(String phone, String password) {
+    public R<Boolean> register(@RequestParam("phone") String phone, @RequestParam("password") String password, @RequestParam("code") String code) {
+        log.info("StoreUserController.register?phone={}&password={}&code={}", phone, password, code);
+        // 2025-11-04 验证码-商家端注册
+        String cacheCode = baseRedisService.getString("verification_store_register_" + phone);
+        if (null == cacheCode) {
+            return R.fail("当验证码过期或未发送");
+        }
+        if (!cacheCode.trim().equals(code.trim())) {
+            return R.fail("验证码错误");
+        }
         log.info("StoreUserController.register?phone={}&password={}", phone, password);
         return storeUserService.register(phone, password);
     }
@@ -376,7 +349,7 @@ public class StoreUserController {
     @PostMapping("/storeCancelAccountVerification")
     public R<Map<String, String>> storeCancelAccountVerification(@RequestBody StoreUserVo storeUserVo) {
         log.info("StoreUserController.storeCancelAccountVerification?storeUserVo={}", storeUserVo);
-             return R.data(storeUserService.storeCancelAccountVerification(storeUserVo));
+        return R.data(storeUserService.storeCancelAccountVerification(storeUserVo));
     }
 
     /**
@@ -386,6 +359,14 @@ public class StoreUserController {
     @PostMapping("/storeCancelAccount")
     public R<StoreUserVo> storeCancelAccount(@RequestBody StoreUserVo storeUserVo) {
         log.info("StoreUserController.storeCancelAccount?storeUserVo={}", storeUserVo);
+        // 2025-11-04 验证码-商家端注册
+        String cacheCode = baseRedisService.getString("verification_store_cancel_account_" + storeUserVo.getPhone());
+        if (null == cacheCode) {
+            return R.fail("当验证码过期或未发送");
+        }
+        if (!cacheCode.trim().equals(storeUserVo.getVerificationCode().trim())) {
+            return R.fail("验证码错误");
+        }
         try {
             storeUserService.storeCancelAccount(storeUserVo);
         } catch (Exception e) {
@@ -421,9 +402,9 @@ public class StoreUserController {
      */
     @ApiOperation("是否有支付密码")
     @GetMapping("/havePayPassword")
-    public R<Boolean> havePayPassword(@RequestParam String storeUserId,@RequestParam(required = false) String password){
-        log.info("StoreUserController.havePayPassword?storeUserId={},password={}",storeUserId,password);
-        return storeUserService.havePayPassword(storeUserId,password);
+    public R<Boolean> havePayPassword(@RequestParam String storeUserId, @RequestParam(required = false) String password) {
+        log.info("StoreUserController.havePayPassword?storeUserId={},password={}", storeUserId, password);
+        return storeUserService.havePayPassword(storeUserId, password);
     }
 
     /**
@@ -434,8 +415,8 @@ public class StoreUserController {
     public R<Integer> addAlipayAccount(@RequestBody StoreUserVo storeUserVo) {
         log.info("StoreUserController.addAlipayAccount?storeUserVo={}", storeUserVo);
         LambdaUpdateWrapper<StoreUser> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
-        lambdaUpdateWrapper.set(StoreUser :: getAlipayAccount, storeUserVo.getAlipayAccount());
-        lambdaUpdateWrapper.eq(StoreUser :: getId, storeUserVo.getId());
-        return R.data(storeUserMapper.update(null,lambdaUpdateWrapper));
+        lambdaUpdateWrapper.set(StoreUser::getAlipayAccount, storeUserVo.getAlipayAccount());
+        lambdaUpdateWrapper.eq(StoreUser::getId, storeUserVo.getId());
+        return R.data(storeUserMapper.update(null, lambdaUpdateWrapper));
     }
 }

+ 35 - 13
alien-store/src/main/java/shop/alien/store/service/LifeUserOrderService.java

@@ -14,7 +14,6 @@ import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import shop.alien.entity.result.BusinessException;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.LifeUserOrderDto;
@@ -453,7 +452,7 @@ public class LifeUserOrderService extends ServiceImpl<LifeUserOrderMapper, LifeU
 
         // 更新的中间表id
         List<Integer> updateIds = orderCouponMiddles1.stream().limit(refundCouponAmount).map(x -> x.getId()).collect(Collectors.toList());
-        if (result.equals("调用失败")) {
+        if (!result.equals("调用成功")) {
             refundMessage = "编号为" + order.getOrderNo() + "的订单退款失败,请重新发起申请。";
             // TODO 退款失败目前不做处理
             /*orderCouponMiddleMapper.update(null,new UpdateWrapper<OrderCouponMiddle>().in("id",updateIds)
@@ -562,6 +561,14 @@ public class LifeUserOrderService extends ServiceImpl<LifeUserOrderMapper, LifeU
                 lifeGroupBuyMainMapper.update(null, new LambdaUpdateWrapper<LifeGroupBuyMain>()
                         .setSql("inventory_num=inventory_num+" + refundCouponAmount)
                         .eq(LifeGroupBuyMain::getId, couponId));
+
+                //判断当前时间在开始时间start_date和结束时间end_date之间,并且库存single_qty大于0,则修改状态status为5
+                lifeGroupBuyMainMapper.update(null, new LambdaUpdateWrapper<LifeGroupBuyMain>()
+                        .set(LifeGroupBuyMain::getStatus, 5)
+                        .eq(LifeGroupBuyMain::getId, couponId)
+                        .le(LifeGroupBuyMain::getStartTimeValue, LocalDateTime.now())
+                        .ge(LifeGroupBuyMain::getEndTime, LocalDateTime.now())
+                        .gt(LifeGroupBuyMain::getInventoryNum, 0));
             }
         } catch (Exception e) {
             log.error("LifeUserOrderService,恢复团购券/代金券数量报错={}", e);
@@ -774,20 +781,31 @@ public class LifeUserOrderService extends ServiceImpl<LifeUserOrderMapper, LifeU
             orderCouponMiddleService.save(orderCouponMiddle);
         }
         //4. 代金券/团购库存扣除 coupon_type 1 代金券 2团购
-        int successful = 0;
+        Map<String, Object> returnMap = new HashMap<>();
+        int soldOutStatus = CouponStatusEnum.SOLD_OUT.getCode();
+        int updateRows = 0;
+
         if (lifeUserOrderDto.getCouponType() == 2) {
-            // 团购库存扣除
-            successful = lifeGroupBuyMainMapper.update(null, new UpdateWrapper<LifeGroupBuyMain>()
-                    .eq("id", lifeUserOrderDto.getCouponId()).ge("inventory_num", lifeUserOrderDto.getCount()).setSql("inventory_num = inventory_num - " + lifeUserOrderDto.getCount()));
+            // 团购库存:原子扣减
+            updateRows = lifeGroupBuyMainMapper.deductInventoryAtomically(
+                    lifeUserOrderDto.getCouponId(),
+                    buyCount,
+                    soldOutStatus
+            );
         } else {
-            successful = lifeCouponMapper.update(null, new UpdateWrapper<LifeCoupon>()
-                    .eq("id", lifeUserOrderDto.getCouponId()).ge("single_qty", lifeUserOrderDto.getCount()).setSql("single_qty = single_qty - " + lifeUserOrderDto.getCount()));
+            // 代金券库存:原子扣减
+            updateRows = lifeCouponMapper.deductInventoryAtomically(
+                    lifeUserOrderDto.getCouponId(),
+                    buyCount,
+                    soldOutStatus
+            );
         }
-        if (successful == 0) {
-            log.error("库存不足");
-            throw new BusinessException("库存不足");
+// 判断库存扣减结果
+        if (updateRows == 0) {
+            log.error("couponid:"+lifeUserOrderDto.getCouponId()+" 库存不足,当前购买数量:"+ buyCount);
+            // 手动抛出异常,触发事务回滚(回滚之前创建的订单和优惠券状态变更)
+            throw new RuntimeException("库存不足,下单失败");
         }
-        Map<String, Object> returnMap = new HashMap<>();
         returnMap.put("success", "下单成功");
         returnMap.put("orderNo", lifeUserOrderDto.getOrderNo());
         returnMap.put("lifeUserOrder", lifeUserOrder);
@@ -1125,7 +1143,11 @@ public class LifeUserOrderService extends ServiceImpl<LifeUserOrderMapper, LifeU
         // 状态(0草稿/1待审核/2未开始/3审核拒绝/4已售罄/5进行中/6已下架/7已结束/8=2+手动下架)
         if (status != CouponStatusEnum.ONGOING.getCode()) {
             returnMap.put("success", false);
-            returnMap.put("reason", "团购/代金券未开始");
+            if( status == CouponStatusEnum.SOLD_OUT.getCode()){
+                returnMap.put("reason", "团购/代金券已售罄");
+            }else{
+                returnMap.put("reason", "团购/代金券未开始");
+            }
             return returnMap;
         }
         // 限购

+ 9 - 2
alien-store/src/main/java/shop/alien/store/service/impl/ActivityInviteConfigServiceImpl.java

@@ -24,6 +24,8 @@ import shop.alien.util.common.RandomCreateUtil;
 
 import java.time.Instant;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.Date;
 import java.util.List;
 
@@ -250,8 +252,13 @@ public class ActivityInviteConfigServiceImpl extends ServiceImpl<ActivityInviteC
                        LambdaQueryWrapper<ActivityInviteLog> activityInviteLogLambdaQueryWrapper = new LambdaQueryWrapper<>();
                        activityInviteLogLambdaQueryWrapper.eq(ActivityInviteLog::getInviteUserId, inviteUserId);
                        activityInviteLogLambdaQueryWrapper.eq(ActivityInviteLog::getDeleteFlag, 0);
-                       String today = LocalDate.now().toString();
-                       activityInviteLogLambdaQueryWrapper.between(ActivityInviteLog::getInviteTime, today + " 00:00:00", today + " 23:59:59");
+                       // 使用 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 "绑定数量已超过当天最大绑定数量";

+ 15 - 12
alien-store/src/main/java/shop/alien/store/service/impl/LifeCouponServiceImpl.java

@@ -135,20 +135,8 @@ public class LifeCouponServiceImpl extends ServiceImpl<LifeCouponMapper, LifeCou
         wrapper.like(name != null && !name.isEmpty(), LifeCoupon::getName, name);
         wrapper.eq(LifeCoupon::getType, 1);
         wrapper.eq(LifeCoupon::getDataType, dataType);
-//        修改排序代金券排序状态,按照状态和时间排序
-//        wrapper.orderByDesc(LifeCoupon::getCreatedTime);
         wrapper.last("ORDER BY CASE WHEN status = 5 THEN 0 ELSE 1 END ASC , created_time DESC");
         IPage<LifeCoupon> lifeCouponIPage = new Page<>(page, size);
-        //如果singleQty 库存的数量<=0,则修改status状态为已售罄 状态为4
-        LifeCoupon lifeCouponNew = new LifeCoupon();
-        lifeCouponNew.setStoreId(storeId);
-        lifeCouponNew.setStatus(4);
-        lifeCouponMapper.update(lifeCouponNew, new LambdaUpdateWrapper<LifeCoupon>().eq(LifeCoupon::getSingleQty, 0));
-        //如果当前时间小于开始时间,则修改status状态为未开始 状态为2
-//        LifeCoupon lifeCouponNew1 = new LifeCoupon();
-//        lifeCouponNew1.setStoreId(storeId);
-//        lifeCouponNew1.setStatus(2);
-//        lifeCouponMapper.update(lifeCouponNew1, new LambdaUpdateWrapper<LifeCoupon>().gt(LifeCoupon::getStartDate, LocalDateTime.now()));
         return lifeCouponMapper.selectPage(lifeCouponIPage, wrapper);
     }
 
@@ -660,6 +648,21 @@ public class LifeCouponServiceImpl extends ServiceImpl<LifeCouponMapper, LifeCou
                 return R.fail("该劵不在有效期内");
             }
         }
+
+        // 获取当天开始时间(00:00:00)
+        LocalDateTime todayStart = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
+        // 获取当天结束时间(23:59:59.999)
+        LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
+        if(!lifeCoupon.getSingleCanUse().equals("0")){
+            Integer orderNum = orderCouponMiddleMapper.selectCount(new LambdaQueryWrapper<OrderCouponMiddle>()
+                    .eq(OrderCouponMiddle :: getOrderId, orderCouponMiddle.getOrderId())
+                    .between(OrderCouponMiddle :: getUsedTime,todayStart,todayEnd)
+                    .eq(OrderCouponMiddle ::getStatus, 2)
+            );
+            if(orderNum >= Integer.parseInt(lifeCoupon.getSingleCanUse())){
+                return R.fail("该订单已经超过今日单次核销数量");
+            }
+        }
         return R.success("效验通过");
     }
 

+ 149 - 87
alien-store/src/main/java/shop/alien/store/service/impl/LifeUserViolationServiceImpl.java

@@ -14,6 +14,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
 import shop.alien.entity.second.SecondGoodsRecord;
 import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.LifeUserViolationDto;
@@ -310,43 +311,43 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
     @Override
     public IPage<LifeUserViolationDto> getViolationPage(int page, int size, String nickName, String phone, String processingStatus) {
-        List<String> MIDs = storeUserService.getIds(nickName, phone);
-        List<String> UIDs = lifeUserService.getIds(nickName, phone);
-        boolean flag = FunctionMagic.isListFlag(MIDs, UIDs);
-        IPage<LifeUserViolation> iPage = new Page<>(page, size);
-        QueryWrapper<LifeUserViolation> queryWrapper = new QueryWrapper<>();
-        queryWrapper.eq(StringUtils.isNotEmpty(processingStatus), PROCESSING_STATUS, processingStatus).and(flag, wrapper -> wrapper.nested(wq -> wq.eq(REPORTING_USER_TYPE, "1").in(!MIDs.isEmpty(), REPORTING_USER_ID, MIDs)).or(wq -> wq.eq(REPORTING_USER_TYPE, "2").in(!UIDs.isEmpty(), REPORTING_USER_ID, UIDs))).orderByDesc("updated_time");
-        String commonReportContextType = "1,2,3";
-        List<String> commonReportContextTypeList = Arrays.asList(commonReportContextType.split(","));
-        queryWrapper.lambda().in(LifeUserViolation::getReportContextType, commonReportContextTypeList);
-        IPage<LifeUserViolation> resultPage = lifeUserViolationMapper.selectPage(iPage, queryWrapper);
+        IPage<LifeUserViolationVo> pageRequest = new Page<>(page, size);
+        QueryWrapper<LifeUserViolationVo> queryWrapper = new QueryWrapper<>();
+        
+        // 基础查询条件
+        queryWrapper.eq("luv.delete_flag", 0)
+                .in("luv.report_context_type", Arrays.asList("1", "2", "3"));
+        
+        // 动态查询条件
+        queryWrapper.like(StringUtils.isNotEmpty(nickName), "ui.nick_name", nickName)
+                .like(StringUtils.isNotEmpty(phone), "ui.phone", phone);
+        
+        if (StringUtils.isNotEmpty(processingStatus)) {
+            queryWrapper.eq("luv.processing_status", processingStatus);
+        }
+        
+        queryWrapper.orderByDesc("luv.updated_time");
+        
+        IPage<LifeUserViolationVo> resultPage = lifeUserViolationMapper.getViolationPage(pageRequest, queryWrapper);
+
         return resultPage.convert(e -> {
             LifeUserViolationDto dto = new LifeUserViolationDto();
             BeanUtils.copyProperties(e, dto);
+            
+            // 处理举报凭证图片
             if (Objects.nonNull(e.getReportEvidenceImg())) {
-                List<String> list = Arrays.stream(e.getReportEvidenceImg().split(",")).map(String::trim).collect(Collectors.toList());
-                dto.setImage(list.get(0));
-                dto.setImageList(list);
-            }
-            if (e.getReportingUserType().equals("1")) {
-                // M
-                StoreUser storeUser = storeUserService.getOne(new QueryWrapper<StoreUser>().eq("id", e.getReportingUserId()));
-                if (storeUser == null) {
-                    storeUser = storeUserMapper.getRemoveUser(e.getReportingUserId());
-                    dto.setNickname(Objects.isNull(storeUser.getName()) ? "" : storeUser.getName());
-                } else {
-                    dto.setNickname(Objects.isNull(storeUser.getNickName()) ? "" : storeUser.getNickName());
-                }
-                dto.setPhone(Objects.isNull(storeUser.getPhone()) ? "" : storeUser.getPhone());
-            } else {
-                // U
-                LifeUser lifeUser = lifeUserService.getOne(new QueryWrapper<LifeUser>().eq("id", e.getReportingUserId()));
-                if (lifeUser == null) {
-                    lifeUser = lifeUserMapper.getRemoveUser(e.getReportingUserId());
+                List<String> imageList = Arrays.stream(e.getReportEvidenceImg().split(","))
+                        .map(String::trim)
+                        .filter(StringUtils::isNotEmpty)
+                        .collect(Collectors.toList());
+                
+                if (!imageList.isEmpty()) {
+                    dto.setImage(imageList.get(0));
+                    dto.setImageList(imageList);
                 }
-                dto.setNickname(Objects.isNull(lifeUser.getUserName()) ? "" : lifeUser.getUserName());
-                dto.setPhone(Objects.isNull(lifeUser.getUserPhone()) ? "" : lifeUser.getUserPhone());
             }
+            
+            dto.setNickname(e.getNickName());
             return dto;
         });
     }
@@ -562,66 +563,127 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
     @Override
     public String exportExcel(String nickName, String phone, String processingStatus) throws IOException {
-        // 定义格式化模式
-        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-        List<String> MIDs = storeUserService.getIds(nickName, phone);
-        List<String> UIDs = lifeUserService.getIds(nickName, phone);
-        boolean flag = FunctionMagic.isListFlag(MIDs, UIDs);
-        QueryWrapper<LifeUserViolation> queryWrapper = new QueryWrapper<>();
-        queryWrapper.eq(StringUtils.isNotEmpty(processingStatus), PROCESSING_STATUS, processingStatus).and(flag, wrapper -> wrapper.nested(wq -> wq.eq(REPORTING_USER_TYPE, "1").in(!MIDs.isEmpty(), REPORTING_USER_ID, MIDs)).or(wq -> wq.eq(REPORTING_USER_TYPE, "2").in(!UIDs.isEmpty(), REPORTING_USER_ID, UIDs))).orderByDesc("updated_time");
-        List<LifeUserViolation> resultPage = lifeUserViolationMapper.selectList(queryWrapper);
-        List<LifeUserViolationExcelVO> res = Lists.newArrayList();
+        QueryWrapper<LifeUserViolationVo> queryWrapper = new QueryWrapper<>();
+
+        // 基础查询条件(与 getViolationPage 保持一致)
+        queryWrapper.eq("luv.delete_flag", 0)
+                .in("luv.report_context_type", Arrays.asList("1", "2", "3"));
+
+        // 动态查询条件
+        queryWrapper.like(StringUtils.isNotEmpty(nickName), "ui.nick_name", nickName)
+                .like(StringUtils.isNotEmpty(phone), "ui.phone", phone);
+        if (StringUtils.isNotEmpty(processingStatus)) {
+            queryWrapper.eq("luv.processing_status", processingStatus);
+        }
+        queryWrapper.orderByDesc("luv.updated_time");
+
+        List<LifeUserViolationVo> violationList = lifeUserViolationMapper.getViolationList(queryWrapper);
+        
+        // 如果查询结果为空,返回空列表生成的Excel
+        if (CollectionUtils.isEmpty(violationList)) {
+            log.warn("导出Excel时查询结果为空,nickName={}, phone={}, processingStatus={}", nickName, phone, processingStatus);
+        }
+
+        // 日期格式化器(复用)
+        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        
+        // 使用Stream API进行转换,提高性能和可读性
         AtomicInteger serialNumber = new AtomicInteger(1);
-        resultPage.forEach(e -> {
-            LifeUserViolationExcelVO dto = new LifeUserViolationExcelVO();
-            BeanUtils.copyProperties(e, dto);
-            dto.setId(serialNumber.getAndIncrement());
-            // 图片
-            if (Objects.nonNull(e.getReportEvidenceImg())) {
-                String firstPart = e.getReportEvidenceImg().split(",")[0];
-                dto.setReportEvidenceImg(firstPart);
+        List<LifeUserViolationExcelVO> excelDataList = violationList.stream()
+                .map(vo -> convertToExcelVO(vo, serialNumber.getAndIncrement(), dateTimeFormatter))
+                .collect(Collectors.toList());
+
+        // 生成Excel文件
+        String fileName = UUID.randomUUID().toString().replace("-", "");
+        String filePath = ExcelGenerator.generateExcel(
+                excelPath + excelGeneratePath + fileName + ".xlsx", 
+                excelDataList, 
+                LifeUserViolationExcelVO.class);
+        
+        // 上传到OSS并返回URL
+        return aliOSSUtil.uploadFile(new File(filePath), "excel/" + fileName + ".xlsx");
+    }
+
+    /**
+     * 将 LifeUserViolationVo 转换为 LifeUserViolationExcelVO
+     *
+     * @param vo 源对象
+     * @param serialNumber 序号
+     * @param formatter 日期格式化器
+     * @return Excel VO对象
+     */
+    private LifeUserViolationExcelVO convertToExcelVO(LifeUserViolationVo vo, int serialNumber, DateTimeFormatter formatter) {
+        LifeUserViolationExcelVO excelVO = new LifeUserViolationExcelVO();
+        BeanUtils.copyProperties(vo, excelVO);
+        
+        excelVO.setId(serialNumber);
+        excelVO.setNickname(vo.getNickName());
+        
+        // 处理举报凭证图片(安全分割,避免数组越界)
+        if (StringUtils.isNotEmpty(vo.getReportEvidenceImg())) {
+            String[] imageParts = vo.getReportEvidenceImg().split(",");
+            if (imageParts.length > 0 && StringUtils.isNotEmpty(imageParts[0])) {
+                excelVO.setReportEvidenceImg(imageParts[0].trim());
+            }
+        }
+        
+        // 处理举报内容类型
+        if (StringUtils.isNotEmpty(vo.getReportContextType())) {
+            try {
+                excelVO.setReportContextType(FunctionMagic.getContext(vo.getReportContextType()));
+            } catch (Exception e) {
+                log.warn("转换举报内容类型失败,reportContextType={}, error={}", vo.getReportContextType(), e.getMessage());
+                excelVO.setReportContextType(vo.getReportContextType());
             }
-            // 举报内容
-            dto.setReportContextType(FunctionMagic.getContext(e.getReportContextType()));
-            // 举报理由
-            dto.setViolationType(FunctionMagic.violation(e.getViolationType()));
-            // 状态
-            dto.setProcessingStatus(FunctionMagic.status(e.getProcessingStatus()));
-            // 时间
-            Instant instant = e.getCreatedTime().toInstant();
-            String formattedTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime().format(formatter);
-            dto.setCreatedTime(formattedTime);
-            if (Objects.nonNull(e.getProcessingTime())) {
-                Instant processing = e.getProcessingTime().toInstant();
-                String processingTime = processing.atZone(ZoneId.systemDefault()).toLocalDateTime().format(formatter);
-                dto.setProcessingTime(processingTime);
+        }
+        
+        // 处理举报理由
+        if (StringUtils.isNotEmpty(vo.getViolationType())) {
+            try {
+                excelVO.setViolationType(FunctionMagic.violation(vo.getViolationType()));
+            } catch (Exception e) {
+                log.warn("转换举报理由失败,violationType={}, error={}", vo.getViolationType(), e.getMessage());
+                excelVO.setViolationType(vo.getViolationType());
             }
-            if (e.getReportingUserType().equals("1")) {
-                // M
-                // 举报人
-                StoreUser storeUser = storeUserService.getOne(new QueryWrapper<StoreUser>().eq("id", e.getReportingUserId()));
-                dto.setNickname(storeUser.getName());
-                dto.setPhone(storeUser.getPhone());
-                // 被举报人
-                StoreUser reportedUser = storeUserService.getOne(new QueryWrapper<StoreUser>().eq("id", e.getReportedUserId()));
-                if (Objects.nonNull(reportedUser)) {
-                    dto.setReportedAcc(reportedUser.getPhone());
-                }
-            } else {
-                // U
-                LifeUser lifeUser = lifeUserService.getOne(new QueryWrapper<LifeUser>().eq("id", e.getReportingUserId()));
-                dto.setNickname(lifeUser.getUserName());
-                dto.setPhone(lifeUser.getUserPhone());
-                LifeUser reportedUser = lifeUserService.getOne(new QueryWrapper<LifeUser>().eq("id", e.getReportedUserId()));
-                if (Objects.nonNull(reportedUser)) {
-                    dto.setReportedAcc(reportedUser.getUserPhone());
-                }
+        }
+        
+        // 处理处理状态
+        if (StringUtils.isNotEmpty(vo.getProcessingStatus())) {
+            try {
+                excelVO.setProcessingStatus(FunctionMagic.status(vo.getProcessingStatus()));
+            } catch (Exception e) {
+                log.warn("转换处理状态失败,processingStatus={}, error={}", vo.getProcessingStatus(), e.getMessage());
+                excelVO.setProcessingStatus(vo.getProcessingStatus());
             }
-            res.add(dto);
-        });
-        String fileName = UUID.randomUUID().toString().replace("-", "");
-        String filePath = ExcelGenerator.generateExcel(excelPath + excelGeneratePath + fileName + ".xlsx", res, LifeUserViolationExcelVO.class);
-        return aliOSSUtil.uploadFile(new File(filePath), "excel/" + fileName + ".xlsx");
+        }
+        
+        // 格式化创建时间
+        if (Objects.nonNull(vo.getCreatedTime())) {
+            try {
+                String formattedTime = vo.getCreatedTime().toInstant()
+                        .atZone(ZoneId.systemDefault())
+                        .toLocalDateTime()
+                        .format(formatter);
+                excelVO.setCreatedTime(formattedTime);
+            } catch (Exception e) {
+                log.warn("格式化创建时间失败,createdTime={}, error={}", vo.getCreatedTime(), e.getMessage());
+            }
+        }
+        
+        // 格式化处理时间
+        if (Objects.nonNull(vo.getProcessingTime())) {
+            try {
+                String formattedProcessingTime = vo.getProcessingTime().toInstant()
+                        .atZone(ZoneId.systemDefault())
+                        .toLocalDateTime()
+                        .format(formatter);
+                excelVO.setProcessingTime(formattedProcessingTime);
+            } catch (Exception e) {
+                log.warn("格式化处理时间失败,processingTime={}, error={}", vo.getProcessingTime(), e.getMessage());
+            }
+        }
+        
+        return excelVO;
     }
 
     @Override

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

@@ -260,6 +260,12 @@ public class PlatformStoreCouponServiceImpl implements PlatformStoreCouponServic
             if (lifeGroupBuyMain.getStartTimeType() == 0 && lifeGroupBuyMain.getStatus() == 5) {
                 lifeGroupBuyMain.setStartTimeValue(new Date());
             }
+
+            //审核通过 开始时间是手动设置且不在当前时间内
+            if (lifeGroupBuyMain.getStartTimeType() == 1 && lifeGroupBuyMain.getStatus() == 5 && lifeGroupBuyMain.getStartTimeValue().after(new Date())) {
+                lifeGroupBuyMain.setStatus(2);
+            }
+
             // 将待办列表状态设置为已审核
             LambdaUpdateWrapper<WebAudit> wrapper = new LambdaUpdateWrapper<>();
             wrapper.eq(WebAudit::getLifeGroupPackageId, id);

+ 45 - 43
alien-store/src/main/java/shop/alien/store/service/impl/StoreIncomeDetailsRecordServiceImpl.java

@@ -68,8 +68,8 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
     /**
      * 提现-提现全部-手续费一单一算
      *
-     * @param storeId     门店id
-     * @param payPassword 支付密码
+     * @param storeId         门店id
+     * @param payPassword     支付密码
      * @param withdrawalMoney 提现金额
      * @return 是否成功
      */
@@ -131,8 +131,8 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
             }
             return "账单未到可提现时间";
         }*/
-        if ( null != storeUser){
-            if( storeUser.getMoney() >= withdrawalMoney){
+        if (null != storeUser) {
+            if (storeUser.getMoney() >= withdrawalMoney) {
                 //调用支付宝转账
                 BigDecimal decimal = new BigDecimal(withdrawalMoney);
                 BigDecimal divide = decimal.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
@@ -140,14 +140,14 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
                     return R.fail("金额不能小于0.1元");
                 }
                 StoreAliPayLog pay = new StoreAliPayLog();
-                if(StringUtils.isNotBlank(storeUser.getAlipayAccount())){
+                if (StringUtils.isNotBlank(storeUser.getAlipayAccount())) {
                     pay = aliApi.pay(storeUser.getName(), storeUser.getIdCard(), storeUser.getAlipayAccount(), divide.toString());
-                }else{
+                } else {
                     pay = aliApi.payAccount(storeUser.getName(), storeUser.getIdCard(), null, divide.toString(), storeUser.getPhone());
                 }
                 //增加提现记录
                 StoreCashOutRecord storeCashOutRecord = new StoreCashOutRecord();
-                if(pay.getStoreAliPayErrorLog() == null){
+                if (pay.getStoreAliPayErrorLog() == null) {
                     storeCashOutRecord.setStoreId(storeId);
                     storeCashOutRecord.setOrderNo(pay.getOutBizNo());
                     storeCashOutRecord.setMoney(withdrawalMoney);
@@ -165,7 +165,7 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
                     //减少账户余额
                     storeUserMapper.updateById(new StoreUser(storeUser.getId(), storeUser.getMoney() - withdrawalMoney));
                     return R.data(storeCashOutRecord);
-                }else{
+                } else {
                     // 提现失败
                     storeCashOutRecord.setStoreId(storeId);
                     storeCashOutRecord.setPaymentStatus(2);
@@ -187,7 +187,7 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
     /**
      * 提现申请-提现全部-手续费一单一算
      *
-     * @param storeId     门店id
+     * @param storeId 门店id
      * @return 是否成功
      */
     @Transactional
@@ -198,10 +198,10 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
             int freeze = 0;
             //查询冻结配置
             SystemConfigVo systemConfigVo = storeDictService.getConfig("freeze");
-            if(systemConfigVo != null){
+            if (systemConfigVo != null) {
                 String freezeFlag = systemConfigVo.getConfigValue1();
-                String freezeDay =  systemConfigVo.getConfigValue2();
-                if(StringUtils.isNotBlank(freezeFlag) && freezeFlag.equals("1") && StringUtils.isNotBlank(freezeDay)){
+                String freezeDay = systemConfigVo.getConfigValue2();
+                if (StringUtils.isNotBlank(freezeFlag) && freezeFlag.equals("1") && StringUtils.isNotBlank(freezeDay)) {
                     freeze = Integer.parseInt(freezeDay);
                 }
             }
@@ -209,7 +209,7 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
             //查询可用账单
             LambdaQueryWrapper<StoreIncomeDetailsRecord> wrapper = new LambdaQueryWrapper<>();
             //当前时间-3天大于创建时间
-            if(freeze > 0){
+            if (freeze > 0) {
                 wrapper.lt(StoreIncomeDetailsRecord::getCreatedTime, DateUtils.calcDays(new Date(), -freeze));
             }
             //未绑定提现记录的
@@ -256,7 +256,7 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
     public String approveCashOut(Integer cashOutId, String approveStatus, String failReason) {
         if (cashOutId != null && cashOutId > 0 && StringUtils.isNotBlank(approveStatus)) {
             StoreCashOutRecord storeCashOutRecord = storeCashOutRecordMapper.selectById(cashOutId);
-            if(!(storeCashOutRecord.getPaymentStatus() == 3)){
+            if (!(storeCashOutRecord.getPaymentStatus() == 3)) {
                 return "付款状态异常";
             }
             Integer storeUserId = storeCashOutRecord.getStoreUserId();
@@ -264,11 +264,11 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
 
             if (approveStatus.equals("0")) {
                 //同意提现通知
-                cashOutSendNotice(storeCashOutRecord, storeUser, failReason,"1");
+                cashOutSendNotice(storeCashOutRecord, storeUser, failReason, "1");
 
                 //同意,开始提现
                 int money = storeCashOutRecord.getMoney();
-                if(money > storeUser.getMoney()) {
+                if (money > storeUser.getMoney()) {
                     money = storeUser.getMoney();
                 }
                 BigDecimal decimal = new BigDecimal(money);
@@ -286,12 +286,12 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
                     storeCashOutRecord.setPayDate(new Date());//支付时间
                     storeCashOutRecord.setPaymentDate(new Date());//支付到账时间
                     Integer commission = storeCashOutRecord.getCommission();
-                    if(storeCashOutRecord.getCommission() == null ) {
+                    if (storeCashOutRecord.getCommission() == null) {
                         commission = 0;
                     }
                     storeCashOutRecordMapper.updateById(storeCashOutRecord);
-                    int deductMoney =  storeCashOutRecord.getMoney() - commission;
-                    if(storeUser.getMoney() < deductMoney) {
+                    int deductMoney = storeCashOutRecord.getMoney() - commission;
+                    if (storeUser.getMoney() < deductMoney) {
                         storeUserMapper.updateById(new StoreUser(storeUserId, 0));
                     } else {
                         storeUserMapper.updateById(new StoreUser(storeUserId, storeUser.getMoney() - storeCashOutRecord.getMoney() - commission));
@@ -320,7 +320,7 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
                 lambdaUpdateWrapper.eq(StoreIncomeDetailsRecord::getStoreId, storeCashOutRecord.getStoreId())
                         .eq(StoreIncomeDetailsRecord::getCashOutId, storeCashOutRecord.getId())
                         .set(StoreIncomeDetailsRecord::getCashOutId, "");
-                 int result = storeIncomeDetailsRecordMapper.updateByCashOutId(storeCashOutRecord.getStoreId(), storeCashOutRecord.getId());
+                int result = storeIncomeDetailsRecordMapper.updateByCashOutId(storeCashOutRecord.getStoreId(), storeCashOutRecord.getId());
 
                 //驳回提现申请通知
                 cashOutSendNotice(storeCashOutRecord, storeUser, failReason, "2");
@@ -341,16 +341,16 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
         com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
         String text = "";
         if (noticeType.equals("1")) {
-            text = "您在"+commonDate+"申请提现"+moneyStr+"元,已审核成功,提现中,请耐心等待。";
+            text = "您在" + commonDate + "申请提现" + moneyStr + "元,已审核成功,提现中,请耐心等待。";
             lifeMessage.setTitle("提现审核");
-        } else if(noticeType.equals("2")) {
-            text = "您在"+commonDate+"申请提现"+moneyStr+"元,审核失败。失败原因:"+failReason;
+        } else if (noticeType.equals("2")) {
+            text = "您在" + commonDate + "申请提现" + moneyStr + "元,审核失败。失败原因:" + failReason;
             lifeMessage.setTitle("提现审核");
-        } else if(noticeType.equals("3")){
-            text = "您在"+commonDate+"申请提现"+moneyStr+"元,已成功到达您的支付宝账号,请注意查收。";
+        } else if (noticeType.equals("3")) {
+            text = "您在" + commonDate + "申请提现" + moneyStr + "元,已成功到达您的支付宝账号,请注意查收。";
             lifeMessage.setTitle("提现到账");
-        } else{
-            text = "您在"+commonDate+"申请提现"+moneyStr+"元,到账失败,请重新申请或联系客服。";
+        } else {
+            text = "您在" + commonDate + "申请提现" + moneyStr + "元,到账失败,请重新申请或联系客服。";
             lifeMessage.setTitle("提现到账");
         }
         jsonObject.put("message", text);
@@ -422,8 +422,8 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
             Date endDate = DateUtils.calcDays(now, -4);
             jsonObject.put("date", df.format(startDate) + " ~ " + df.format(endDate));
         }
-        if(storeId!=null){
-            wrapper.eq("sidr.store_id",storeId);
+        if (storeId != null) {
+            wrapper.eq("sidr.store_id", storeId);
         }
         //未绑定提现记录的
         LocalDate startDate = LocalDate.parse(startTime);
@@ -431,24 +431,25 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
         // 创建当天的开始和结束时间点
         LocalDateTime startOfDay = startDate.atStartOfDay();
         LocalDateTime endOfDay = endDate.atTime(LocalTime.MAX);
-        wrapper.between("sidr.created_time",startOfDay,endOfDay)
+        wrapper.between("sidr.created_time", startOfDay, endOfDay)
                 .orderByDesc("sidr.created_time");
-        if(null != incomeType) {
+        if (null != incomeType) {
             if (0 == incomeType) {
-                wrapper.in("sidr.income_type", CouponTypeEnum.COUPON.getCode(),CouponTypeEnum.GROUP_BUY.getCode());
+                wrapper.in("sidr.income_type", CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
             } else {
                 wrapper.eq("sidr.income_type", incomeType);
             }
         }
         // 计算总钱数
         List<StoreIncomeDetailsRecordVo> list = storeIncomeDetailsRecordMapper.selectRecordList(wrapper);
-
+        StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
         for (StoreIncomeDetailsRecordVo storeIncomeDetailsRecord : list) {
             StoreIncomeDetailsRecordVo vo = new StoreIncomeDetailsRecordVo();
             BeanUtils.copyProperties(storeIncomeDetailsRecord, vo);
             storeIncomeDetailsRecord.setMoneyStr(new BigDecimal(vo.getMoney()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
             String format = df.format(storeIncomeDetailsRecord.getCreatedTime());
             storeIncomeDetailsRecord.setDate(format);
+            vo.setCommissionRate(storeInfo.getCommissionRate());
         }
         jsonObject.put("data", ListToPage.setPage(list, page, size));
         jsonObject.put("money", new BigDecimal(list.stream().mapToInt(StoreIncomeDetailsRecord::getMoney).sum()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
@@ -479,7 +480,7 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
         List<StoreIncomeDetailsRecord> list = this.list(wrapper);
         int cashOutMoney = 0;
         cashOutMoney += list.stream().mapToInt(StoreIncomeDetailsRecord::getMoney).sum();
-        List<StoreCashOutRecord> storeCashOutRecords = storeCashOutRecordMapper.selectList(new QueryWrapper<StoreCashOutRecord>().eq("store_id", storeId).in("payment_status", "1","3").eq("delete_flag", "0"));
+        List<StoreCashOutRecord> storeCashOutRecords = storeCashOutRecordMapper.selectList(new QueryWrapper<StoreCashOutRecord>().eq("store_id", storeId).in("payment_status", "1", "3").eq("delete_flag", "0"));
         int totalAmount = storeCashOutRecords.stream().collect(Collectors.summingInt(StoreCashOutRecord::getMoney));
         //可提现金额 4~27天
         map.put("cashOutMoney", new BigDecimal(cashOutMoney).subtract(BigDecimal.valueOf(totalAmount)).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
@@ -502,8 +503,8 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
                 //未绑定提现记录的
                 .isNull(StoreIncomeDetailsRecord::getCashOutId)
                 .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
-        if(null != incomeType && 0 == incomeType){
-            wrapper.in(StoreIncomeDetailsRecord::getIncomeType, CouponTypeEnum.COUPON.getCode(),CouponTypeEnum.GROUP_BUY.getCode());
+        if (null != incomeType && 0 == incomeType) {
+            wrapper.in(StoreIncomeDetailsRecord::getIncomeType, CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
         } else {
             wrapper.eq(StoreIncomeDetailsRecord::getIncomeType, incomeType);
         }
@@ -518,6 +519,7 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
             commission += storeIncomeDetailsRecord.getCommission();
         }
         StoreIncomeDetailsRecordVo vo = new StoreIncomeDetailsRecordVo();
+        vo.setCommissionRate(storeInfo.getCommissionRate());
         JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(list));
         SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         jsonArray.forEach(item -> {
@@ -547,9 +549,9 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
                 .eq("income.store_id", storeId)
                 .between("income.created_time", date + " 00:00:00", date + " 23:59:59")
                 .orderByDesc("income.created_time");
-        if(null != incomeType) {
+        if (null != incomeType) {
             if (0 == incomeType) {
-                incomeWrapper.in("income_type", CouponTypeEnum.COUPON.getCode(),CouponTypeEnum.GROUP_BUY.getCode());
+                incomeWrapper.in("income_type", CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
             } else {
                 incomeWrapper.eq("income_type", incomeType);
             }
@@ -560,16 +562,16 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
         vo.setIncomeDetailsRecordVoList(new ArrayList<>());
         vo.setRefundMoney(new BigDecimal(0));
         vo.setCouponCount(0);
-        if ( collect.containsKey("false")){
-            vo.setIncomeDetailsRecordVoList(ListToPage.setPage(incomeDetailsRecordVoList,page,size).getRecords());
+        if (collect.containsKey("false")) {
+            vo.setIncomeDetailsRecordVoList(ListToPage.setPage(incomeDetailsRecordVoList, page, size).getRecords());
 //                    ListToPage.setPage(
 //                            collect.get("false")
 //                                    .stream()
 //                                    .sorted(Comparator.comparing(StoreIncomeDetailsRecordVo::getCheckTime)
 //                                            .reversed()).collect(Collectors.toList()), page, size).getRecords());
-            vo.setCouponCount( collect.get("false").size() - (collect.containsKey("true") ? collect.get("true").size() : 0));
+            vo.setCouponCount(collect.get("false").size() - (collect.containsKey("true") ? collect.get("true").size() : 0));
         }
-        if( collect.containsKey("true") ){
+        if (collect.containsKey("true")) {
             vo.setRefundMoney(new BigDecimal(collect.get("true").stream().mapToInt(StoreIncomeDetailsRecordVo::getMoney).sum()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
         }
         return vo;
@@ -580,7 +582,7 @@ public class StoreIncomeDetailsRecordServiceImpl extends ServiceImpl<StoreIncome
         LambdaQueryWrapper<LifeUserOrder> wrapper = new LambdaQueryWrapper<>();
         LocalDate now = LocalDate.now();
         wrapper.eq(LifeUserOrder::getStoreId, storeId)
-                .notIn(LifeUserOrder::getStatus, OrderStatusEnum.CANCEL.getStatus(), OrderStatusEnum.WAIT_PAY.getStatus(),OrderStatusEnum.EXPIRE.getStatus())
+                .notIn(LifeUserOrder::getStatus, OrderStatusEnum.CANCEL.getStatus(), OrderStatusEnum.WAIT_PAY.getStatus(), OrderStatusEnum.EXPIRE.getStatus())
                 .between(LifeUserOrder::getBuyTime, now + " 00:00:00", now + " 23:59:59");
         return lifeUserOrderMapper.selectCount(wrapper);
     }

+ 93 - 127
alien-store/src/main/java/shop/alien/store/service/impl/StoreUserServiceImpl.java

@@ -104,7 +104,7 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
         lambdaQueryWrapper.eq(StoreUser::getPhone, phone);
         StoreUser user = this.getOne(lambdaQueryWrapper);
         StoreUserVo storeUserVo = new StoreUserVo();
-        if(user != null){
+        if (user != null) {
             if (user.getStatus() == -1) {
                 LocalDateTime localDateTime = user.getLogoutTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                 LocalDateTime future = localDateTime.plusDays(7);
@@ -113,7 +113,7 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
                 long correct = duration.toMillis();
                 storeUserVo.setCountdown(correct);
             }
-        }else {
+        } else {
             return new StoreUserVo();
         }
         BeanUtils.copyProperties(user, storeUserVo);
@@ -210,7 +210,7 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
         try {
             //类型为0 忘记密码
             if (type == 0) {
-                return forgetPassword(phone, newPassword, verificationCode);
+                return forgetPassword(phone, newPassword);
             }
             //修改密码
             else if (type == 1) {
@@ -236,7 +236,7 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
             }
             //更换绑定手机号
             else if (type == 2) {
-                return ChangeBoundPhone(phone, newPhone, verificationCode);
+                return ChangeBoundPhone(phone, newPhone);
             }
             return R.success("密码修改成功");
         } catch (Exception e) {
@@ -247,7 +247,6 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
     @Override
     public Map<String, String> changePhoneVerification(String phone, String oldPassword, String verificationCode) {
         Map<String, String> changePhoneMap = new HashMap<>();
-
         if (oldPassword != null && !oldPassword.equals("")) {
             LambdaUpdateWrapper<StoreUser> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
             userLambdaUpdateWrapper.eq(StoreUser::getPhone, phone);
@@ -260,19 +259,8 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
                 return changePhoneMap;
             }
         } else {
-            String oldPhoneVerification = "verification_" + phone;
-            //获取新手机号验证码
-            String redisVerificationCode = baseRedisService.getString(oldPhoneVerification);
-            if(StringUtils.isEmpty(redisVerificationCode)){
-                throw new RuntimeException("验证码已失效 请重新获取验证码");
-            }
-            if (redisVerificationCode.equals(verificationCode)) {
-                changePhoneMap.put("verificationStatus", "1");
-                return changePhoneMap;
-            } else {
-                changePhoneMap.put("verificationStatus", "0");
-                return changePhoneMap;
-            }
+            changePhoneMap.put("verificationStatus", "0");
+            return changePhoneMap;
         }
     }
 
@@ -297,62 +285,50 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
         }
     }
 
-    private R<String> forgetPassword(String phone, String newPassword, String verificationCode) {
+    private R<String> forgetPassword(String phone, String newPassword) {
+        boolean flag = false;
+        LambdaUpdateWrapper<StoreUser> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(StoreUser::getPhone, phone);
+        updateWrapper.set(StoreUser::getPassword, newPassword);
+        flag = this.update(updateWrapper);
+        if (flag) {
+            log.info("密码修改成功");
+            String token = "store_" + phone;
+            String tokenStr = baseRedisService.getString(token);
+            if (tokenStr != null) {
+                baseRedisService.delete(token);
+            }
+        }
+        if (!flag) {
+            log.error("密码修改失败");
+            throw new RuntimeException("密码修改失败");
+        }
+        return R.success("密码修改成功");
+    }
+
+    private R<String> ChangeBoundPhone(String phone, String newPhone) {
         boolean flag = false;
-        String key = "verification_" + phone;
-        String redisVerificationCode = baseRedisService.getString(key);
-        if (!StringUtils.isEmpty(redisVerificationCode) && redisVerificationCode.equals(verificationCode)) {
+        //获取新手机号验证码
+        LambdaUpdateWrapper<StoreUser> newUpdateWrapper = new LambdaUpdateWrapper<>();
+        newUpdateWrapper.eq(StoreUser::getPhone, newPhone);
+        StoreUser newStoreUser = this.getOne(newUpdateWrapper);
+        if (newStoreUser != null) {
+            if (newStoreUser.getPhone().equals(newPhone)) {
+                throw new RuntimeException("该手机号已注册过商户");
+            }
+        } else {
             LambdaUpdateWrapper<StoreUser> updateWrapper = new LambdaUpdateWrapper<>();
             updateWrapper.eq(StoreUser::getPhone, phone);
-            updateWrapper.set(StoreUser::getPassword, newPassword);
-            flag = this.update(updateWrapper);
+            StoreUser storeUser = this.getOne(updateWrapper);
+            storeUser.setPhone(newPhone);
+            flag = this.updateById(storeUser);
             if (flag) {
-                log.info("密码修改成功");
                 String token = "store_" + phone;
                 String tokenStr = baseRedisService.getString(token);
                 if (tokenStr != null) {
                     baseRedisService.delete(token);
                 }
             }
-            if (!flag) {
-                log.error("密码修改失败");
-                throw new RuntimeException("密码修改失败");
-            }
-            return R.success("密码修改成功");
-        } else {
-            throw new RuntimeException("验证码错误");
-        }
-    }
-
-    private R<String> ChangeBoundPhone(String phone, String newPhone, String verificationCode) {
-        boolean flag = false;
-        String newPhoneVerification = "verification_" + newPhone;
-        //获取新手机号验证码
-        String redisVerificationCode = baseRedisService.getString(newPhoneVerification);
-        if (!StringUtils.isEmpty(redisVerificationCode) && redisVerificationCode.equals(verificationCode)) {
-            LambdaUpdateWrapper<StoreUser> newUpdateWrapper = new LambdaUpdateWrapper<>();
-            newUpdateWrapper.eq(StoreUser::getPhone, newPhone);
-            StoreUser newStoreUser = this.getOne(newUpdateWrapper);
-            if (newStoreUser != null) {
-                if (newStoreUser.getPhone().equals(newPhone)) {
-                    throw new RuntimeException("该手机号已注册过商户");
-                }
-            } else {
-                LambdaUpdateWrapper<StoreUser> updateWrapper = new LambdaUpdateWrapper<>();
-                updateWrapper.eq(StoreUser::getPhone, phone);
-                StoreUser storeUser = this.getOne(updateWrapper);
-                storeUser.setPhone(newPhone);
-                flag = this.updateById(storeUser);
-                if (flag) {
-                    String token = "store_" + phone;
-                    String tokenStr = baseRedisService.getString(token);
-                    if (tokenStr != null) {
-                        baseRedisService.delete(token);
-                    }
-                }
-            }
-        } else {
-            throw new RuntimeException("验证码错误");
         }
         return R.success("新手机号绑定成功");
     }
@@ -662,57 +638,47 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
         LambdaQueryWrapper<StoreUser> storeUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
         storeUserLambdaQueryWrapper.eq(StoreUser::getId, storeUserVo.getId());
         StoreUser storeUser = storeUserMapper.selectOne(storeUserLambdaQueryWrapper);
-        String key = "verification_" + storeUser.getPhone();
-        String redisVerificationCode = baseRedisService.getString(key);
-        if (redisVerificationCode != null) {
-            if (redisVerificationCode.equals(storeUserVo.getVerificationCode())) {
-                if (storeUser != null) {
-                    // 添加注销原因
-                    storeUser.setLogoutReason(storeUserVo.getLogoutReason());
-                    // 添加注销code
-                    storeUser.setLogoutCode(storeUserVo.getLogoutCode());
-                    // 注销中状态
-                    storeUser.setStatus(-1);
-                    // 添加注销申请时间
-                    storeUser.setLogoutTime(new Date());
-                    // 更新logout_flag状态为1
-                    storeUser.setLogoutFlag(1);
-                    int num = storeUserMapper.updateById(storeUser);
-                    if (num > 0) {
-                        // 发送通知
-                        LifeNotice lifeMessage = new LifeNotice();
-                        lifeMessage.setReceiverId("store_" + storeUser.getPhone());
-                        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-                        String storeDate = simpleDateFormat.format(new Date());
-                        String text = "您在"+storeDate+"注销了账号,平台将为您保留7天,在此期间您可撤销注销账号,7天后将会永久删除。";
-                        com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
-                        jsonObject.put("message", text);
-                        lifeMessage.setContext(jsonObject.toJSONString());
-                        lifeMessage.setTitle("注销商家账号通知");
-                        lifeMessage.setSenderId("system");
-                        lifeMessage.setIsRead(0);
-                        lifeMessage.setNoticeType(1);
-                        lifeNoticeMapper.insert(lifeMessage);
-
-                        WebSocketVo websocketVo = new WebSocketVo();
-                        websocketVo.setSenderId("system");
-                        websocketVo.setReceiverId("store_" + storeUser.getPhone());
-                        websocketVo.setCategory("notice");
-                        websocketVo.setNoticeType("1");
-                        websocketVo.setIsRead(0);
-                        websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeMessage).toJSONString());
-                        try {
-                            webSocketProcess.sendMessage("store_" + storeUser.getPhone(), com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
-                        } catch (Exception e) {
-                            log.error("LifeUserViolationServiceImpl_approve Error Stack={}", e.getMessage());
-                        }
-                    }
+        if (storeUser != null) {
+            // 添加注销原因
+            storeUser.setLogoutReason(storeUserVo.getLogoutReason());
+            // 添加注销code
+            storeUser.setLogoutCode(storeUserVo.getLogoutCode());
+            // 注销中状态
+            storeUser.setStatus(-1);
+            // 添加注销申请时间
+            storeUser.setLogoutTime(new Date());
+            // 更新logout_flag状态为1
+            storeUser.setLogoutFlag(1);
+            int num = storeUserMapper.updateById(storeUser);
+            if (num > 0) {
+                // 发送通知
+                LifeNotice lifeMessage = new LifeNotice();
+                lifeMessage.setReceiverId("store_" + storeUser.getPhone());
+                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                String storeDate = simpleDateFormat.format(new Date());
+                String text = "您在" + storeDate + "注销了账号,平台将为您保留7天,在此期间您可撤销注销账号,7天后将会永久删除。";
+                com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
+                jsonObject.put("message", text);
+                lifeMessage.setContext(jsonObject.toJSONString());
+                lifeMessage.setTitle("注销商家账号通知");
+                lifeMessage.setSenderId("system");
+                lifeMessage.setIsRead(0);
+                lifeMessage.setNoticeType(1);
+                lifeNoticeMapper.insert(lifeMessage);
+
+                WebSocketVo websocketVo = new WebSocketVo();
+                websocketVo.setSenderId("system");
+                websocketVo.setReceiverId("store_" + storeUser.getPhone());
+                websocketVo.setCategory("notice");
+                websocketVo.setNoticeType("1");
+                websocketVo.setIsRead(0);
+                websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeMessage).toJSONString());
+                try {
+                    webSocketProcess.sendMessage("store_" + storeUser.getPhone(), com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
+                } catch (Exception e) {
+                    log.error("LifeUserViolationServiceImpl_approve Error Stack={}", e.getMessage());
                 }
-            } else {
-                throw new RuntimeException("验证码错误");
             }
-        } else {
-            throw new RuntimeException("验证码错误");
         }
     }
 
@@ -741,7 +707,7 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
             lifeMessage.setReceiverId("store_" + storeUser.getPhone());
             SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             String storeDate = simpleDateFormat.format(storeUser.getCreatedTime());
-            String text = "您在"+storeDate+"撤销了注销账号,所有数据均已保留,您可继续在平台使用。";
+            String text = "您在" + storeDate + "撤销了注销账号,所有数据均已保留,您可继续在平台使用。";
             com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
             jsonObject.put("message", text);
             lifeMessage.setContext(jsonObject.toJSONString());
@@ -804,28 +770,28 @@ public class StoreUserServiceImpl extends ServiceImpl<StoreUserMapper, StoreUser
     }
 
     @Override
-    public R havePayPassword(String storeUserId,String password) {
+    public R havePayPassword(String storeUserId, String password) {
         StoreUser storeUser = storeUserMapper.selectById(storeUserId);
         HashMap<Object, Object> returnMap = new HashMap<>();
-        returnMap.put("code",200);
+        returnMap.put("code", 200);
         if (null == storeUser) {
-            returnMap.put("message","未查询到用户");
-            returnMap.put("data","false");
+            returnMap.put("message", "未查询到用户");
+            returnMap.put("data", "false");
             return R.data(returnMap);
         }
-        if( null == storeUser.getPayPassword()){
-            returnMap.put("message","用户未设置支付密码");
-            returnMap.put("data","false");
+        if (null == storeUser.getPayPassword()) {
+            returnMap.put("message", "用户未设置支付密码");
+            returnMap.put("data", "false");
             return R.data(returnMap);
-        } else if(null != password){
-            if( !password.equals(storeUser.getPayPassword())){
-                returnMap.put("message","密码错误");
-                returnMap.put("data","false");
+        } else if (null != password) {
+            if (!password.equals(storeUser.getPayPassword())) {
+                returnMap.put("message", "密码错误");
+                returnMap.put("data", "false");
                 return R.data(returnMap);
             }
         }
-        returnMap.put("data","true");
-        returnMap.put("message","用户已设置支付密码");
+        returnMap.put("data", "true");
+        returnMap.put("message", "用户已设置支付密码");
         return R.data(returnMap);
     }
 

+ 2 - 0
alien-store/src/main/java/shop/alien/store/util/FunctionMagic.java

@@ -261,6 +261,8 @@ public class FunctionMagic {
         CONTEXT.put("1", "用户");
         CONTEXT.put("2", "动态");
         CONTEXT.put("3", "评论");
+        CONTEXT.put("4", "二手商品");
+        CONTEXT.put("5", "二手用户");
     }
 
 

+ 2 - 1
alien-store/src/main/java/shop/alien/store/util/ali/AliApi.java

@@ -207,7 +207,8 @@ public class AliApi {
 
             } else {
                 log.warn("AliPayConfig.processRefund ERROR Msg={}", response.getBody());
-                refundReslut = "调用失败";
+                JSONObject jsonObject = JSONObject.parseObject(response.getBody()).getJSONObject("alipay_trade_refund_response");
+                refundReslut =  jsonObject.getString("sub_msg");
             }
             return refundReslut;
         } catch (AlipayApiException e) {

+ 44 - 5
alien-store/src/main/java/shop/alien/store/util/ali/AliSms.java

@@ -43,21 +43,60 @@ public class AliSms {
     @Value("${ali.sms.templateCode}")
     private String templateCode;
 
+    @Value("${ali.sms.codeTimeOut}")
+    private Long codeTimeOut;
+
     /**
      * 发送验证码
      *
-     * @param phone 手机号
+     * @param phone        手机号
+     * @param appType      端区分(0:用户, 1:商家)
+     * @param businessType 业务类型 (0:登录, 1:修改密码, 2:注册, 3:修改手机号, 4:注销店铺, 5:注销账号, 6:忘记密码)
      * @return 验证码
      */
-    public Integer sendSms(String phone) {
-        log.info("AliSmsConfig.sendSms?phone={}", phone);
+    public Integer sendSms(String phone, Integer appType, Integer businessType) {
+        log.info("AliSmsConfig.sendSms?phone={}&appType={}&businessType={}", phone, appType, businessType);
         try {
+            String appTypeStr = appType == 0 ? "user" : "store";
+            String businessTypeStr;
+            switch (businessType) {
+                case 0:
+                    //登录
+                    businessTypeStr = "login";
+                    break;
+                case 1:
+                    //修改密码
+                    businessTypeStr = "modify_password";
+                    break;
+                case 2:
+                    //注册
+                    businessTypeStr = "register";
+                    break;
+                case 3:
+                    //修改手机号
+                    businessTypeStr = "modify_phone";
+                    break;
+                case 4:
+                    //注销店铺
+                    businessTypeStr = "cancel_store";
+                    break;
+                case 5:
+                    //注销账号
+                    businessTypeStr = "cancel_account";
+                    break;
+                case 6:
+                    //忘记密码
+                    businessTypeStr = "forget_password";
+                    break;
+                default:
+                    businessTypeStr = "login";
+            }
             // -----------------测试用手机号--------------------------------------------------------------------------------------------
             List<String> phoneList = Arrays.asList("19999990001", "19999990002", "19999990003", "19999990004", "19999990005", "19999990006", "19999990007", "19999990008", "19999990009", "19999990010",
                     "16666660001", "16666660002", "16666660003", "16666660004", "16666660005", "16666660006", "16666660007", "16666660008", "16666660009", "16666660010");
             if (phoneList.contains(phone)) {
                 // 验证码发送成功,将验证码保存到redis中 设置60秒过期
-                baseRedisService.setString("verification_"+phone,"123456",Long.valueOf(60));
+                baseRedisService.setString("verification_" + appTypeStr + "_" + businessTypeStr + "_" + phone, "123456", codeTimeOut);
                 return 123456;
             }
             // -----------------测试用手机号--------------------------------------------------------------------------------------------
@@ -87,7 +126,7 @@ public class AliSms {
                 return null;
             }
             // 验证码发送成功,将验证码保存到redis中 设置60秒过期
-            baseRedisService.setString("verification_"+phone,code.toString(),Long.valueOf(60));
+            baseRedisService.setString("verification_" + appTypeStr + "_" + businessTypeStr + "_" + phone, code.toString(), codeTimeOut);
             return code;
         } catch (Exception e) {
             log.error("AliSmsConfig.sendSms ERROR Msg={}", e.getMessage());

+ 7 - 0
pom.xml

@@ -359,6 +359,13 @@
                 <version>3.4.0</version>
             </dependency>
 
+            <!-- 验证码 -->
+            <dependency>
+                <groupId>pro.fessional</groupId>
+                <artifactId>kaptcha</artifactId>
+                <version>2.3.3</version>
+            </dependency>
+
             <!--Other End-->
         </dependencies>
     </dependencyManagement>