소스 검색

chore: merge master

周玉环 5 일 전
부모
커밋
9d6960bb8f
17개의 변경된 파일470개의 추가작업 그리고 31개의 파일을 삭제
  1. 1 1
      xinkeaboard-server/b2b2c-core/src/main/java/com/slodon/b2b2c/core/config/DomainUrlUtil.java
  2. 8 0
      xinkeaboard-server/b2b2c-core/src/main/java/com/slodon/b2b2c/core/constant/RedisConst.java
  3. 9 0
      xinkeaboard-server/b2b2c-core/src/main/java/com/slodon/b2b2c/core/util/AssertUtil.java
  4. 10 0
      xinkeaboard-server/b2b2c-core/src/main/resources/i18n_en.properties
  5. 6 0
      xinkeaboard-server/b2b2c-entity/src/main/java/com/slodon/b2b2c/member/pojo/Member.java
  6. 14 0
      xinkeaboard-server/b2b2c-entity/src/main/java/com/slodon/b2b2c/system/dto/CountryAreaApiDto.java
  7. 15 1
      xinkeaboard-server/b2b2c-web/pom.xml
  8. 9 0
      xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/controller/member/front/MemberInfoController.java
  9. 198 1
      xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/controller/member/front/advich/MemberEmailActiveController.java
  10. 1 1
      xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/controller/sso/front/FrontAuthController.java
  11. 7 0
      xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/model/member/MemberProductLookLogModel.java
  12. 10 2
      xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/model/member/advich/MemberEnquiryModel.java
  13. 86 0
      xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/model/member/advich/MemberRegisterActiveModel.java
  14. 34 25
      xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/upload/MinioUpload.java
  15. 4 0
      xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/vo/member/FollowProductVO.java
  16. 3 0
      xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/vo/member/ProductLookLogVO.java
  17. 55 0
      xinkeaboard-server/b2b2c-web/src/main/resources/application-test.yml

+ 1 - 1
xinkeaboard-server/b2b2c-core/src/main/java/com/slodon/b2b2c/core/config/DomainUrlUtil.java

@@ -44,7 +44,7 @@ public class DomainUrlUtil {
     /**
      * minio图片资源的URL
      */
-    public static final String SLD_IMAGE_RESOURCES = "http://54.46.9.88";
+    public static final String SLD_IMAGE_RESOURCES = "https://xinke-xinke-s3-test.s3.ap-east-1.amazonaws.com/";
 
     /**
      * 七牛云图片资源的URL

+ 8 - 0
xinkeaboard-server/b2b2c-core/src/main/java/com/slodon/b2b2c/core/constant/RedisConst.java

@@ -78,6 +78,8 @@ public class RedisConst {
      */
     public static final String DRAW_PREFIX = "draw_";
 
+    public static final String SLD_PC_EMAIL_VERIFY_CODE_LIMIT="sld_pc_verify_code_limit:email::";
+
     /**
      * reids中保存邮箱用户注册,key=前缀+email
      */
@@ -98,6 +100,12 @@ public class RedisConst {
      */
     public static final long REGISTER_USER_AND_FORGETPWD_USER_EMAIL_LINK_EXPIRE_TIME = 60L * 10 * 6 * 24;
 
+
+    /**
+     * 用户注册、忘记密码验证码有效时间:5min
+     */
+    public static final long REGISTER_USER_AND_FORGETPWD_USER_EMAIL_VERIFICATION_CODE_EXPIRE_TIME = 60L * 5;
+
     /**
      * 单点登录链接有效时间:600秒即10分钟
      */

+ 9 - 0
xinkeaboard-server/b2b2c-core/src/main/java/com/slodon/b2b2c/core/util/AssertUtil.java

@@ -214,4 +214,13 @@ public class AssertUtil {
             throw new MallException("请输入正确的数字");
         }
     }
+
+    public static void passwordCheck(String password) {
+        String check = "^[a-zA-Z0-9!@#$%^&*()\\\\{\\\\}\\\\[\\\\]|\\\\\\\\:;'\\\",./?]{6,20}$";
+        Pattern regex = Pattern.compile(check);
+        Matcher matcher = regex.matcher(password);
+        if (!matcher.matches()) {
+            throw new MallException("请输入正确的密码");
+        }
+    }
 }

+ 10 - 0
xinkeaboard-server/b2b2c-core/src/main/resources/i18n_en.properties

@@ -1807,6 +1807,7 @@ admin账号不可删除=admin account can not be deleted
 更新直播和短视频设置表失败,请重试=Live updates and short video settings table failed, please try again
 退出成功=exit successfully
 用户名或密码错误=wrong user name or password
+会员邮箱或密码错误=wrong member email or password
 系统开小差了=System deserted
 账号已被冻结=Account has been frozen
 账号不可用=Account unavailable
@@ -2519,9 +2520,17 @@ pc端注册页面logo=
 榜单分类不存在=
 该邮箱已注册,请登录=This email has been registered, please log in
 邮件不能为空=Email cannot be empty
+验证码不能为空=Verification cannot be empty
+请勿频繁操作,请1分钟后重试=Please do not operate frequently, please retry in 1 minute
+验证码发送失败,请重试=Failed to send verification code, please try again
 发送验证邮件失败,请重试=Failed to send verification email, please try again
 发送验证邮件成功=Successfully sent the verification email
+验证码发送成功=Successfully sent the verification code
+验证码已过期,请重新获取=Verification code has expired, please get a new one
+验证码校验失败=Verification code verification failed
+验证码校验成功=Verification code verification successful
 注册链接不正确,请重新注册=The registration link is incorrect, please register again
+密码不一致,请重新输入=Passwords do not match. Please reenter
 会员邮箱不存在,请重新注册=Member email does not exist, please re register
 会员邮箱已激活,无需重复注册,请登录=Member email has been activated, no need to re register, please log in
 注册链接激活码已失效,请重新注册=The registration link activation code has expired, please re register
@@ -2537,6 +2546,7 @@ pc端注册页面logo=
 重置密码链接已失效,请重新获取=The reset password link has expired, please obtain it again
 亲爱的=Dear 
 感谢您申请注册=Thank you for applying for registration
+验证码=Verification code
 请点击或复制以下链接到浏览器激活账号=Please click or copy the following link to the browser to activate your account
 欢迎注册=Welcome to register 
 请验证您的邮箱=Please verify your email

+ 6 - 0
xinkeaboard-server/b2b2c-entity/src/main/java/com/slodon/b2b2c/member/pojo/Member.java

@@ -139,6 +139,12 @@ public class Member implements Serializable {
     @ApiModelProperty("主营业务")
     private String memberMainProduct;
 
+    @ApiModelProperty("国家")
+    private String country;
+
+    @ApiModelProperty("城市")
+    private String city;
+
     /**
      * 获取账户余额
      */

+ 14 - 0
xinkeaboard-server/b2b2c-entity/src/main/java/com/slodon/b2b2c/system/dto/CountryAreaApiDto.java

@@ -8,6 +8,8 @@ package com.slodon.b2b2c.system.dto;
 public class CountryAreaApiDto {
     private String countryZhCN;
     private String countryIsoCode;
+    private String cityZhCN;
+    private String cityIsoCode;
     private String subdivisionZhCN;
     private String subdivisionIsoCode;
     private String timeZone;
@@ -28,6 +30,18 @@ public class CountryAreaApiDto {
         this.countryIsoCode = countryIsoCode;
     }
 
+    public String getCityZhCN() {return cityZhCN;}
+
+    public void setCityZhCN(String cityZhCN) {
+        this.cityZhCN = cityZhCN;
+    }
+
+    public String getCityIsoCode() {return cityIsoCode;}
+
+    public void setCityIsoCode(String cityIsoCode) {
+        this.cityIsoCode = cityIsoCode;
+    }
+
     public String getSubdivisionZhCN() {
         return subdivisionZhCN;
     }

+ 15 - 1
xinkeaboard-server/b2b2c-web/pom.xml

@@ -78,7 +78,14 @@
         <dependency>
             <groupId>io.minio</groupId>
             <artifactId>minio</artifactId>
-            <version>3.0.10</version>
+<!--            <version>3.0.10</version>-->
+            <version>8.5.7</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>4.11.0</version>
         </dependency>
 
         <!--json-->
@@ -208,6 +215,13 @@
             <artifactId>xml-apis</artifactId>
             <version>1.4.01</version>
         </dependency>
+
+        <dependency>
+            <groupId>software.amazon.awssdk</groupId>
+            <artifactId>s3</artifactId>
+            <version>2.20.0</version>  <!-- 使用最新版本 -->
+        </dependency>
+
     </dependencies>
 
     <build>

+ 9 - 0
xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/controller/member/front/MemberInfoController.java

@@ -32,6 +32,7 @@ import com.slodon.b2b2c.model.msg.MemberReceiveModel;
 import com.slodon.b2b2c.model.promotion.CouponMemberModel;
 import com.slodon.b2b2c.msg.example.MemberReceiveExample;
 import com.slodon.b2b2c.promotion.example.CouponMemberExample;
+import com.slodon.b2b2c.system.dto.CountryAreaApiDto;
 import com.slodon.b2b2c.vo.member.MemberInfoVO;
 import com.slodon.b2b2c.vo.member.MemberVO;
 import io.swagger.annotations.Api;
@@ -171,6 +172,14 @@ public class MemberInfoController extends BaseController {
         if (StringUtil.isEmpty(memberInfoUpdateDTO.getMemberTrueName())) {
             memberNew.setMemberTrueName("");
         }
+
+        //实时查询会员信息
+        Member memberDb = memberModel.getMemberByMemberId(member.getMemberId());
+        if (!StringUtil.isEmpty(memberDb.getLastLoginIp())){
+            CountryAreaApiDto countryAreaApiDto = memberEnquiryModel.getCountryAndAreaByIp(memberDb.getLastLoginIp());
+            memberNew.setCountry(countryAreaApiDto.getCountryIsoCode());
+            memberNew.setCity(countryAreaApiDto.getCityIsoCode());
+        }
         memberModel.updateMember(memberNew);
         return SldResponse.success(Language.translate("编辑成功",Language.EN_LANGUAGE_TYPE));
     }

+ 198 - 1
xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/controller/member/front/advich/MemberEmailActiveController.java

@@ -22,7 +22,6 @@ import io.swagger.annotations.Api;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.util.CollectionUtils;
-import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
@@ -30,7 +29,10 @@ import org.springframework.web.bind.annotation.RestController;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @author cyan
@@ -62,6 +64,13 @@ public class MemberEmailActiveController extends BaseController {
     @Resource
     private VendorModel vendorModel;
 
+    private static final Map<Integer, String> EMAIL_KEY_MAP = new HashMap<>();
+
+    static {
+        EMAIL_KEY_MAP.put(1, RedisConst.SLD_PC_NEW_REGISTER_USER_EMAIL);
+        EMAIL_KEY_MAP.put(2, RedisConst.SLD_PC_FORGET_PWD_USER_EMAIL);
+    }
+
 //    /**
 //     * @param request
 //     * @param email
@@ -140,6 +149,194 @@ public class MemberEmailActiveController extends BaseController {
         return SldResponse.success(Language.translate("发送验证邮件成功", Language.EN_LANGUAGE_TYPE));
     }
 
+
+    /**
+     * 发送注册或忘记密码验证码
+     *
+     * @param request
+     * @param email
+     * @param source
+     * @param type
+     * @return
+     */
+    @PostMapping("/verification/code")
+    public JsonResult<Object> getMemberEmailVerificationCode(HttpServletRequest request, String email, Integer source, Integer type) {
+        log.info("getMemberEmailVerificationCode - email:{},source:{},type:{}", email, source, type);
+        if (StringUtil.isEmpty(email)) {
+            return SldResponse.fail(Language.translate("邮件不能为空", Language.EN_LANGUAGE_TYPE));
+        }
+        AssertUtil.emailCheck(email);
+
+        // 1. 判断是否在1分钟内已发送过验证码
+        String redisKey = RedisConst.SLD_PC_EMAIL_VERIFY_CODE_LIMIT + email + "::" + type;
+        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(redisKey))) {
+            return SldResponse.fail(Language.translate("请勿频繁操作,请1分钟后重试", Language.EN_LANGUAGE_TYPE));
+        }
+
+        int count;
+        if (type == 1) {
+            count = memberRegisterActiveModel.sendRegisterUserEmailVerificationCode(email, source);
+            AssertUtil.isTrue((count == 0), Language.translate("验证码发送失败,请重试", Language.EN_LANGUAGE_TYPE));
+            AssertUtil.isTrue((count == 2), Language.translate("该邮箱已注册,请登录", Language.EN_LANGUAGE_TYPE));
+        } else {
+            count = memberRegisterActiveModel.checkMemberUserInfo(email, type);
+            AssertUtil.isTrue((count == 0), Language.translate("验证码发送失败,请重试", Language.EN_LANGUAGE_TYPE));
+            AssertUtil.isTrue((count == 2), Language.translate("该邮箱未激活,请先激活", Language.EN_LANGUAGE_TYPE));
+            AssertUtil.isTrue((count == 3), Language.translate("该邮箱未注册,请先注册", Language.EN_LANGUAGE_TYPE));
+        }
+        // 2. 设置 Redis 标记,1分钟内不可重复发送
+        stringRedisTemplate.opsForValue().set(redisKey, "1", 60, TimeUnit.SECONDS);
+        return SldResponse.success(Language.translate("验证码发送成功", Language.EN_LANGUAGE_TYPE));
+
+    }
+
+
+    /**
+     * 校验注册或忘记密码验证码
+     *
+     * @param request
+     * @param email
+     * @param type
+     * @param verificationCode
+     * @return
+     */
+    @PostMapping("/check/verification/code")
+    public JsonResult<Object> checkMemberEmailVerificationCode(HttpServletRequest request, String email, Integer type, String verificationCode) {
+        log.info("checkMemberEmailVerificationCode - email:{},type:{},verificationCode:{}", email, type, verificationCode);
+        if (StringUtil.isEmpty(email)) {
+            return SldResponse.fail(Language.translate("邮件不能为空", Language.EN_LANGUAGE_TYPE));
+        }
+        if (StringUtil.isEmpty(verificationCode)) {
+            return SldResponse.fail(Language.translate("验证码不能为空", Language.EN_LANGUAGE_TYPE));
+        }
+        AssertUtil.emailCheck(email);
+
+        String baseKey = EMAIL_KEY_MAP.get(type);
+        if (baseKey == null) {
+            return SldResponse.fail(Language.translate("验证码不能为空", Language.EN_LANGUAGE_TYPE));
+        }
+
+        String redisKey = baseKey + email;
+        if (!stringRedisTemplate.hasKey(redisKey)) {
+            return SldResponse.fail(Language.translate("验证码已过期,请重新获取", Language.EN_LANGUAGE_TYPE));
+        }
+
+        String storedCode = stringRedisTemplate.opsForValue().get(redisKey);
+        if (!verificationCode.equals(storedCode)) {
+            return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("验证码校验失败", Language.EN_LANGUAGE_TYPE));
+        }
+        stringRedisTemplate.delete(redisKey);
+        return SldResponse.success(Language.translate("验证码校验成功", Language.EN_LANGUAGE_TYPE));
+    }
+
+
+    /**
+     * 注册
+     * @param request
+     * @param email
+     * @param nickName
+     * @param password
+     * @param confirmPassword
+     * @return
+     */
+    @PostMapping("/register")
+    public JsonResult<Object> activeMemberEmailInfo(HttpServletRequest request, String email, String nickName, String password,String confirmPassword) {
+        log.info("activeMemberEmailInfo - email:{},nickName:{},password:{},confirmPassword:{}", email, nickName, password,confirmPassword);
+        if (StringUtil.isEmpty(email)) {
+            return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("邮件不能为空", Language.EN_LANGUAGE_TYPE));
+        }
+        if(!password.equals(confirmPassword)){
+            return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("密码不一致,请重新输入", Language.EN_LANGUAGE_TYPE));
+        }
+        AssertUtil.passwordCheck(password);
+        MemberExample memberExample = new MemberExample();
+        memberExample.setMemberEmail(email.toLowerCase());
+        List<Member> memberList = memberReadMapper.listByExample(memberExample);
+        if (CollectionUtils.isEmpty(memberList)) {
+            return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("会员邮箱不存在,请重新注册", Language.EN_LANGUAGE_TYPE));
+        }
+
+        Member memberInfo = memberList.get(0);
+        if (memberInfo.getIsEmailActive() != null && memberInfo.getIsEmailActive() == 1) {
+            return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("会员邮箱已激活,无需重复注册,请登录", Language.EN_LANGUAGE_TYPE));
+        }
+
+        // 更新用户邮箱激活状态
+        Member memberNew = new Member();
+        memberNew.setMemberId(memberInfo.getMemberId());
+        memberNew.setIsEmailActive(1);
+        memberNew.setUpdateTime(new Date());
+        memberNew.setMemberNickName(nickName);
+        memberNew.setLoginPwd(Md5.getMd5String(password));
+        memberModel.updateMember(memberNew);
+
+        // 发送账户注册成功邮件
+        memberRegisterActiveModel.sendRegisterSuccessEmail(email, nickName);
+        return SldResponse.success(Language.translate("会员邮箱账户激活成功", Language.EN_LANGUAGE_TYPE));
+    }
+
+
+
+    /**
+     * 用户邮箱重置忘记密码
+     *
+     * @param request
+     * @param email
+     * @param verificationCode
+     * @param loginPwd
+     * @param confirmPassWord
+     * @return
+     */
+    @PostMapping("/email/reset/pwdNew")
+    public JsonResult<Object> emailResetPwdNew(HttpServletRequest request, String email, String verificationCode, String loginPwd, String confirmPassWord) {
+        log.info("emailResetPwd - email:{},verificationCode:{},loginPwd:{},confirmPassWord:{}", email, verificationCode, loginPwd, confirmPassWord);
+        if (StringUtil.isEmpty(email) ) {
+            return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("邮件不能为空", Language.EN_LANGUAGE_TYPE));
+        }
+        MemberExample memberExample = new MemberExample();
+        memberExample.setMemberEmail(email.toLowerCase());
+        List<Member> memberList = memberReadMapper.listByExample(memberExample);
+
+        if (CollectionUtils.isEmpty(memberList)) {
+            return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("该邮箱不存在,请先注册", Language.EN_LANGUAGE_TYPE));
+        }
+
+        Member memberInfo = memberList.get(0);
+        if (memberInfo.getIsEmailActive() != null && memberInfo.getIsEmailActive() == 0) {
+            return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("该邮箱未激活,请先激活", Language.EN_LANGUAGE_TYPE));
+        }
+
+        if(!loginPwd.equals(confirmPassWord)){
+            return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("密码不一致,请重新输入", Language.EN_LANGUAGE_TYPE));
+        }
+
+        AssertUtil.passwordCheck(loginPwd);
+
+        String forgetPwdEmailKey = RedisConst.SLD_PC_FORGET_PWD_USER_EMAIL + email;
+        log.info("forgetPwdEmailKey:{}", forgetPwdEmailKey);
+        if (stringRedisTemplate.hasKey(forgetPwdEmailKey)) {
+            String verifyNumber = stringRedisTemplate.opsForValue().get(forgetPwdEmailKey);
+            log.info("verifyNumber:{}", verifyNumber);
+            if (!verificationCode.equals(verifyNumber)) {
+                return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("验证码校验失败", Language.EN_LANGUAGE_TYPE));
+            }
+
+            // 更新用户密码
+            Member memberNew = new Member();
+            memberNew.setMemberId(memberInfo.getMemberId());
+            memberNew.setLoginPwd(Md5.getMd5String(loginPwd));
+            memberNew.setUpdateTime(new Date());
+            memberModel.updateMember(memberNew);
+
+            stringRedisTemplate.delete(forgetPwdEmailKey);
+
+            return SldResponse.success(Language.translate("重置登录密码成功", Language.EN_LANGUAGE_TYPE));
+        }
+        return SldResponse.fail(ResponseConst.STATE_FAIL, Language.translate("验证码已过期,请重新获取", Language.EN_LANGUAGE_TYPE));
+    }
+
+
+
     /**
      * 用户邮箱链接激活
      *

+ 1 - 1
xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/controller/sso/front/FrontAuthController.java

@@ -118,7 +118,7 @@ public class FrontAuthController {
                     // 检测gp系统用户是否存在
                     GpUserInfoDto gpUserInfoDto = oneClickLoginModel.checkGpSystemUserIsExist(request, username, password, "PC");
                     if (gpUserInfoDto == null) {
-                        AssertUtil.notEmpty(memberList, Language.translate("用户名或密码错误",Language.EN_LANGUAGE_TYPE));
+                        AssertUtil.notEmpty(memberList, Language.translate("会员邮箱或密码错误",Language.EN_LANGUAGE_TYPE));
                         AssertUtil.isTrue(!memberList.get(0).getLoginPwd().equals(Md5.getMd5String(password)), Language.translate("用户名或密码错误",Language.EN_LANGUAGE_TYPE));
                     } else {
                         memberList = memberModel.getMemberList(memberExample, null);

+ 7 - 0
xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/model/member/MemberProductLookLogModel.java

@@ -3,6 +3,7 @@ package com.slodon.b2b2c.model.member;
 import com.slodon.b2b2c.core.exception.MallException;
 import com.slodon.b2b2c.core.i18n.Language;
 import com.slodon.b2b2c.core.response.PagerInfo;
+import com.slodon.b2b2c.core.util.AssertUtil;
 import com.slodon.b2b2c.core.util.StringUtil;
 import com.slodon.b2b2c.dao.read.goods.GoodsReadMapper;
 import com.slodon.b2b2c.dao.read.member.MemberProductLookLogReadMapper;
@@ -16,6 +17,7 @@ import com.slodon.b2b2c.member.example.MemberProductLookLogExample;
 import com.slodon.b2b2c.member.pojo.MemberFollowProduct;
 import com.slodon.b2b2c.member.pojo.MemberProductLookLog;
 import com.slodon.b2b2c.model.goods.GoodsExtendModel;
+import com.slodon.b2b2c.model.goods.GoodsModel;
 import com.slodon.b2b2c.vo.member.ProductLookLogVO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
@@ -45,6 +47,8 @@ public class MemberProductLookLogModel {
     private MemberFollowProductModel memberFollowProductModel;
     @Resource
     private GoodsExtendModel goodsExtendModel;
+    @Resource
+    private GoodsModel goodsModel;
 
 
     /**
@@ -217,6 +221,9 @@ public class MemberProductLookLogModel {
                     if (!CollectionUtils.isEmpty(followProductList)) {
                         productLookLogInfo.setIsFollowProduct(true);
                     }
+                    Goods goods = goodsModel.getGoodsByGoodsId(productLookLog.getGoodsId());
+                    AssertUtil.notNull(goods, Language.translate("查询的商品信息为空",Language.EN_LANGUAGE_TYPE));
+                    productLookLogInfo.setState(goods.getState());
                     infos.add(productLookLogInfo);
                 });
                 vo.setProductLookLogInfoList(infos);

+ 10 - 2
xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/model/member/advich/MemberEnquiryModel.java

@@ -6,6 +6,7 @@ import com.maxmind.db.CHMCache;
 import com.maxmind.geoip2.DatabaseReader;
 import com.maxmind.geoip2.exception.GeoIp2Exception;
 import com.maxmind.geoip2.model.CityResponse;
+import com.maxmind.geoip2.record.City;
 import com.maxmind.geoip2.record.Country;
 import com.maxmind.geoip2.record.Location;
 import com.maxmind.geoip2.record.Subdivision;
@@ -135,10 +136,17 @@ public class MemberEnquiryModel {
             if ("台湾".equals(country.getNames().get("zh-CN"))) {
                 dto.setCountryZhCN("中国台湾");
             }
+            if ("中华民国".equals(country.getNames().get("zh-CN"))) {
+                dto.setCountryZhCN("中国台湾");
+            }
             if ("澳门".equals(country.getNames().get("zh-CN"))) {
                 dto.setCountryZhCN("中国澳门");
             }
             dto.setCountryIsoCode(country.getIsoCode());
+            //城市
+            City city = response.getCity();
+            dto.setCityZhCN(city.getNames().get("zh-CN"));
+            dto.setCityIsoCode(StringUtil.isEmpty(city.getName()) ? country.getIsoCode() : city.getName());
             //省份
             Subdivision subdivision = response.getMostSpecificSubdivision();
             dto.setSubdivisionIsoCode(subdivision.getIsoCode());
@@ -597,10 +605,10 @@ public class MemberEnquiryModel {
                 try {
                     String[] siteParam = {siteName};
                     //定义参数值
-                    if(StringUtil.isEmpty(enquiry.getPhone())){
+                    if (StringUtil.isEmpty(enquiry.getPhone())) {
                         enquiry.setPhoneCode("");
                     }
-                    String[] templateArray = {enquiry.getName(), enquiry.getEmail(), enquiry.getCountry(),enquiry.getPhoneCode(), enquiry.getPhone(), enquiry.getQuantity() == null ? "-" : String.valueOf(enquiry.getQuantity()), enquiry.getMessage(), enquiry.getPageUrl()};
+                    String[] templateArray = {enquiry.getName(), enquiry.getEmail(), enquiry.getCountry(), enquiry.getPhoneCode(), enquiry.getPhone(), enquiry.getQuantity() == null ? "-" : String.valueOf(enquiry.getQuantity()), enquiry.getMessage(), enquiry.getPageUrl()};
                     sendHtmlMail(storeTpl.getEmailContent(), tos, siteParam, templateArray, pattern);
                 } catch (Exception e) {
                     log.error("[sendEnquiryInfo][发送邮件]:", e);

+ 86 - 0
xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/model/member/advich/MemberRegisterActiveModel.java

@@ -5,6 +5,7 @@ import com.slodon.b2b2c.captcha.work.Producer;
 import com.slodon.b2b2c.core.config.DomainUrlUtil;
 import com.slodon.b2b2c.core.constant.RedisConst;
 import com.slodon.b2b2c.core.i18n.Language;
+import com.slodon.b2b2c.core.random.RandomUtil;
 import com.slodon.b2b2c.core.uid.GoodsIdGenerator;
 import com.slodon.b2b2c.core.util.StringUtil;
 import com.slodon.b2b2c.dao.read.member.MemberReadMapper;
@@ -269,4 +270,89 @@ public class MemberRegisterActiveModel {
         String subject = Language.translate("注册成功", Language.EN_LANGUAGE_TYPE) + " " + pcDomainUrl.toUpperCase();
         msgSendModel.sendHtmlMail(email, subject, emailMsgTpl.toString());
     }
+
+    public Integer sendRegisterUserEmailVerificationCode(String email, Integer source) {
+        int res = 0;
+        MemberExample example = new MemberExample();
+        example.setMemberEmail(email.toLowerCase());
+        List<Member> memberList = memberReadMapper.listByExample(example);
+        if (CollectionUtil.isNotEmpty(memberList)) {
+            Member memberInfo = memberList.get(0);
+            if (memberInfo.getIsEmailActive() == 0) {
+                sendEmailRegisterVerificationCode(email);
+                res = 1;
+            } else {
+                res = 2;
+            }
+        } else {
+            res = 1;
+            sendEmailRegisterVerificationCode(email);
+            saveEmailUserInfo(email, source);
+        }
+        return res;
+    }
+
+    private void sendEmailRegisterVerificationCode(String email) {
+        String pcDomainUrl = DomainUrlUtil.SLD_PC_URL.replace("http://", "").replace("https://", "");
+        String verifCode = "";
+        //邮件验证码过期时间,单位秒
+        long expireTime = RedisConst.REGISTER_USER_AND_FORGETPWD_USER_EMAIL_VERIFICATION_CODE_EXPIRE_TIME;
+
+        //将随机数存在redis中
+        String activeEmailKey = RedisConst.SLD_PC_NEW_REGISTER_USER_EMAIL + email;
+        verifCode = RandomUtil.randomNumber(4);
+        stringRedisTemplate.opsForValue().set(activeEmailKey, verifCode, expireTime, TimeUnit.SECONDS);
+
+        StringBuffer emailMsgTpl = new StringBuffer();
+        emailMsgTpl.append(Language.translate("感谢您申请注册", Language.EN_LANGUAGE_TYPE) + "\r\n");
+        emailMsgTpl.append(Language.translate("验证码", Language.EN_LANGUAGE_TYPE));
+        emailMsgTpl.append(":" + verifCode);
+
+        String subject = Language.translate("欢迎注册", Language.EN_LANGUAGE_TYPE) + pcDomainUrl.toUpperCase() + "," + Language.translate("请验证您的邮箱", Language.EN_LANGUAGE_TYPE);
+        msgSendModel.sendHtmlMail(email, subject, emailMsgTpl.toString());
+    }
+
+    public Integer checkMemberUserInfo(String email, Integer type) {
+        int res = 0;
+        MemberExample memberExample = new MemberExample();
+        memberExample.setMemberEmail(email);
+        List<Member> memberList = memberReadMapper.listByExample(memberExample);
+        if (CollectionUtil.isNotEmpty(memberList)) {
+            Member memberInfo = memberList.get(0);
+            if (memberInfo.getIsEmailActive() == 1) {
+                sendEmailForgetPasswordVerificationCode(email, memberInfo.getMemberNickName());
+                res = 1;
+            } else {
+                res = 2;
+            }
+        } else {
+            res = 3;
+        }
+        return res;
+    }
+
+    private void sendEmailForgetPasswordVerificationCode(String email, String nickName) {
+        String pcDomainUrl = DomainUrlUtil.SLD_PC_URL.replace("http://", "").replace("https://", "");
+        String verifCode = "";
+        //重置密码验证码过期时间,单位秒
+        long expireTime = RedisConst.REGISTER_USER_AND_FORGETPWD_USER_EMAIL_VERIFICATION_CODE_EXPIRE_TIME;
+
+        //将随机数存在redis中
+        String forgetPwdEmailKey = RedisConst.SLD_PC_FORGET_PWD_USER_EMAIL + email;
+        verifCode = RandomUtil.randomNumber(4);
+        stringRedisTemplate.opsForValue().set(forgetPwdEmailKey, verifCode, expireTime, TimeUnit.SECONDS);
+
+        StringBuffer emailMsgTpl = new StringBuffer(Language.translate("亲爱的", Language.EN_LANGUAGE_TYPE));
+        emailMsgTpl.append(StringUtil.isEmpty(nickName) ? email : nickName + ":" + "<br/>");
+        emailMsgTpl.append(Language.translate("验证码", Language.EN_LANGUAGE_TYPE));
+        emailMsgTpl.append(":" + verifCode);
+        emailMsgTpl.append("<br/>");
+        emailMsgTpl.append(Language.translate("如果您没有请求新密码,请忽略此邮件。", Language.EN_LANGUAGE_TYPE) + "<br/>");
+        emailMsgTpl.append(Language.translate("这是系统邮件,请不要回复。", Language.EN_LANGUAGE_TYPE) + "<br/>");
+        emailMsgTpl.append("<br/>");
+        emailMsgTpl.append(Language.translate("最好的问候", Language.EN_LANGUAGE_TYPE) + "<br/>");
+        emailMsgTpl.append(pcDomainUrl);
+        String subject = Language.translate("重设您的", Language.EN_LANGUAGE_TYPE) + pcDomainUrl.toUpperCase() + " " + Language.translate("帐户密码", Language.EN_LANGUAGE_TYPE);
+        msgSendModel.sendHtmlMail(email, subject, emailMsgTpl.toString());
+    }
 }

+ 34 - 25
xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/upload/MinioUpload.java

@@ -3,17 +3,10 @@ package com.slodon.b2b2c.upload;
 import com.slodon.b2b2c.core.config.DomainUrlUtil;
 import com.slodon.b2b2c.core.exception.MallException;
 import com.slodon.b2b2c.upload.base.Upload;
-import io.minio.MinioClient;
-import io.minio.errors.*;
-import io.minio.policy.PolicyType;
+import io.minio.*;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.web.multipart.MultipartFile;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
 
 /**
  * minio上传文件
@@ -36,32 +29,48 @@ public class MinioUpload extends Upload {
         //获取配置
         String minioUrl = stringRedisTemplate.opsForValue().get("minio_url");//minio地址
         int port = Integer.parseInt(stringRedisTemplate.opsForValue().get("minio_port"));//minio端口
+        String minioS3Region = stringRedisTemplate.opsForValue().get("minio_s3_region");//兼容S3时需要设置
         String accessKey = stringRedisTemplate.opsForValue().get("minio_access_key");
         String secretKey = stringRedisTemplate.opsForValue().get("minio_secret_key");
+        String bucketName = stringRedisTemplate.opsForValue().get("minio_bucket_name");//存储桶名称
 
         //从fileName中获取bucketName和objectName
-        String reg = "/(\\S*?)(/\\S*)";
-        String bucketName = fileName.replaceFirst(reg, "$1");
-        String objectName = fileName.replaceFirst(reg, "$2");
+//        String reg = "/(\\S*?)(/\\S*)";
+//        String folderName = fileName.replaceFirst(reg, "$1");
+//        //String objectName = fileName.replaceFirst(reg, "$2");
 
-        //调用api上传文件
-        MinioClient minioClient = null;
         try {
-            minioClient = new MinioClient(minioUrl + ":" + port, accessKey, secretKey);
+            // 1. 配置 MinIO/S3 客户端
+            MinioClient minioClient = MinioClient.builder()
+                    .endpoint(minioUrl, port, true)
+                    // 替换为您的MinIO服务器地址
+                    .credentials(accessKey, secretKey) // 您的访问密钥和秘密密钥
+                    .region(minioS3Region) // 设置区域
+                    .build();
             //查询桶是否已存在,不存在则创建桶
-            boolean bucketExists = minioClient.bucketExists(bucketName);
-            if (!bucketExists) {
-                minioClient.makeBucket(bucketName);
-                //创建安全级别
-                minioClient.setBucketPolicy(bucketName, "*", PolicyType.READ_ONLY);
+            // 2. 检查桶是否存在,不存在则创建
+            boolean found = minioClient.bucketExists(BucketExistsArgs.builder()
+                    .bucket(bucketName)
+                    .build());
+
+            if (!found) {
+                log.info("Bucket does not exist, creating new bucket:{}", bucketName);
+                minioClient.makeBucket(MakeBucketArgs.builder()
+                        .bucket(bucketName)
+                        .region(minioS3Region)
+                        .build());
             }
+
             //上传文件
-            minioClient.putObject(bucketName, objectName, file.getInputStream(), file.getContentType());
-        } catch (InvalidEndpointException | InvalidPortException | InvalidObjectPrefixException |
-                InvalidKeyException | NoSuchAlgorithmException | NoResponseException |
-                XmlPullParserException | InvalidBucketNameException | InvalidArgumentException |
-                RegionConflictException | InsufficientDataException | ErrorResponseException |
-                InternalException | IOException e) {
+            log.info("Uploading file to bucket...");
+            minioClient.putObject(PutObjectArgs.builder()
+                    .bucket(bucketName)
+                    .object(fileName)
+                    .stream(file.getInputStream(), file.getSize(), -1)
+                    .contentType(file.getContentType())
+                    .build());
+            log.info("File uploaded successfully");
+        } catch (Exception e) {
             log.error("上传文件失败", e);
             throw new MallException("上传文件失败");
         }

+ 4 - 0
xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/vo/member/FollowProductVO.java

@@ -63,6 +63,9 @@ public class FollowProductVO implements Serializable {
     @ApiModelProperty("商品自定义价格")
     private String goodsMoney;
 
+    @ApiModelProperty("商品状态:11-放入仓库无需审核;12-放入仓库审核通过;20-立即上架待审核;21-放入仓库待审核;3-上架(a. 审核通过上架,b. 不需要平台审核,商户创建商品后点击上架操作);4-审核驳回(平台驳回);5-商品下架(商户自行下架);6-违规下架(平台违规下架操作);7-已删除(状态1、5、6可以删除后进入此状态)")
+    private Integer state;
+
     public FollowProductVO(MemberFollowProduct memberFollowProduct, Store store, Goods goods) {
         followId = memberFollowProduct.getFollowId();
         productId = memberFollowProduct.getProductId();
@@ -78,6 +81,7 @@ public class FollowProductVO implements Serializable {
         isOwnStoreValue = dealIsOwnStoreValue(store.getIsOwnStore());
         salesNum = goods.getVirtualSales() + goods.getActualSales();
         goodsMoney = goods.getGoodsMoney();
+        state = goods.getState();
     }
 
     public static String dealIsOwnStoreValue(Integer isOwnStore) {

+ 3 - 0
xinkeaboard-server/b2b2c-web/src/main/java/com/slodon/b2b2c/vo/member/ProductLookLogVO.java

@@ -74,6 +74,9 @@ public class ProductLookLogVO implements Serializable {
         @ApiModelProperty("是否收藏商品:false 未收藏, true 收藏")
         private Boolean isFollowProduct;
 
+        @ApiModelProperty("商品状态:11-放入仓库无需审核;12-放入仓库审核通过;20-立即上架待审核;21-放入仓库待审核;3-上架(a. 审核通过上架,b. 不需要平台审核,商户创建商品后点击上架操作);4-审核驳回(平台驳回);5-商品下架(商户自行下架);6-违规下架(平台违规下架操作);7-已删除(状态1、5、6可以删除后进入此状态)")
+        private Integer state;
+
         public ProductLookLogInfo(MemberProductLookLog memberProductLookLog) {
             logId = memberProductLookLog.getLogId();
             memberId = memberProductLookLog.getMemberId();

+ 55 - 0
xinkeaboard-server/b2b2c-web/src/main/resources/application-test.yml

@@ -0,0 +1,55 @@
+server:
+  port: 8001
+  compression:
+    enabled: true
+    min-response-size: 1024
+    mime-types: application/json
+spring:
+  application:
+    name: b2b2c-web
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+    time-zone: GMT+8
+  servlet:
+    multipart:
+      max-file-size: 50MB
+      max-request-size: 50MB
+  data:
+    mongodb:
+      uri: mongodb://dev:oJ6mZ2fY6aL3fS8yO8wF@54.46.9.88:27017/b2b2c
+logging:
+  file:
+    name: ./log/${spring.application.name}.log
+  level:
+    com.slodon.b2b2c: debug
+slodon:
+  b2b2c:
+    datasource:
+      write:
+        url: jdbc:mysql://54.46.9.88:27234/xinkeaboard_dev?useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
+      read:
+        url: jdbc:mysql://54.46.9.88:27234/xinkeaboard_dev?useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
+socketio:
+  # 客服模块socket配置
+  im:
+    host: 0.0.0.0    #监听的ip
+    port: 8112        #监听端口
+    # 设置最大每帧处理数据的长度,防止他人利用大数据来攻击服务器    maxFramePayloadLength: 1048576
+    # 设置http交互最大内容长度
+    maxHttpContentLength: 1048576
+    # socket连接数大小(如只监听一个端口boss线程组为1即可)
+    bossCount: 1
+    workCount: 100
+    allowCustomRequests: true
+    # 协议升级超时时间(毫秒),默认10秒。HTTP握手升级为ws协议超时时间
+    upgradeTimeout: 1000000
+    # Ping消息超时时间(毫秒),默认60秒,这个时间间隔内没有接收到心跳消息就会发送超时事件
+    pingTimeout: 6000000
+    # Ping消息间隔(毫秒),默认25秒。客户端向服务器发送一条心跳消息间隔
+    pingInterval: 25000
+
+#geo地址
+geoip:
+  static:
+    city:
+      mmdb: /data/GeoLite2/GeoLite2-City.mmdb