|  | @@ -4,6 +4,8 @@ import cn.hutool.core.collection.CollUtil;
 | 
	
		
			
				|  |  |  import cn.hutool.core.collection.ListUtil;
 | 
	
		
			
				|  |  |  import cn.hutool.core.lang.Assert;
 | 
	
		
			
				|  |  |  import cn.hutool.core.util.*;
 | 
	
		
			
				|  |  | +import cn.hutool.http.HttpUtil;
 | 
	
		
			
				|  |  | +import cn.hutool.json.JSONUtil;
 | 
	
		
			
				|  |  |  import cn.newfeifan.mall.framework.common.enums.CommonStatusEnum;
 | 
	
		
			
				|  |  |  import cn.newfeifan.mall.framework.common.enums.UserTypeEnum;
 | 
	
		
			
				|  |  |  import cn.newfeifan.mall.framework.common.exception.ErrorCode;
 | 
	
	
		
			
				|  | @@ -23,10 +25,14 @@ import cn.newfeifan.mall.module.system.api.sms.SmsCodeApi;
 | 
	
		
			
				|  |  |  import cn.newfeifan.mall.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
 | 
	
		
			
				|  |  |  import cn.newfeifan.mall.module.system.api.social.SocialClientApi;
 | 
	
		
			
				|  |  |  import cn.newfeifan.mall.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
 | 
	
		
			
				|  |  | +import cn.newfeifan.mall.module.system.dal.dataobject.social.SocialUserDO;
 | 
	
		
			
				|  |  | +import cn.newfeifan.mall.module.system.dal.mysql.social.SocialUserMapper;
 | 
	
		
			
				|  |  |  import cn.newfeifan.mall.module.system.enums.sms.SmsSceneEnum;
 | 
	
		
			
				|  |  |  import cn.newfeifan.mall.module.system.service.user.AdminUserService;
 | 
	
		
			
				|  |  |  import com.google.common.annotations.VisibleForTesting;
 | 
	
		
			
				|  |  |  import lombok.extern.slf4j.Slf4j;
 | 
	
		
			
				|  |  | +import org.springframework.beans.factory.annotation.Value;
 | 
	
		
			
				|  |  | +import org.springframework.data.redis.core.StringRedisTemplate;
 | 
	
		
			
				|  |  |  import org.springframework.security.crypto.password.PasswordEncoder;
 | 
	
		
			
				|  |  |  import org.springframework.stereotype.Service;
 | 
	
		
			
				|  |  |  import org.springframework.transaction.annotation.Transactional;
 | 
	
	
		
			
				|  | @@ -41,8 +47,10 @@ import java.util.List;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import static cn.newfeifan.mall.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
	
		
			
				|  |  |  import static cn.newfeifan.mall.framework.common.util.servlet.ServletUtils.getClientIP;
 | 
	
		
			
				|  |  | +import static cn.newfeifan.mall.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 | 
	
		
			
				|  |  |  import static cn.newfeifan.mall.module.member.enums.DictTypeConstants.*;
 | 
	
		
			
				|  |  |  import static cn.newfeifan.mall.module.member.enums.ErrorCodeConstants.*;
 | 
	
		
			
				|  |  | +import static cn.newfeifan.mall.module.system.constant.SystemConstants.WX_TICK_KEY_APPID;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * 会员 User Service 实现类
 | 
	
	
		
			
				|  | @@ -75,6 +83,14 @@ public class MemberUserServiceImpl implements MemberUserService {
 | 
	
		
			
				|  |  |      private ShareLinkRegisterProducer shareLinkRegisterProducer;
 | 
	
		
			
				|  |  |      @Resource
 | 
	
		
			
				|  |  |      private AdminUserService adminUserService;
 | 
	
		
			
				|  |  | +    @Resource
 | 
	
		
			
				|  |  | +    private SocialUserMapper socialUserMapper;
 | 
	
		
			
				|  |  | +    @Resource
 | 
	
		
			
				|  |  | +    private StringRedisTemplate stringRedisTemplate;
 | 
	
		
			
				|  |  | +    @Value("${wx.mp.app-id}")
 | 
	
		
			
				|  |  | +    private String appid;
 | 
	
		
			
				|  |  | +    @Value("${wx.mp.secret}")
 | 
	
		
			
				|  |  | +    private String Wxgsecret;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public MemberUserDO getUserByMobile(String mobile) {
 | 
	
	
		
			
				|  | @@ -182,9 +198,9 @@ public class MemberUserServiceImpl implements MemberUserService {
 | 
	
		
			
				|  |  |          MemberUserDO user = memberUserMapper.selectById(userId);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          // 如果用户名被修改,则校验是否被有同名
 | 
	
		
			
				|  |  | -        if(reqVO.getUsername() != null && !reqVO.getUsername().equals(user.getUsername())){
 | 
	
		
			
				|  |  | +        if (reqVO.getUsername() != null && !reqVO.getUsername().equals(user.getUsername())) {
 | 
	
		
			
				|  |  |              Long count = selectCountByUsername(reqVO.getUsername());
 | 
	
		
			
				|  |  | -            if(count > 0){
 | 
	
		
			
				|  |  | +            if (count > 0) {
 | 
	
		
			
				|  |  |                  throw exception(AUTH_USERNAME_EXISTS);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -401,16 +417,16 @@ public class MemberUserServiceImpl implements MemberUserService {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public MemberUserDO getUserByMobile(String mobile, Long userId) {
 | 
	
		
			
				|  |  | -        return memberUserMapper.selectOne(MemberUserDO::getMobile, mobile, MemberUserDO::getId,userId);
 | 
	
		
			
				|  |  | +        return memberUserMapper.selectOne(MemberUserDO::getMobile, mobile, MemberUserDO::getId, userId);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public void updateUserByAlipayAccount(Long loginUserId, AppMemberUserUpdateAlipayAccountReqVO reqVO) {
 | 
	
		
			
				|  |  | -        if(isValidPhoneNumber(reqVO.getAlipayAccount()) || validateEmail(reqVO.getAlipayAccount())){
 | 
	
		
			
				|  |  | +        if (isValidPhoneNumber(reqVO.getAlipayAccount()) || validateEmail(reqVO.getAlipayAccount())) {
 | 
	
		
			
				|  |  |              MemberUserDO user = BeanUtils.toBean(reqVO, MemberUserDO.class);
 | 
	
		
			
				|  |  |              user.setId(loginUserId);
 | 
	
		
			
				|  |  |              memberUserMapper.updateById(user);
 | 
	
		
			
				|  |  | -        }else{
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  |              ErrorCode ALIPAY_ACCOUNT_ERROR = new ErrorCode(1_004_001_000, "支付宝账号格式错误");
 | 
	
		
			
				|  |  |              throw exception(ALIPAY_ACCOUNT_ERROR);
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -418,7 +434,7 @@ public class MemberUserServiceImpl implements MemberUserService {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public void updateUserByBankAccount(Long loginUserId, AppMemberUserUpdateBankAccountReqVO reqVO) {
 | 
	
		
			
				|  |  | -        if(!isAccountFormatValid(reqVO.getBankAccount())){
 | 
	
		
			
				|  |  | +        if (!isAccountFormatValid(reqVO.getBankAccount())) {
 | 
	
		
			
				|  |  |              ErrorCode BANK_COUNT_ERROR = new ErrorCode(1_004_001_000, "银行账号格式错误");
 | 
	
		
			
				|  |  |              throw exception(BANK_COUNT_ERROR);
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -430,12 +446,87 @@ public class MemberUserServiceImpl implements MemberUserService {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public List<MemberUserDO> selectListByMobile(String mobile) {
 | 
	
		
			
				|  |  | -        return memberUserMapper.selectList(MemberUserDO::getMobile,mobile);
 | 
	
		
			
				|  |  | +        return memberUserMapper.selectList(MemberUserDO::getMobile, mobile);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public MemberUserDO getUserByUserNameWithMobile(String username, String mobile) {
 | 
	
		
			
				|  |  | -        return memberUserMapper.selectOne(MemberUserDO::getUsername,username,MemberUserDO::getMobile,mobile);
 | 
	
		
			
				|  |  | +        return memberUserMapper.selectOne(MemberUserDO::getUsername, username, MemberUserDO::getMobile, mobile);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public void addRegisterId(String registerId) {
 | 
	
		
			
				|  |  | +        MemberUserDO memberUserDO = memberUserMapper.selectById(getLoginUserId());
 | 
	
		
			
				|  |  | +        if (registerId != null && !registerId.equals(memberUserDO.getAndroidRegisterId())) {
 | 
	
		
			
				|  |  | +            // 防止一台设备登录多个账号会导致异常发送的可能,所以如果有同样的registerId就先删除后加入
 | 
	
		
			
				|  |  | +            List<MemberUserDO> users = memberUserMapper.selectList(MemberUserDO::getAndroidRegisterId, registerId);
 | 
	
		
			
				|  |  | +            if(users != null){
 | 
	
		
			
				|  |  | +                for (MemberUserDO user : users) {
 | 
	
		
			
				|  |  | +                    user.setAndroidRegisterId(null);
 | 
	
		
			
				|  |  | +                    memberUserMapper.updateById(user);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            memberUserDO.setAndroidRegisterId(registerId);
 | 
	
		
			
				|  |  | +            memberUserMapper.updateById(memberUserDO);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public Boolean getUserIsSubscribe(Long userId) {
 | 
	
		
			
				|  |  | +        SocialUserDO socialUserDO = socialUserMapper.selectOne(SocialUserDO::getCreator, getLoginUserId());
 | 
	
		
			
				|  |  | +        // 如果没有存储用户的openid就直接返回false
 | 
	
		
			
				|  |  | +        if(socialUserDO == null){
 | 
	
		
			
				|  |  | +            return false;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return isSubscribe(socialUserDO.getOpenid());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private Boolean isSubscribe(String openid) {
 | 
	
		
			
				|  |  | +        String requestUrl = StrUtil.format("https://api.weixin.qq.com/cgi-bin/user/info?access_token={}&openid={}&lang=zh_CN", getAccessToken(), openid);
 | 
	
		
			
				|  |  | +        String returnMsg = HttpUtil.get(requestUrl);
 | 
	
		
			
				|  |  | +        cn.hutool.json.JSONObject responseJsonObject = JSONUtil.parseObj(returnMsg);
 | 
	
		
			
				|  |  | +        if (ObjectUtil.isNull(responseJsonObject)) try {
 | 
	
		
			
				|  |  | +            throw new Exception("响应异常:获取信息为空!");
 | 
	
		
			
				|  |  | +        }catch (Exception e) {
 | 
	
		
			
				|  |  | +            throw new RuntimeException(e);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        Integer subscribe = (Integer) responseJsonObject.get("subscribe");
 | 
	
		
			
				|  |  | +        log.info("用户信息:{}", responseJsonObject);
 | 
	
		
			
				|  |  | +        // 微信返回的数据中1为关注,0为未关注
 | 
	
		
			
				|  |  | +        return subscribe == 1;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 获取accessToken
 | 
	
		
			
				|  |  | +     *
 | 
	
		
			
				|  |  | +     * @return accessToken
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private String getAccessToken() {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        String accessToken = stringRedisTemplate.opsForValue().get(WX_TICK_KEY_APPID);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (StrUtil.isEmpty(accessToken)) {
 | 
	
		
			
				|  |  | +            // 服务号的appid以及秘钥
 | 
	
		
			
				|  |  | +            String requestUrl = StrUtil.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}", appid, Wxgsecret);
 | 
	
		
			
				|  |  | +            String returnMsg = HttpUtil.get(requestUrl);
 | 
	
		
			
				|  |  | +            cn.hutool.json.JSONObject responseJsonObject = JSONUtil.parseObj(returnMsg);
 | 
	
		
			
				|  |  | +            if (ObjectUtil.isNull(responseJsonObject)) try {
 | 
	
		
			
				|  |  | +                throw new Exception("响应异常:获取信息为空!");
 | 
	
		
			
				|  |  | +            } catch (Exception e) {
 | 
	
		
			
				|  |  | +                throw new RuntimeException(e);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            accessToken = responseJsonObject.getStr("access_token");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            //微信的accessToken的失效时间是两个小时,这里改为一小时五十五分钟,怕双方的时间误差导致accessToken失效
 | 
	
		
			
				|  |  | +            long expireTime = 60 * 60 + 55 * 60; // 1小时55分钟转换为秒
 | 
	
		
			
				|  |  | +            stringRedisTemplate.opsForValue().set(WX_TICK_KEY_APPID, accessToken, expireTime, java.util.concurrent.TimeUnit.SECONDS);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        log.info("===========Access_token:{}", accessToken);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return accessToken;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /**
 |