mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 04:08:43 +08:00 
			
		
		
		
	多模块重构 1:将 yudao-user-server 涉及到 member 模块的逻辑,都迁移到 yudao-module-member 中
This commit is contained in:
		@@ -1,6 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 属于整个 yudao-user-server 的 framework 封装
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
package cn.iocoder.yudao.userserver.framework;
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.framework.security;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import org.springframework.security.config.Customizer;
 | 
			
		||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 | 
			
		||||
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
 | 
			
		||||
@Configuration
 | 
			
		||||
public class SecurityConfiguration {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private WebProperties webProperties;
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    public Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer() {
 | 
			
		||||
        return registry -> {
 | 
			
		||||
            registry.antMatchers(api("/**")).permitAll(); // 默认 API 都是用户可访问
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String api(String url) {
 | 
			
		||||
        return webProperties.getApiPrefix() + url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.controller;
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
### 请求 /member/user/profile/get 接口 => 没有权限
 | 
			
		||||
GET {{userServerUrl}}/member/user/profile/get
 | 
			
		||||
Authorization: Bearer test245
 | 
			
		||||
 | 
			
		||||
### 请求 /member/user/profile/revise-nickname 接口 成功
 | 
			
		||||
PUT {{userServerUrl}}/member/user/profile/update-nickname?nickName=yunai222
 | 
			
		||||
Authorization: Bearer test245
 | 
			
		||||
 | 
			
		||||
### 请求 /member/user/profile/get-user-info 接口 成功
 | 
			
		||||
GET {{userServerUrl}}/member/user/profile/get-user-info?id=245
 | 
			
		||||
Authorization: Bearer test245
 | 
			
		||||
@@ -1,73 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.controller.user;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 | 
			
		||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
 | 
			
		||||
import io.swagger.annotations.Api;
 | 
			
		||||
import io.swagger.annotations.ApiOperation;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
import org.springframework.web.multipart.MultipartFile;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import javax.validation.Valid;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
 | 
			
		||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 | 
			
		||||
import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.FILE_IS_EMPTY;
 | 
			
		||||
 | 
			
		||||
@Api(tags = "用户个人中心")
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/member/user/profile")
 | 
			
		||||
@Validated
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class SysUserProfileController {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private MbrUserService userService;
 | 
			
		||||
 | 
			
		||||
    @PutMapping("/update-nickname")
 | 
			
		||||
    @ApiOperation("修改用户昵称")
 | 
			
		||||
    @PreAuthenticated
 | 
			
		||||
    public CommonResult<Boolean> updateNickname(@RequestParam("nickname") String nickname) {
 | 
			
		||||
        userService.updateNickname(getLoginUserId(), nickname);
 | 
			
		||||
        return success(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PutMapping("/update-avatar")
 | 
			
		||||
    @ApiOperation("修改用户头像")
 | 
			
		||||
    @PreAuthenticated
 | 
			
		||||
    public CommonResult<String> updateAvatar(@RequestParam("avatarFile") MultipartFile file) throws IOException {
 | 
			
		||||
        if (file.isEmpty()) {
 | 
			
		||||
            throw ServiceExceptionUtil.exception(FILE_IS_EMPTY);
 | 
			
		||||
        }
 | 
			
		||||
        String avatar = userService.updateAvatar(getLoginUserId(), file.getInputStream());
 | 
			
		||||
        return success(avatar);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/get")
 | 
			
		||||
    @ApiOperation("获得基本信息")
 | 
			
		||||
    @PreAuthenticated
 | 
			
		||||
    public CommonResult<MbrUserInfoRespVO> getUserInfo() {
 | 
			
		||||
        return success(userService.getUserInfo(getLoginUserId()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/update-mobile")
 | 
			
		||||
    @ApiOperation(value = "修改用户手机")
 | 
			
		||||
    @PreAuthenticated
 | 
			
		||||
    public CommonResult<Boolean> updateMobile(@RequestBody @Valid MbrUserUpdateMobileReqVO reqVO) {
 | 
			
		||||
        userService.updateMobile(getLoginUserId(), reqVO);
 | 
			
		||||
        return success(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.controller.user.vo;
 | 
			
		||||
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
@ApiModel("用户个人信息 Response VO")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class MbrUserInfoRespVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿")
 | 
			
		||||
    private String nickname;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "用户头像", required = true, example = "/infra/file/get/35a12e57-4297-4faa-bf7d-7ed2f211c952")
 | 
			
		||||
    private String avatar;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,48 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.controller.user.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.hibernate.validator.constraints.Length;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotBlank;
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
import javax.validation.constraints.Pattern;
 | 
			
		||||
 | 
			
		||||
@ApiModel("修改手机 Request VO")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class MbrUserUpdateMobileReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
 | 
			
		||||
    @NotEmpty(message = "手机验证码不能为空")
 | 
			
		||||
    @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
 | 
			
		||||
    @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
 | 
			
		||||
    private String code;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机号",required = true,example = "15823654487")
 | 
			
		||||
    @NotBlank(message = "手机号不能为空")
 | 
			
		||||
    @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
 | 
			
		||||
    @Mobile
 | 
			
		||||
    private String mobile;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "原手机验证码", required = true, example = "1024")
 | 
			
		||||
    @NotEmpty(message = "原手机验证码不能为空")
 | 
			
		||||
    @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
 | 
			
		||||
    @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
 | 
			
		||||
    private String oldCode;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "原手机号",required = true,example = "15823654487")
 | 
			
		||||
    @NotBlank(message = "手机号不能为空")
 | 
			
		||||
    @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
 | 
			
		||||
    @Mobile
 | 
			
		||||
    private String oldMobile;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 提供 POJO 类的实体转换
 | 
			
		||||
 *
 | 
			
		||||
 * 目前使用 MapStruct 框架
 | 
			
		||||
 */
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.convert;
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.convert.user;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
 | 
			
		||||
import org.mapstruct.Mapper;
 | 
			
		||||
import org.mapstruct.factory.Mappers;
 | 
			
		||||
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface UserProfileConvert {
 | 
			
		||||
 | 
			
		||||
    UserProfileConvert INSTANCE = Mappers.getMapper(UserProfileConvert.class);
 | 
			
		||||
 | 
			
		||||
    MbrUserInfoRespVO convert(MbrUserDO bean);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.dal.mysql.user;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * MbrUserDO Mapper
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface MbrUserMapper extends BaseMapperX<MbrUserDO> {
 | 
			
		||||
 | 
			
		||||
    default MbrUserDO selectByMobile(String mobile) {
 | 
			
		||||
        return selectOne(MbrUserDO::getMobile, mobile);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.enums;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Member 错误码枚举类
 | 
			
		||||
 *
 | 
			
		||||
 * member 系统,使用 1-004-000-000 段
 | 
			
		||||
 */
 | 
			
		||||
public interface MbrErrorCodeConstants {
 | 
			
		||||
 | 
			
		||||
    // ==========用户相关  1004001000============
 | 
			
		||||
    ErrorCode USER_NOT_EXISTS = new ErrorCode(1004001000, "用户不存在");
 | 
			
		||||
 | 
			
		||||
    // ==========文件相关 1004002000 ===========
 | 
			
		||||
    ErrorCode FILE_IS_EMPTY = new ErrorCode(1004002000, "文件为空");
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * weixin 包下,我们放微信相关业务.
 | 
			
		||||
 * 例如说:微信公众号、等等
 | 
			
		||||
 * ps:微信支付,还是放在 pay 包下
 | 
			
		||||
 *
 | 
			
		||||
 * 缩写:wx
 | 
			
		||||
 */
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member;
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.service;
 | 
			
		||||
@@ -1,81 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.service.user;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO;
 | 
			
		||||
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 前台用户 Service 接口
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
public interface MbrUserService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 通过手机查询用户
 | 
			
		||||
     *
 | 
			
		||||
     * @param mobile 手机
 | 
			
		||||
     * @return 用户对象
 | 
			
		||||
     */
 | 
			
		||||
    MbrUserDO getUserByMobile(String mobile);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 基于手机号创建用户。
 | 
			
		||||
     * 如果用户已经存在,则直接进行返回
 | 
			
		||||
     *
 | 
			
		||||
     * @param mobile 手机号
 | 
			
		||||
     * @param registerIp 注册 IP
 | 
			
		||||
     * @return 用户对象
 | 
			
		||||
     */
 | 
			
		||||
    MbrUserDO createUserIfAbsent(@Mobile String mobile, String registerIp);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 更新用户的最后登陆信息
 | 
			
		||||
     *
 | 
			
		||||
     * @param id 用户编号
 | 
			
		||||
     * @param loginIp 登陆 IP
 | 
			
		||||
     */
 | 
			
		||||
    void updateUserLogin(Long id, String loginIp);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 通过用户 ID 查询用户
 | 
			
		||||
     *
 | 
			
		||||
     * @param id 用户ID
 | 
			
		||||
     * @return 用户对象信息
 | 
			
		||||
     */
 | 
			
		||||
    MbrUserDO getUser(Long id);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 修改用户昵称
 | 
			
		||||
     * @param userId 用户id
 | 
			
		||||
     * @param nickname 用户新昵称
 | 
			
		||||
     */
 | 
			
		||||
    void updateNickname(Long userId, String nickname);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 修改用户头像
 | 
			
		||||
     * @param userId 用户id
 | 
			
		||||
     * @param inputStream 头像文件
 | 
			
		||||
     * @return 头像url
 | 
			
		||||
     */
 | 
			
		||||
    String updateAvatar(Long userId, InputStream inputStream);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据用户id,获取用户头像与昵称
 | 
			
		||||
     *
 | 
			
		||||
     * @param userId 用户id
 | 
			
		||||
     * @return 用户响应实体类
 | 
			
		||||
     */
 | 
			
		||||
    MbrUserInfoRespVO getUserInfo(Long userId);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 修改手机
 | 
			
		||||
     * @param userId 用户id
 | 
			
		||||
     * @param reqVO 请求实体
 | 
			
		||||
     */
 | 
			
		||||
    void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,161 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.service.user.impl;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.io.IoUtil;
 | 
			
		||||
import cn.hutool.core.util.IdUtil;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.convert.user.UserProfileConvert;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
 | 
			
		||||
import com.google.common.annotations.VisibleForTesting;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.security.crypto.password.PasswordEncoder;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import javax.validation.Valid;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
 | 
			
		||||
import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.USER_NOT_EXISTS;
 | 
			
		||||
import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.USER_SMS_CODE_IS_UNUSED;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * User Service 实现类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
@Valid
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class MbrUserServiceImpl implements MbrUserService {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private MbrUserMapper userMapper;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private InfFileCoreService fileCoreService;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private PasswordEncoder passwordEncoder;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysSmsCodeService smsCodeService;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MbrUserDO getUserByMobile(String mobile) {
 | 
			
		||||
        return userMapper.selectByMobile(mobile);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MbrUserDO createUserIfAbsent(String mobile, String registerIp) {
 | 
			
		||||
        // 用户已经存在
 | 
			
		||||
        MbrUserDO user = userMapper.selectByMobile(mobile);
 | 
			
		||||
        if (user != null) {
 | 
			
		||||
            return user;
 | 
			
		||||
        }
 | 
			
		||||
        // 用户不存在,则进行创建
 | 
			
		||||
        return this.createUser(mobile, registerIp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private MbrUserDO createUser(String mobile, String registerIp) {
 | 
			
		||||
        // 生成密码
 | 
			
		||||
        String password = IdUtil.fastSimpleUUID();
 | 
			
		||||
        // 插入用户
 | 
			
		||||
        MbrUserDO user = new MbrUserDO();
 | 
			
		||||
        user.setMobile(mobile);
 | 
			
		||||
        user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
 | 
			
		||||
        user.setPassword(passwordEncoder.encode(password)); // 加密密码
 | 
			
		||||
        user.setRegisterIp(registerIp);
 | 
			
		||||
        userMapper.insert(user);
 | 
			
		||||
        return user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateUserLogin(Long id, String loginIp) {
 | 
			
		||||
        userMapper.updateById(new MbrUserDO().setId(id).setLoginIp(loginIp).setLoginDate(new Date()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MbrUserDO getUser(Long id) {
 | 
			
		||||
        return userMapper.selectById(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateNickname(Long userId, String nickname) {
 | 
			
		||||
        MbrUserDO user = this.checkUserExists(userId);
 | 
			
		||||
        // 仅当新昵称不等于旧昵称时进行修改
 | 
			
		||||
        if (nickname.equals(user.getNickname())){
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        MbrUserDO userDO = new MbrUserDO();
 | 
			
		||||
        userDO.setId(user.getId());
 | 
			
		||||
        userDO.setNickname(nickname);
 | 
			
		||||
        userMapper.updateById(userDO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String updateAvatar(Long userId, InputStream avatarFile) {
 | 
			
		||||
        this.checkUserExists(userId);
 | 
			
		||||
        // 创建文件
 | 
			
		||||
        String avatar = fileCoreService.createFile(IdUtil.fastUUID(), IoUtil.readBytes(avatarFile));
 | 
			
		||||
        // 更新头像路径
 | 
			
		||||
        MbrUserDO userDO = MbrUserDO.builder()
 | 
			
		||||
                .id(userId)
 | 
			
		||||
                .avatar(avatar)
 | 
			
		||||
                .build();
 | 
			
		||||
        userMapper.updateById(userDO);
 | 
			
		||||
        return avatar;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MbrUserInfoRespVO getUserInfo(Long userId) {
 | 
			
		||||
        MbrUserDO user = this.checkUserExists(userId);
 | 
			
		||||
        // 拼接返回结果
 | 
			
		||||
        return UserProfileConvert.INSTANCE.convert(user);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Transactional(rollbackFor = Exception.class)
 | 
			
		||||
    public void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO) {
 | 
			
		||||
        // 检测用户是否存在
 | 
			
		||||
        checkUserExists(userId);
 | 
			
		||||
 | 
			
		||||
        // 校验旧手机和旧验证码
 | 
			
		||||
        SysSmsCodeDO sysSmsCodeDO = smsCodeService.checkCodeIsExpired(reqVO.getOldMobile(), reqVO.getOldCode(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene());
 | 
			
		||||
        // 判断旧code是否未被使用,如果是,抛出异常
 | 
			
		||||
        if (Boolean.FALSE.equals(sysSmsCodeDO.getUsed())){
 | 
			
		||||
            throw exception(USER_SMS_CODE_IS_UNUSED);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 使用新验证码
 | 
			
		||||
        smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP());
 | 
			
		||||
 | 
			
		||||
        // 更新用户手机
 | 
			
		||||
        MbrUserDO userDO = MbrUserDO.builder().id(userId).mobile(reqVO.getMobile()).build();
 | 
			
		||||
        userMapper.updateById(userDO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @VisibleForTesting
 | 
			
		||||
    public MbrUserDO checkUserExists(Long id) {
 | 
			
		||||
        if (id == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        MbrUserDO user = userMapper.selectById(id);
 | 
			
		||||
        if (user == null) {
 | 
			
		||||
            throw exception(USER_NOT_EXISTS);
 | 
			
		||||
        }else{
 | 
			
		||||
            return user;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
### 请求 /login 接口 => 成功
 | 
			
		||||
POST {{userServerUrl}}/login
 | 
			
		||||
Content-Type: application/json
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
  "mobile": "15601691300",
 | 
			
		||||
  "password": "admin123"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
### 请求 /send-sms-code 接口 => 成功
 | 
			
		||||
POST {{userServerUrl}}/send-sms-code
 | 
			
		||||
Content-Type: application/json
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
  "mobile": "15601691399",
 | 
			
		||||
  "scene": 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
### 请求 /sms-login 接口 => 成功
 | 
			
		||||
POST {{userServerUrl}}/sms-login
 | 
			
		||||
Content-Type: application/json
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
  "mobile": "15601691301",
 | 
			
		||||
  "code": 9999
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
### 请求 /logout 接口 => 成功
 | 
			
		||||
POST {{userServerUrl}}/logout
 | 
			
		||||
Content-Type: application/json
 | 
			
		||||
Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66
 | 
			
		||||
@@ -1,130 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 | 
			
		||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
 | 
			
		||||
import io.swagger.annotations.Api;
 | 
			
		||||
import io.swagger.annotations.ApiImplicitParam;
 | 
			
		||||
import io.swagger.annotations.ApiImplicitParams;
 | 
			
		||||
import io.swagger.annotations.ApiOperation;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import javax.validation.Valid;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent;
 | 
			
		||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 | 
			
		||||
 | 
			
		||||
@Api(tags = "认证")
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/")
 | 
			
		||||
@Validated
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class SysAuthController {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysAuthService authService;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysSmsCodeService smsCodeService;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysSocialCoreService socialService;
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/login")
 | 
			
		||||
    @ApiOperation("使用手机 + 密码登录")
 | 
			
		||||
    public CommonResult<SysAuthLoginRespVO> login(@RequestBody @Valid SysAuthLoginReqVO reqVO) {
 | 
			
		||||
        String token = authService.login(reqVO, getClientIP(), getUserAgent());
 | 
			
		||||
        // 返回结果
 | 
			
		||||
        return success(SysAuthLoginRespVO.builder().token(token).build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/sms-login")
 | 
			
		||||
    @ApiOperation("使用手机 + 验证码登录")
 | 
			
		||||
    public CommonResult<SysAuthLoginRespVO> smsLogin(@RequestBody @Valid SysAuthSmsLoginReqVO reqVO) {
 | 
			
		||||
        String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent());
 | 
			
		||||
        // 返回结果
 | 
			
		||||
        return success(SysAuthLoginRespVO.builder().token(token).build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/send-sms-code")
 | 
			
		||||
    @ApiOperation(value = "发送手机验证码")
 | 
			
		||||
    public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) {
 | 
			
		||||
        smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP());
 | 
			
		||||
        return success(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/send-sms-code-login")
 | 
			
		||||
    @ApiOperation(value = "向已登录用户发送验证码",notes = "修改手机时验证原手机号使用")
 | 
			
		||||
    public CommonResult<Boolean> sendSmsCodeLogin() {
 | 
			
		||||
        smsCodeService.sendSmsCodeLogin(getLoginUserId());
 | 
			
		||||
        return success(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/reset-password")
 | 
			
		||||
    @ApiOperation(value = "重置密码", notes = "用户忘记密码时使用")
 | 
			
		||||
    @PreAuthenticated
 | 
			
		||||
    public CommonResult<Boolean> resetPassword(@RequestBody @Valid MbrAuthResetPasswordReqVO reqVO) {
 | 
			
		||||
        authService.resetPassword(reqVO);
 | 
			
		||||
        return success(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/update-password")
 | 
			
		||||
    @ApiOperation(value = "修改用户密码",notes = "用户修改密码时使用")
 | 
			
		||||
    @PreAuthenticated
 | 
			
		||||
    public CommonResult<Boolean> updatePassword(@RequestBody @Valid MbrAuthUpdatePasswordReqVO reqVO) {
 | 
			
		||||
        authService.updatePassword(getLoginUserId(), reqVO);
 | 
			
		||||
        return success(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // ========== 社交登录相关 ==========
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/social-auth-redirect")
 | 
			
		||||
    @ApiOperation("社交授权的跳转")
 | 
			
		||||
    @ApiImplicitParams({
 | 
			
		||||
            @ApiImplicitParam(name = "type", value = "社交类型", required = true, dataTypeClass = Integer.class),
 | 
			
		||||
            @ApiImplicitParam(name = "redirectUri", value = "回调路径", dataTypeClass = String.class)
 | 
			
		||||
    })
 | 
			
		||||
    public CommonResult<String> socialAuthRedirect(@RequestParam("type") Integer type,
 | 
			
		||||
                                                   @RequestParam("redirectUri") String redirectUri) {
 | 
			
		||||
        return CommonResult.success(socialService.getAuthorizeUrl(type, redirectUri));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/social-login")
 | 
			
		||||
    @ApiOperation("社交登录,使用 code 授权码")
 | 
			
		||||
    public CommonResult<SysAuthLoginRespVO> socialLogin(@RequestBody @Valid MbrAuthSocialLoginReqVO reqVO) {
 | 
			
		||||
        String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent());
 | 
			
		||||
        return success(SysAuthLoginRespVO.builder().token(token).build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/social-login2")
 | 
			
		||||
    @ApiOperation("社交登录,使用 手机号 + 手机验证码")
 | 
			
		||||
    public CommonResult<SysAuthLoginRespVO> socialLogin2(@RequestBody @Valid MbrAuthSocialLogin2ReqVO reqVO) {
 | 
			
		||||
        String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent());
 | 
			
		||||
        return success(SysAuthLoginRespVO.builder().token(token).build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/social-bind")
 | 
			
		||||
    @ApiOperation("社交绑定,使用 code 授权码")
 | 
			
		||||
    public CommonResult<Boolean> socialBind(@RequestBody @Valid MbrAuthSocialBindReqVO reqVO) {
 | 
			
		||||
        authService.socialBind(getLoginUserId(), reqVO);
 | 
			
		||||
        return CommonResult.success(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @DeleteMapping("/social-unbind")
 | 
			
		||||
    @ApiOperation("取消社交绑定")
 | 
			
		||||
    public CommonResult<Boolean> socialUnbind(@RequestBody MbrAuthSocialUnbindReqVO reqVO) {
 | 
			
		||||
        socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId(), UserTypeEnum.MEMBER);
 | 
			
		||||
        return CommonResult.success(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.hibernate.validator.constraints.Length;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotBlank;
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
import javax.validation.constraints.Pattern;
 | 
			
		||||
 | 
			
		||||
@ApiModel("重置密码 Request VO")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class MbrAuthResetPasswordReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "新密码", required = true, example = "buzhidao")
 | 
			
		||||
    @NotEmpty(message = "新密码不能为空")
 | 
			
		||||
    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
 | 
			
		||||
    private String password;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
 | 
			
		||||
    @NotEmpty(message = "手机验证码不能为空")
 | 
			
		||||
    @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
 | 
			
		||||
    @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
 | 
			
		||||
    private String code;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机号",required = true,example = "15878962356")
 | 
			
		||||
    @NotBlank(message = "手机号不能为空")
 | 
			
		||||
    @Mobile
 | 
			
		||||
    private String mobile;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
@ApiModel("社交绑定 Request VO,使用 code 授权码")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class MbrAuthSocialBindReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
 | 
			
		||||
    @InEnum(SysSocialTypeEnum.class)
 | 
			
		||||
    @NotNull(message = "社交平台的类型不能为空")
 | 
			
		||||
    private Integer type;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "授权码", required = true, example = "1024")
 | 
			
		||||
    @NotEmpty(message = "授权码不能为空")
 | 
			
		||||
    private String code;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "state", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
 | 
			
		||||
    @NotEmpty(message = "state 不能为空")
 | 
			
		||||
    private String state;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.hibernate.validator.constraints.Length;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
import javax.validation.constraints.Pattern;
 | 
			
		||||
 | 
			
		||||
@ApiModel("社交登录 Request VO,使用 code 授权码 + 账号密码")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class MbrAuthSocialLogin2ReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
 | 
			
		||||
    @InEnum(SysSocialTypeEnum.class)
 | 
			
		||||
    @NotNull(message = "社交平台的类型不能为空")
 | 
			
		||||
    private Integer type;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "授权码", required = true, example = "1024")
 | 
			
		||||
    @NotEmpty(message = "授权码不能为空")
 | 
			
		||||
    private String code;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "state", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
 | 
			
		||||
    @NotEmpty(message = "state 不能为空")
 | 
			
		||||
    private String state;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机号", required = true, example = "15119100000")
 | 
			
		||||
    @NotEmpty(message = "手机号不能为空")
 | 
			
		||||
    @Length(min = 11, max = 11, message = "手机号是11位数字")
 | 
			
		||||
    private String mobile;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
 | 
			
		||||
    @NotEmpty(message = "手机验证码不能为空")
 | 
			
		||||
    @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
 | 
			
		||||
    @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
 | 
			
		||||
    private String smsCode;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
@ApiModel("社交登录 Request VO,使用 code 授权码")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class MbrAuthSocialLoginReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
 | 
			
		||||
    @InEnum(SysSocialTypeEnum.class)
 | 
			
		||||
    @NotNull(message = "社交平台的类型不能为空")
 | 
			
		||||
    private Integer type;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "授权码", required = true, example = "1024")
 | 
			
		||||
    @NotEmpty(message = "授权码不能为空")
 | 
			
		||||
    private String code;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "state", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
 | 
			
		||||
    @NotEmpty(message = "state 不能为空")
 | 
			
		||||
    private String state;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
@ApiModel("取消社交绑定 Request VO,使用 code 授权码")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class MbrAuthSocialUnbindReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
 | 
			
		||||
    @InEnum(SysSocialTypeEnum.class)
 | 
			
		||||
    @NotNull(message = "社交平台的类型不能为空")
 | 
			
		||||
    private Integer type;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "社交的全局编号", required = true, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE")
 | 
			
		||||
    @NotEmpty(message = "社交的全局编号不能为空")
 | 
			
		||||
    private String unionId;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.hibernate.validator.constraints.Length;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotBlank;
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
 | 
			
		||||
@ApiModel("修改密码 Request VO")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class MbrAuthUpdatePasswordReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "用户旧密码", required = true, example = "123456")
 | 
			
		||||
    @NotBlank(message = "旧密码不能为空")
 | 
			
		||||
    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
 | 
			
		||||
    private String oldPassword;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "新密码", required = true, example = "buzhidao")
 | 
			
		||||
    @NotEmpty(message = "新密码不能为空")
 | 
			
		||||
    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
 | 
			
		||||
    private String password;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,40 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.hibernate.validator.constraints.Length;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotBlank;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
import javax.validation.constraints.Pattern;
 | 
			
		||||
 | 
			
		||||
@ApiModel("校验验证码 Request VO")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class SysAuthCheckCodeReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机号", example = "15601691234")
 | 
			
		||||
    @NotBlank(message = "手机号不能为空")
 | 
			
		||||
    @Mobile
 | 
			
		||||
    private String mobile;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
 | 
			
		||||
    @NotBlank(message = "手机验证码不能为空")
 | 
			
		||||
    @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
 | 
			
		||||
    @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
 | 
			
		||||
    private String code;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "发送场景", example = "1", notes = "对应 MbrSmsSceneEnum 枚举")
 | 
			
		||||
    @NotNull(message = "发送场景不能为空")
 | 
			
		||||
    @InEnum(SysSmsSceneEnum.class)
 | 
			
		||||
    private Integer scene;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.hibernate.validator.constraints.Length;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
 | 
			
		||||
@ApiModel("手机 + 密码登录 Request VO")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class SysAuthLoginReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机号", required = true, example = "15601691300")
 | 
			
		||||
    @NotEmpty(message = "手机号不能为空")
 | 
			
		||||
    @Mobile
 | 
			
		||||
    private String mobile;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "密码", required = true, example = "buzhidao")
 | 
			
		||||
    @NotEmpty(message = "密码不能为空")
 | 
			
		||||
    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
 | 
			
		||||
    private String password;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
@ApiModel("手机密码登录 Response VO")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class SysAuthLoginRespVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "token", required = true, example = "yudaoyuanma")
 | 
			
		||||
    private String token;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
@ApiModel("发送手机验证码 Response VO")
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class SysAuthSendSmsReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机号", example = "15601691234")
 | 
			
		||||
    @Mobile
 | 
			
		||||
    private String mobile;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "发送场景", example = "1", notes = "对应 MbrSmsSceneEnum 枚举")
 | 
			
		||||
    @NotNull(message = "发送场景不能为空")
 | 
			
		||||
    @InEnum(SysSmsSceneEnum.class)
 | 
			
		||||
    private Integer scene;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.hibernate.validator.constraints.Length;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
import javax.validation.constraints.Pattern;
 | 
			
		||||
 | 
			
		||||
@ApiModel("手机 + 验证码登录 Request VO")
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class SysAuthSmsLoginReqVO {
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机号", required = true, example = "15601691300")
 | 
			
		||||
    @NotEmpty(message = "手机号不能为空")
 | 
			
		||||
    @Mobile
 | 
			
		||||
    private String mobile;
 | 
			
		||||
 | 
			
		||||
    @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
 | 
			
		||||
    @NotEmpty(message = "手机验证码不能为空")
 | 
			
		||||
    @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
 | 
			
		||||
    @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
 | 
			
		||||
    private String code;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 占位
 | 
			
		||||
 */
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.controller;
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.convert.auth;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
 | 
			
		||||
import org.mapstruct.Mapper;
 | 
			
		||||
import org.mapstruct.Mapping;
 | 
			
		||||
import org.mapstruct.factory.Mappers;
 | 
			
		||||
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface SysAuthConvert {
 | 
			
		||||
 | 
			
		||||
    SysAuthConvert INSTANCE = Mappers.getMapper(SysAuthConvert.class);
 | 
			
		||||
 | 
			
		||||
    @Mapping(source = "mobile", target = "username")
 | 
			
		||||
    LoginUser convert0(MbrUserDO bean);
 | 
			
		||||
 | 
			
		||||
    default LoginUser convert(MbrUserDO bean) {
 | 
			
		||||
        // 目的,为了设置 UserTypeEnum.MEMBER.getValue()
 | 
			
		||||
        return convert0(bean).setUserType(UserTypeEnum.MEMBER.getValue());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 提供 POJO 类的实体转换
 | 
			
		||||
 *
 | 
			
		||||
 * 目前使用 MapStruct 框架
 | 
			
		||||
 */
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.convert;
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.dal.dataobject;
 | 
			
		||||
@@ -1,64 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
import lombok.*;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 手机验证码 DO
 | 
			
		||||
 *
 | 
			
		||||
 * idx_mobile 索引:基于 {@link #mobile} 字段
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@TableName("sys_sms_code")
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@Builder
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class SysSmsCodeDO extends BaseDO {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 编号
 | 
			
		||||
     */
 | 
			
		||||
    private Integer id;
 | 
			
		||||
    /**
 | 
			
		||||
     * 手机号
 | 
			
		||||
     */
 | 
			
		||||
    private String mobile;
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码
 | 
			
		||||
     */
 | 
			
		||||
    private String code;
 | 
			
		||||
    /**
 | 
			
		||||
     * 发送场景
 | 
			
		||||
     *
 | 
			
		||||
     * 枚举 {@link SysSmsCodeDO}
 | 
			
		||||
     */
 | 
			
		||||
    private Integer scene;
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建 IP
 | 
			
		||||
     */
 | 
			
		||||
    private String createIp;
 | 
			
		||||
    /**
 | 
			
		||||
     * 今日发送的第几条
 | 
			
		||||
     */
 | 
			
		||||
    private Integer todayIndex;
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否使用
 | 
			
		||||
     */
 | 
			
		||||
    private Boolean used;
 | 
			
		||||
    /**
 | 
			
		||||
     * 使用时间
 | 
			
		||||
     */
 | 
			
		||||
    private Date usedTime;
 | 
			
		||||
    /**
 | 
			
		||||
     * 使用 IP
 | 
			
		||||
     */
 | 
			
		||||
    private String usedIp;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.dal.mysql;
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface SysSmsCodeMapper extends BaseMapperX<SysSmsCodeDO> {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得手机号的最后一个手机验证码
 | 
			
		||||
     *
 | 
			
		||||
     * @param mobile 手机号
 | 
			
		||||
     * @param scene 发送场景,选填
 | 
			
		||||
     * @param code 验证码 选填
 | 
			
		||||
     * @return 手机验证码
 | 
			
		||||
     */
 | 
			
		||||
    default SysSmsCodeDO selectLastByMobile(String mobile,String code,Integer scene) {
 | 
			
		||||
        return selectOne(new QueryWrapperX<SysSmsCodeDO>()
 | 
			
		||||
                .eq("mobile", mobile)
 | 
			
		||||
                .eqIfPresent("scene", scene)
 | 
			
		||||
                .eqIfPresent("code", code)
 | 
			
		||||
                .orderByDesc("id")
 | 
			
		||||
                .last("LIMIT 1"));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.dal.redis;
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.enums;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * System 错误码枚举类
 | 
			
		||||
 *
 | 
			
		||||
 * system 系统,使用 1-005-000-000 段
 | 
			
		||||
 */
 | 
			
		||||
public interface SysErrorCodeConstants {
 | 
			
		||||
 | 
			
		||||
    // ========== AUTH 模块 1005000000 ==========
 | 
			
		||||
    ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1005000000, "登录失败,账号密码不正确");
 | 
			
		||||
    ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1005000001, "登录失败,账号被禁用");
 | 
			
		||||
    ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1005000002, "登录失败"); // 登录失败的兜底,未知原因
 | 
			
		||||
    ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1005000003, "Token 已经过期");
 | 
			
		||||
    ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1005000004, "未绑定账号,需要进行绑定");
 | 
			
		||||
 | 
			
		||||
    // ========== SMS CODE 模块 1005001000 ==========
 | 
			
		||||
    ErrorCode USER_SMS_CODE_NOT_FOUND = new ErrorCode(1005001000, "验证码不存在");
 | 
			
		||||
    ErrorCode USER_SMS_CODE_EXPIRED = new ErrorCode(1005001001, "验证码已过期");
 | 
			
		||||
    ErrorCode USER_SMS_CODE_USED = new ErrorCode(1005001002, "验证码已使用");
 | 
			
		||||
    ErrorCode USER_SMS_CODE_NOT_CORRECT = new ErrorCode(1005001003, "验证码不正确");
 | 
			
		||||
    ErrorCode USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1005001004, "超过每日短信发送数量");
 | 
			
		||||
    ErrorCode USER_SMS_CODE_SEND_TOO_FAST = new ErrorCode(1005001005, "短信发送过于频率");
 | 
			
		||||
    ErrorCode USER_SMS_CODE_IS_EXISTS = new ErrorCode(1005001006, "手机号已被使用");
 | 
			
		||||
    ErrorCode USER_SMS_CODE_IS_UNUSED = new ErrorCode(1005001006, "验证码未被使用");
 | 
			
		||||
 | 
			
		||||
    // ========== 用户模块 1005002000 ==========
 | 
			
		||||
    ErrorCode USER_NOT_EXISTS = new ErrorCode(1005002001, "用户不存在");
 | 
			
		||||
    ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1005002003, "密码校验失败");
 | 
			
		||||
}
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.enums.sms;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 用户短信验证码发送场景的枚举
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Getter
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public enum SysSmsSceneEnum implements IntArrayValuable {
 | 
			
		||||
 | 
			
		||||
    LOGIN_BY_SMS(1,SysSmsTemplateCodeConstants.USER_SMS_LOGIN, "手机号登陆"),
 | 
			
		||||
    CHANGE_MOBILE_BY_SMS(2,SysSmsTemplateCodeConstants.USER_SMS_UPDATE_MOBILE, "更换手机号"),
 | 
			
		||||
    FORGET_MOBILE_BY_SMS(3,SysSmsTemplateCodeConstants.USER_SMS_RESET_PASSWORD, "忘记密码"),
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSmsSceneEnum::getScene).toArray();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证那场景编号
 | 
			
		||||
     */
 | 
			
		||||
    private final Integer scene;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 模版编码
 | 
			
		||||
     */
 | 
			
		||||
    private final String code;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 描述
 | 
			
		||||
     */
 | 
			
		||||
    private final String name;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int[] array() {
 | 
			
		||||
        return ARRAYS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String getCodeByScene(Integer scene){
 | 
			
		||||
        for (SysSmsSceneEnum value : values()) {
 | 
			
		||||
            if (value.getScene().equals(scene)){
 | 
			
		||||
                return value.getCode();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,25 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.enums.sms;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * yudao-user-server 使用到的短信模板的 Code 编码的枚举
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
public interface SysSmsTemplateCodeConstants {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 前台用户短信登录
 | 
			
		||||
     */
 | 
			
		||||
    String USER_SMS_LOGIN = "user-sms-login";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户忘记密码
 | 
			
		||||
     */
 | 
			
		||||
    String USER_SMS_RESET_PASSWORD = "user-sms-reset-password";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户更新手机号
 | 
			
		||||
     */
 | 
			
		||||
    String USER_SMS_UPDATE_MOBILE = "user-sms-update-mobile";
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 属于 system 模块的 framework 封装
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.framework;
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.framework.sms;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
 | 
			
		||||
@Configuration
 | 
			
		||||
@EnableConfigurationProperties(SmsCodeProperties.class)
 | 
			
		||||
public class SmsCodeConfiguration {
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.framework.sms;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
 | 
			
		||||
@ConfigurationProperties(prefix = "yudao.sms-code")
 | 
			
		||||
@Validated
 | 
			
		||||
@Data
 | 
			
		||||
public class SmsCodeProperties {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 过期时间
 | 
			
		||||
     */
 | 
			
		||||
    @NotNull(message = "过期时间不能为空")
 | 
			
		||||
    private Duration expireTimes;
 | 
			
		||||
    /**
 | 
			
		||||
     * 短信发送频率
 | 
			
		||||
     */
 | 
			
		||||
    @NotNull(message = "短信发送频率不能为空")
 | 
			
		||||
    private Duration sendFrequency;
 | 
			
		||||
    /**
 | 
			
		||||
     * 每日发送最大数量
 | 
			
		||||
     */
 | 
			
		||||
    @NotNull(message = "每日发送最大数量不能为空")
 | 
			
		||||
    private Integer sendMaximumQuantityPerDay;
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码最小值
 | 
			
		||||
     */
 | 
			
		||||
    @NotNull(message = "验证码最小值不能为空")
 | 
			
		||||
    private Integer beginCode;
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码最大值
 | 
			
		||||
     */
 | 
			
		||||
    @NotNull(message = "验证码最大值不能为空")
 | 
			
		||||
    private Integer endCode;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * system 包下,我们放通用业务,支撑上层的核心业务。
 | 
			
		||||
 * 例如说:用户、部门、权限、数据字典等等
 | 
			
		||||
 *
 | 
			
		||||
 * 缩写:sys
 | 
			
		||||
 */
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system;
 | 
			
		||||
@@ -1,78 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.service.auth;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*;
 | 
			
		||||
 | 
			
		||||
import javax.validation.Valid;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 用户前台的认证 Service 接口
 | 
			
		||||
 *
 | 
			
		||||
 * 提供用户的账号密码登录、token 的校验等认证相关的功能
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
public interface SysAuthService extends SecurityAuthFrameworkService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 手机 + 密码登录
 | 
			
		||||
     *
 | 
			
		||||
     * @param reqVO 登录信息
 | 
			
		||||
     * @param userIp 用户 IP
 | 
			
		||||
     * @param userAgent 用户 UA
 | 
			
		||||
     * @return 身份令牌,使用 JWT 方式
 | 
			
		||||
     */
 | 
			
		||||
    String login(@Valid SysAuthLoginReqVO reqVO, String userIp, String userAgent);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 手机 + 验证码登陆
 | 
			
		||||
     *
 | 
			
		||||
     * @param reqVO 登陆信息
 | 
			
		||||
     * @param userIp 用户 IP
 | 
			
		||||
     * @param userAgent 用户 UA
 | 
			
		||||
     * @return 身份令牌,使用 JWT 方式
 | 
			
		||||
     */
 | 
			
		||||
    String smsLogin(@Valid SysAuthSmsLoginReqVO reqVO, String userIp, String userAgent);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 社交登录,使用 code 授权码
 | 
			
		||||
     *
 | 
			
		||||
     * @param reqVO 登录信息
 | 
			
		||||
     * @param userIp 用户 IP
 | 
			
		||||
     * @param userAgent 用户 UA
 | 
			
		||||
     * @return 身份令牌,使用 JWT 方式
 | 
			
		||||
     */
 | 
			
		||||
    String socialLogin(@Valid MbrAuthSocialLoginReqVO reqVO, String userIp, String userAgent);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 社交登录,使用 手机号 + 手机验证码
 | 
			
		||||
     *
 | 
			
		||||
     * @param reqVO 登录信息
 | 
			
		||||
     * @param userIp 用户 IP
 | 
			
		||||
     * @param userAgent 用户 UA
 | 
			
		||||
     * @return 身份令牌,使用 JWT 方式
 | 
			
		||||
     */
 | 
			
		||||
    String socialLogin2(@Valid MbrAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 社交绑定,使用 code 授权码
 | 
			
		||||
     *
 | 
			
		||||
     * @param userId 用户编号
 | 
			
		||||
     * @param reqVO 绑定信息
 | 
			
		||||
     */
 | 
			
		||||
    void socialBind(Long userId, @Valid MbrAuthSocialBindReqVO reqVO);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 修改用户密码
 | 
			
		||||
     * @param userId 用户id
 | 
			
		||||
     * @param userReqVO 用户请求实体类
 | 
			
		||||
     */
 | 
			
		||||
    void updatePassword(Long userId,MbrAuthUpdatePasswordReqVO userReqVO);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 忘记密码
 | 
			
		||||
     * @param userReqVO 用户请求实体类
 | 
			
		||||
     */
 | 
			
		||||
    void resetPassword(MbrAuthResetPasswordReqVO userReqVO);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,355 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.service.auth.impl;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.lang.Assert;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginLogTypeEnum;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginResultEnum;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.convert.auth.SysAuthConvert;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
 | 
			
		||||
import com.google.common.annotations.VisibleForTesting;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.zhyd.oauth.model.AuthUser;
 | 
			
		||||
import org.springframework.context.annotation.Lazy;
 | 
			
		||||
import org.springframework.data.redis.core.StringRedisTemplate;
 | 
			
		||||
import org.springframework.security.authentication.AuthenticationManager;
 | 
			
		||||
import org.springframework.security.authentication.BadCredentialsException;
 | 
			
		||||
import org.springframework.security.authentication.DisabledException;
 | 
			
		||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
import org.springframework.security.core.AuthenticationException;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetails;
 | 
			
		||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
 | 
			
		||||
import org.springframework.security.crypto.password.PasswordEncoder;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
 | 
			
		||||
import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Auth Service 实现类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class SysAuthServiceImpl implements SysAuthService {
 | 
			
		||||
 | 
			
		||||
    private static final UserTypeEnum USER_TYPE_ENUM = UserTypeEnum.MEMBER;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    @Lazy // 延迟加载,因为存在相互依赖的问题
 | 
			
		||||
    private AuthenticationManager authenticationManager;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private MbrUserService userService;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysSmsCodeService smsCodeService;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysLoginLogCoreService loginLogCoreService;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysUserSessionCoreService userSessionCoreService;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysSocialCoreService socialService;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private StringRedisTemplate stringRedisTemplate;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private PasswordEncoder passwordEncoder;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private MbrUserMapper userMapper;
 | 
			
		||||
 | 
			
		||||
    private static final UserTypeEnum userTypeEnum = UserTypeEnum.MEMBER;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
 | 
			
		||||
        // 获取 username 对应的 SysUserDO
 | 
			
		||||
        MbrUserDO user = userService.getUserByMobile(mobile);
 | 
			
		||||
        if (user == null) {
 | 
			
		||||
            throw new UsernameNotFoundException(mobile);
 | 
			
		||||
        }
 | 
			
		||||
        // 创建 LoginUser 对象
 | 
			
		||||
        return SysAuthConvert.INSTANCE.convert(user);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent) {
 | 
			
		||||
        // 使用手机 + 密码,进行登录。
 | 
			
		||||
        LoginUser loginUser = this.login0(reqVO.getMobile(), reqVO.getPassword());
 | 
			
		||||
 | 
			
		||||
        // 缓存登录用户到 Redis 中,返回 sessionId 编号
 | 
			
		||||
        return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Transactional
 | 
			
		||||
    public String smsLogin(SysAuthSmsLoginReqVO reqVO, String userIp, String userAgent) {
 | 
			
		||||
        // 校验验证码
 | 
			
		||||
        smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.LOGIN_BY_SMS.getScene(),
 | 
			
		||||
                reqVO.getCode(), userIp);
 | 
			
		||||
 | 
			
		||||
        // 获得获得注册用户
 | 
			
		||||
        MbrUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp);
 | 
			
		||||
        Assert.notNull(user, "获取用户失败,结果为空");
 | 
			
		||||
 | 
			
		||||
        // 执行登陆
 | 
			
		||||
        this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SMS, SysLoginResultEnum.SUCCESS);
 | 
			
		||||
        LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
 | 
			
		||||
 | 
			
		||||
        // 缓存登录用户到 Redis 中,返回 sessionId 编号
 | 
			
		||||
        return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String socialLogin(MbrAuthSocialLoginReqVO reqVO, String userIp, String userAgent) {
 | 
			
		||||
        // 使用 code 授权码,进行登录
 | 
			
		||||
        AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
 | 
			
		||||
        org.springframework.util.Assert.notNull(authUser, "授权用户不为空");
 | 
			
		||||
 | 
			
		||||
        // 如果未绑定 SysSocialUserDO 用户,则无法自动登录,进行报错
 | 
			
		||||
        String unionId = socialService.getAuthUserUnionId(authUser);
 | 
			
		||||
        List<SysSocialUserDO> socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, USER_TYPE_ENUM);
 | 
			
		||||
        if (CollUtil.isEmpty(socialUsers)) {
 | 
			
		||||
            throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 自动登录
 | 
			
		||||
        MbrUserDO user = userService.getUser(socialUsers.get(0).getUserId());
 | 
			
		||||
        if (user == null) {
 | 
			
		||||
            throw exception(USER_NOT_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
        this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SOCIAL, SysLoginResultEnum.SUCCESS);
 | 
			
		||||
 | 
			
		||||
        // 创建 LoginUser 对象
 | 
			
		||||
        LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
 | 
			
		||||
 | 
			
		||||
        // 绑定社交用户(更新)
 | 
			
		||||
        socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, USER_TYPE_ENUM);
 | 
			
		||||
 | 
			
		||||
        // 缓存登录用户到 Redis 中,返回 sessionId 编号
 | 
			
		||||
        return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String socialLogin2(MbrAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) {
 | 
			
		||||
        AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
 | 
			
		||||
        org.springframework.util.Assert.notNull(authUser, "授权用户不为空");
 | 
			
		||||
 | 
			
		||||
        // 使用手机号、手机验证码登录
 | 
			
		||||
        SysAuthSmsLoginReqVO loginReqVO = SysAuthSmsLoginReqVO
 | 
			
		||||
                .builder()
 | 
			
		||||
                .mobile(reqVO.getMobile())
 | 
			
		||||
                .code(reqVO.getSmsCode())
 | 
			
		||||
                .build();
 | 
			
		||||
        String sessionId = this.smsLogin(loginReqVO, userIp, userAgent);
 | 
			
		||||
        LoginUser loginUser = userSessionCoreService.getLoginUser(sessionId);
 | 
			
		||||
 | 
			
		||||
        // 绑定社交用户(新增)
 | 
			
		||||
        socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, USER_TYPE_ENUM);
 | 
			
		||||
        return sessionId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void socialBind(Long userId, MbrAuthSocialBindReqVO reqVO) {
 | 
			
		||||
        // 使用 code 授权码,进行登录
 | 
			
		||||
        AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
 | 
			
		||||
        org.springframework.util.Assert.notNull(authUser, "授权用户不为空");
 | 
			
		||||
 | 
			
		||||
        // 绑定社交用户(新增)
 | 
			
		||||
        socialService.bindSocialUser(userId, reqVO.getType(), authUser, USER_TYPE_ENUM);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private LoginUser login0(String username, String password) {
 | 
			
		||||
        final SysLoginLogTypeEnum logTypeEnum = SysLoginLogTypeEnum.LOGIN_USERNAME;
 | 
			
		||||
        // 用户验证
 | 
			
		||||
        Authentication authentication;
 | 
			
		||||
        try {
 | 
			
		||||
            // 调用 Spring Security 的 AuthenticationManager#authenticate(...) 方法,使用账号密码进行认证
 | 
			
		||||
            // 在其内部,会调用到 loadUserByUsername 方法,获取 User 信息
 | 
			
		||||
            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
 | 
			
		||||
        } catch (BadCredentialsException badCredentialsException) {
 | 
			
		||||
            this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS);
 | 
			
		||||
            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
 | 
			
		||||
        } catch (DisabledException disabledException) {
 | 
			
		||||
            this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.USER_DISABLED);
 | 
			
		||||
            throw exception(AUTH_LOGIN_USER_DISABLED);
 | 
			
		||||
        } catch (AuthenticationException authenticationException) {
 | 
			
		||||
            log.error("[login0][username({}) 发生未知异常]", username, authenticationException);
 | 
			
		||||
            this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.UNKNOWN_ERROR);
 | 
			
		||||
            throw exception(AUTH_LOGIN_FAIL_UNKNOWN);
 | 
			
		||||
        }
 | 
			
		||||
        // 登录成功的日志
 | 
			
		||||
        Assert.notNull(authentication.getPrincipal(), "Principal 不会为空");
 | 
			
		||||
        this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.SUCCESS);
 | 
			
		||||
        return (LoginUser) authentication.getPrincipal();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void createLoginLog(String mobile, SysLoginLogTypeEnum logTypeEnum, SysLoginResultEnum loginResult) {
 | 
			
		||||
        // 获得用户
 | 
			
		||||
        MbrUserDO user = userService.getUserByMobile(mobile);
 | 
			
		||||
        // 插入登录日志
 | 
			
		||||
        SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
 | 
			
		||||
        reqDTO.setLogType(logTypeEnum.getType());
 | 
			
		||||
        reqDTO.setTraceId(TracerUtils.getTraceId());
 | 
			
		||||
        if (user != null) {
 | 
			
		||||
            reqDTO.setUserId(user.getId());
 | 
			
		||||
        }
 | 
			
		||||
        reqDTO.setUsername(mobile);
 | 
			
		||||
        reqDTO.setUserAgent(ServletUtils.getUserAgent());
 | 
			
		||||
        reqDTO.setUserIp(getClientIP());
 | 
			
		||||
        reqDTO.setResult(loginResult.getResult());
 | 
			
		||||
        loginLogCoreService.createLoginLog(reqDTO);
 | 
			
		||||
        // 更新最后登录时间
 | 
			
		||||
        if (user != null && Objects.equals(SysLoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) {
 | 
			
		||||
            userService.updateUserLogin(user.getId(), getClientIP());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public LoginUser verifyTokenAndRefresh(String token) {
 | 
			
		||||
        // 获得 LoginUser
 | 
			
		||||
        LoginUser loginUser = userSessionCoreService.getLoginUser(token);
 | 
			
		||||
        if (loginUser == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        // 刷新 LoginUser 缓存
 | 
			
		||||
        this.refreshLoginUserCache(token, loginUser);
 | 
			
		||||
        return loginUser;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void refreshLoginUserCache(String token, LoginUser loginUser) {
 | 
			
		||||
        // 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存
 | 
			
		||||
        if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() <
 | 
			
		||||
                userSessionCoreService.getSessionTimeoutMillis() / 3) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 重新加载 MbrUserDO 信息
 | 
			
		||||
        MbrUserDO user = userService.getUser(loginUser.getId());
 | 
			
		||||
        if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) {
 | 
			
		||||
            throw exception(AUTH_TOKEN_EXPIRED); // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 刷新 LoginUser 缓存
 | 
			
		||||
        userSessionCoreService.refreshUserSession(token, loginUser);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public LoginUser mockLogin(Long userId) {
 | 
			
		||||
        // 获取用户编号对应的 MbrUserDO
 | 
			
		||||
        MbrUserDO user = userService.getUser(userId);
 | 
			
		||||
        if (user == null) {
 | 
			
		||||
            throw new UsernameNotFoundException(String.valueOf(userId));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 执行登陆
 | 
			
		||||
        this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_MOCK, SysLoginResultEnum.SUCCESS);
 | 
			
		||||
 | 
			
		||||
        // 创建 LoginUser 对象
 | 
			
		||||
        return SysAuthConvert.INSTANCE.convert(user);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void logout(String token) {
 | 
			
		||||
        // 查询用户信息
 | 
			
		||||
        LoginUser loginUser = userSessionCoreService.getLoginUser(token);
 | 
			
		||||
        if (loginUser == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // 删除 session
 | 
			
		||||
        userSessionCoreService.deleteUserSession(token);
 | 
			
		||||
        // 记录登出日志
 | 
			
		||||
        this.createLogoutLog(loginUser.getId(), loginUser.getUsername());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updatePassword(Long userId,MbrAuthUpdatePasswordReqVO reqVO) {
 | 
			
		||||
        // 检验旧密码
 | 
			
		||||
        MbrUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword());
 | 
			
		||||
 | 
			
		||||
        // 更新用户密码
 | 
			
		||||
        MbrUserDO mbrUserDO = MbrUserDO.builder().build();
 | 
			
		||||
        mbrUserDO.setId(userDO.getId());
 | 
			
		||||
        mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
 | 
			
		||||
        userMapper.updateById(mbrUserDO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void resetPassword(MbrAuthResetPasswordReqVO reqVO) {
 | 
			
		||||
        // 检验用户是否存在
 | 
			
		||||
        MbrUserDO userDO = checkUserIfExists(reqVO.getMobile());
 | 
			
		||||
 | 
			
		||||
        // 使用验证码
 | 
			
		||||
        smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(),reqVO.getCode(),getClientIP());
 | 
			
		||||
 | 
			
		||||
        // 更新密码
 | 
			
		||||
        MbrUserDO mbrUserDO = MbrUserDO.builder().build();
 | 
			
		||||
        mbrUserDO.setId(userDO.getId());
 | 
			
		||||
        mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
 | 
			
		||||
        userMapper.updateById(mbrUserDO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 校验旧密码
 | 
			
		||||
     *
 | 
			
		||||
     * @param id          用户 id
 | 
			
		||||
     * @param oldPassword 旧密码
 | 
			
		||||
     * @return MbrUserDO 用户实体
 | 
			
		||||
     */
 | 
			
		||||
    @VisibleForTesting
 | 
			
		||||
    public MbrUserDO checkOldPassword(Long id, String oldPassword) {
 | 
			
		||||
        MbrUserDO user = userMapper.selectById(id);
 | 
			
		||||
        if (user == null) {
 | 
			
		||||
            throw exception(USER_NOT_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
        // 参数:未加密密码,编码后的密码
 | 
			
		||||
        if (!passwordEncoder.matches(oldPassword,user.getPassword())) {
 | 
			
		||||
            throw exception(USER_PASSWORD_FAILED);
 | 
			
		||||
        }
 | 
			
		||||
        return user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MbrUserDO checkUserIfExists(String mobile) {
 | 
			
		||||
        MbrUserDO user = userMapper.selectByMobile(mobile);
 | 
			
		||||
        if (user == null) {
 | 
			
		||||
            throw exception(USER_NOT_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
        return user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private void createLogoutLog(Long userId, String username) {
 | 
			
		||||
        SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
 | 
			
		||||
        reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
 | 
			
		||||
        reqDTO.setTraceId(TracerUtils.getTraceId());
 | 
			
		||||
        reqDTO.setUserId(userId);
 | 
			
		||||
        reqDTO.setUserType(USER_TYPE_ENUM.getValue());
 | 
			
		||||
        reqDTO.setUsername(username);
 | 
			
		||||
        reqDTO.setUserAgent(ServletUtils.getUserAgent());
 | 
			
		||||
        reqDTO.setUserIp(getClientIP());
 | 
			
		||||
        reqDTO.setResult(SysLoginResultEnum.SUCCESS.getResult());
 | 
			
		||||
        loginLogCoreService.createLoginLog(reqDTO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.service;
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.service.sms;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 短信验证码 Service 接口
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
public interface SysSmsCodeService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建短信验证码,并进行发送
 | 
			
		||||
     *
 | 
			
		||||
     * @param mobile   手机号
 | 
			
		||||
     * @param scene    发送场景 {@link SysSmsSceneEnum}
 | 
			
		||||
     * @param createIp 发送 IP
 | 
			
		||||
     */
 | 
			
		||||
    void sendSmsCode(@Mobile String mobile, Integer scene, String createIp);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证短信验证码,并进行使用
 | 
			
		||||
     * 如果正确,则将验证码标记成已使用
 | 
			
		||||
     * 如果错误,则抛出 {@link ServiceException} 异常
 | 
			
		||||
     *
 | 
			
		||||
     * @param mobile 手机号
 | 
			
		||||
     * @param scene  发送场景
 | 
			
		||||
     * @param code   验证码
 | 
			
		||||
     * @param usedIp 使用 IP
 | 
			
		||||
     */
 | 
			
		||||
    void useSmsCode(@Mobile String mobile, Integer scene, String code, String usedIp);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据用户id发送验证码
 | 
			
		||||
     *
 | 
			
		||||
     * @param userId 用户id
 | 
			
		||||
     */
 | 
			
		||||
    void sendSmsCodeLogin(Long userId);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查验证码是否有效
 | 
			
		||||
     * @param mobile 手机
 | 
			
		||||
     * @param code 验证码
 | 
			
		||||
     * @param scene 使用场景
 | 
			
		||||
     * @return 验证码记录
 | 
			
		||||
     */
 | 
			
		||||
    SysSmsCodeDO checkCodeIsExpired(String mobile, String code, Integer scene);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,141 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.service.sms.impl;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.map.MapUtil;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms.SysSmsCodeMapper;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.framework.sms.SmsCodeProperties;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
import static cn.hutool.core.util.RandomUtil.randomInt;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
 | 
			
		||||
import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 短信验证码 Service 实现类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
@Validated
 | 
			
		||||
public class SysSmsCodeServiceImpl implements SysSmsCodeService {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SmsCodeProperties smsCodeProperties;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysSmsCodeMapper smsCodeMapper;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private MbrUserService mbrUserService;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysSmsCoreService smsCoreService;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void sendSmsCode(String mobile, Integer scene, String createIp) {
 | 
			
		||||
        // 创建验证码
 | 
			
		||||
        String code = this.createSmsCode(mobile, scene, createIp);
 | 
			
		||||
 | 
			
		||||
        // 获取发送模板
 | 
			
		||||
        String codeTemplate = SysSmsSceneEnum.getCodeByScene(scene);
 | 
			
		||||
 | 
			
		||||
        // 如果是更换手机号发送验证码,则需要检测手机号是否被注册
 | 
			
		||||
        if (SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene().equals(scene)){
 | 
			
		||||
            this.checkMobileIsRegister(mobile,scene);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 发送验证码
 | 
			
		||||
        smsCoreService.sendSingleSmsToMember(mobile, null, codeTemplate,
 | 
			
		||||
                MapUtil.of("code", code));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void checkMobileIsRegister(String mobile, Integer scene) {
 | 
			
		||||
        // 检测手机号是否已被使用
 | 
			
		||||
        MbrUserDO userByMobile = mbrUserService.getUserByMobile(mobile);
 | 
			
		||||
        if (userByMobile != null){
 | 
			
		||||
            throw exception(USER_SMS_CODE_IS_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 发送短信
 | 
			
		||||
        this.sendSmsCode(mobile,scene,getClientIP());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String createSmsCode(String mobile, Integer scene, String ip) {
 | 
			
		||||
        // 校验是否可以发送验证码,不用筛选场景
 | 
			
		||||
        SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,null);
 | 
			
		||||
        if (lastSmsCode != null) {
 | 
			
		||||
            if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。
 | 
			
		||||
                throw exception(USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
 | 
			
		||||
            }
 | 
			
		||||
            if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()
 | 
			
		||||
                    < smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁
 | 
			
		||||
                throw exception(USER_SMS_CODE_SEND_TOO_FAST);
 | 
			
		||||
            }
 | 
			
		||||
            // TODO 芋艿:提升,每个 IP 每天可发送数量
 | 
			
		||||
            // TODO 芋艿:提升,每个 IP 每小时可发送数量
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 创建验证码记录
 | 
			
		||||
        String code = String.valueOf(randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1));
 | 
			
		||||
        SysSmsCodeDO newSmsCode = SysSmsCodeDO.builder().mobile(mobile).code(code)
 | 
			
		||||
                .scene(scene).todayIndex(lastSmsCode != null ? lastSmsCode.getTodayIndex() + 1 : 1)
 | 
			
		||||
                .createIp(ip).used(false).build();
 | 
			
		||||
        smsCodeMapper.insert(newSmsCode);
 | 
			
		||||
        return code;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void useSmsCode(String mobile, Integer scene, String code, String usedIp) {
 | 
			
		||||
 | 
			
		||||
        // 检测验证码是否有效
 | 
			
		||||
        SysSmsCodeDO lastSmsCode = this.checkCodeIsExpired(mobile, code, scene);
 | 
			
		||||
 | 
			
		||||
        // 判断验证码是否已被使用
 | 
			
		||||
        if (Boolean.TRUE.equals(lastSmsCode.getUsed())) {
 | 
			
		||||
            throw exception(USER_SMS_CODE_USED);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 使用验证码
 | 
			
		||||
        smsCodeMapper.updateById(SysSmsCodeDO.builder().id(lastSmsCode.getId())
 | 
			
		||||
                .used(true).usedTime(new Date()).usedIp(usedIp).build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void sendSmsCodeLogin(Long userId) {
 | 
			
		||||
        MbrUserDO user = mbrUserService.getUser(userId);
 | 
			
		||||
        if (user == null){
 | 
			
		||||
            throw exception(USER_NOT_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
        // 发送验证码
 | 
			
		||||
        this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public SysSmsCodeDO checkCodeIsExpired(String mobile, String code, Integer scene) {
 | 
			
		||||
        // 校验验证码
 | 
			
		||||
        SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile,code,scene);
 | 
			
		||||
 | 
			
		||||
        // 若验证码不存在,抛出异常
 | 
			
		||||
        if (lastSmsCode == null) {
 | 
			
		||||
            throw exception(USER_SMS_CODE_NOT_FOUND);
 | 
			
		||||
        }
 | 
			
		||||
        if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()
 | 
			
		||||
                >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期
 | 
			
		||||
            throw exception(USER_SMS_CODE_EXPIRED);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return lastSmsCode;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,48 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
 | 
			
		||||
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
 | 
			
		||||
import cn.iocoder.yudao.userserver.config.RedisTestConfiguration;
 | 
			
		||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
 | 
			
		||||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
 | 
			
		||||
import org.redisson.spring.starter.RedissonAutoConfiguration;
 | 
			
		||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
 | 
			
		||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 | 
			
		||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
 | 
			
		||||
import org.springframework.boot.test.context.SpringBootTest;
 | 
			
		||||
import org.springframework.context.annotation.Import;
 | 
			
		||||
import org.springframework.test.context.ActiveProfiles;
 | 
			
		||||
import org.springframework.test.context.jdbc.Sql;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 依赖内存 DB + Redis 的单元测试
 | 
			
		||||
 *
 | 
			
		||||
 * 相比 {@link BaseDbUnitTest} 来说,额外增加了内存 Redis
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class)
 | 
			
		||||
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
 | 
			
		||||
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
 | 
			
		||||
public class BaseDbAndRedisUnitTest {
 | 
			
		||||
 | 
			
		||||
    @Import({
 | 
			
		||||
            // DB 配置类
 | 
			
		||||
            YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
 | 
			
		||||
            DataSourceAutoConfiguration.class, // Spring DB 自动配置类
 | 
			
		||||
            DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
 | 
			
		||||
            DruidDataSourceAutoConfigure.class, // Druid 自动配置类
 | 
			
		||||
            // MyBatis 配置类
 | 
			
		||||
            YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
 | 
			
		||||
            MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
 | 
			
		||||
            // Redis 配置类
 | 
			
		||||
            RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
 | 
			
		||||
            RedisAutoConfiguration.class, // Spring Redis 自动配置类
 | 
			
		||||
            YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
 | 
			
		||||
            RedissonAutoConfiguration.class, // Redisson 自动高配置类
 | 
			
		||||
    })
 | 
			
		||||
    public static class Application {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
 | 
			
		||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
 | 
			
		||||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
 | 
			
		||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 | 
			
		||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
 | 
			
		||||
import org.springframework.boot.test.context.SpringBootTest;
 | 
			
		||||
import org.springframework.context.annotation.Import;
 | 
			
		||||
import org.springframework.test.context.ActiveProfiles;
 | 
			
		||||
import org.springframework.test.context.jdbc.Sql;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 依赖内存 DB 的单元测试
 | 
			
		||||
 *
 | 
			
		||||
 * 注意,Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class)
 | 
			
		||||
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
 | 
			
		||||
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
 | 
			
		||||
public class BaseDbUnitTest {
 | 
			
		||||
 | 
			
		||||
    @Import({
 | 
			
		||||
            // DB 配置类
 | 
			
		||||
            YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
 | 
			
		||||
            DataSourceAutoConfiguration.class, // Spring DB 自动配置类
 | 
			
		||||
            DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
 | 
			
		||||
            DruidDataSourceAutoConfigure.class, // Druid 自动配置类
 | 
			
		||||
            // MyBatis 配置类
 | 
			
		||||
            YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
 | 
			
		||||
            MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
 | 
			
		||||
    })
 | 
			
		||||
    public static class Application {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.config;
 | 
			
		||||
 | 
			
		||||
import com.github.fppt.jedismock.RedisServer;
 | 
			
		||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
 | 
			
		||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import org.springframework.context.annotation.Lazy;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
@Configuration(proxyBeanMethods = false)
 | 
			
		||||
@Lazy(false) // 禁止延迟加载
 | 
			
		||||
@EnableConfigurationProperties(RedisProperties.class)
 | 
			
		||||
public class RedisTestConfiguration {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建模拟的 Redis Server 服务器
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public RedisServer redisServer(RedisProperties properties) throws IOException {
 | 
			
		||||
        RedisServer redisServer = new RedisServer(properties.getPort());
 | 
			
		||||
        // TODO 芋艿:一次执行多个单元测试时,貌似创建多个 spring 容器,导致不进行 stop。这样,就导致端口被占用,无法启动。。。
 | 
			
		||||
        try {
 | 
			
		||||
            redisServer.start();
 | 
			
		||||
        } catch (Exception ignore) {}
 | 
			
		||||
        return redisServer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,152 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.member.service;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.RandomUtil;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
 | 
			
		||||
import cn.iocoder.yudao.userserver.BaseDbAndRedisUnitTest;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.service.user.impl.MbrUserServiceImpl;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.auth.impl.SysAuthServiceImpl;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.springframework.boot.test.mock.mockito.MockBean;
 | 
			
		||||
import org.springframework.context.annotation.Import;
 | 
			
		||||
import org.springframework.data.redis.core.StringRedisTemplate;
 | 
			
		||||
import org.springframework.security.crypto.password.PasswordEncoder;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
import static cn.hutool.core.util.RandomUtil.*;
 | 
			
		||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
 | 
			
		||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
 | 
			
		||||
import static cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
import static org.mockito.Mockito.*;
 | 
			
		||||
 | 
			
		||||
// TODO @芋艿:单测的 review,等逻辑都达成一致后
 | 
			
		||||
/**
 | 
			
		||||
 * {@link MbrUserServiceImpl} 的单元测试类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 宋天
 | 
			
		||||
 */
 | 
			
		||||
@Import({MbrUserServiceImpl.class, YudaoRedisAutoConfiguration.class})
 | 
			
		||||
public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private MbrUserServiceImpl mbrUserService;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private StringRedisTemplate stringRedisTemplate;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private MbrUserMapper userMapper;
 | 
			
		||||
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private SysAuthServiceImpl authService;
 | 
			
		||||
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private InfFileCoreService fileCoreService;
 | 
			
		||||
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private PasswordEncoder passwordEncoder;
 | 
			
		||||
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private SysSmsCodeService sysSmsCodeService;
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testUpdateNickName_success(){
 | 
			
		||||
        // mock 数据
 | 
			
		||||
        MbrUserDO userDO = randomMbrUserDO();
 | 
			
		||||
        userMapper.insert(userDO);
 | 
			
		||||
 | 
			
		||||
        // 随机昵称
 | 
			
		||||
        String newNickName = randomString();
 | 
			
		||||
 | 
			
		||||
        // 调用接口修改昵称
 | 
			
		||||
        mbrUserService.updateNickname(userDO.getId(),newNickName);
 | 
			
		||||
        // 查询新修改后的昵称
 | 
			
		||||
        String nickname = mbrUserService.getUser(userDO.getId()).getNickname();
 | 
			
		||||
        // 断言
 | 
			
		||||
        assertEquals(newNickName,nickname);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGetUserInfo_success(){
 | 
			
		||||
        // mock 数据
 | 
			
		||||
        MbrUserDO userDO = randomMbrUserDO();
 | 
			
		||||
        userMapper.insert(userDO);
 | 
			
		||||
 | 
			
		||||
        // 查询用户昵称与头像
 | 
			
		||||
        MbrUserInfoRespVO userInfo = mbrUserService.getUserInfo(userDO.getId());
 | 
			
		||||
        System.out.println(userInfo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testUpdateAvatar_success(){
 | 
			
		||||
        // mock 数据
 | 
			
		||||
        MbrUserDO dbUser = randomMbrUserDO();
 | 
			
		||||
        userMapper.insert(dbUser);
 | 
			
		||||
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        Long userId = dbUser.getId();
 | 
			
		||||
        byte[] avatarFileBytes = randomBytes(10);
 | 
			
		||||
        ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes);
 | 
			
		||||
        // mock 方法
 | 
			
		||||
        String avatar = randomString();
 | 
			
		||||
        when(fileCoreService.createFile(anyString(), eq(avatarFileBytes))).thenReturn(avatar);
 | 
			
		||||
        // 调用
 | 
			
		||||
        String str = mbrUserService.updateAvatar(userId, avatarFile);
 | 
			
		||||
        // 断言
 | 
			
		||||
        assertEquals(avatar, str);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void updateMobile_success(){
 | 
			
		||||
        // mock数据
 | 
			
		||||
        String oldMobile = randomNumbers(11);
 | 
			
		||||
        MbrUserDO userDO = randomMbrUserDO();
 | 
			
		||||
        userDO.setMobile(oldMobile);
 | 
			
		||||
        userMapper.insert(userDO);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // 旧手机和旧验证码
 | 
			
		||||
        SysSmsCodeDO codeDO = new SysSmsCodeDO();
 | 
			
		||||
        String oldCode = RandomUtil.randomString(4);
 | 
			
		||||
        codeDO.setMobile(userDO.getMobile());
 | 
			
		||||
        codeDO.setCode(oldCode);
 | 
			
		||||
        codeDO.setScene(CHANGE_MOBILE_BY_SMS.getScene());
 | 
			
		||||
        codeDO.setUsed(Boolean.FALSE);
 | 
			
		||||
        when(sysSmsCodeService.checkCodeIsExpired(codeDO.getMobile(),codeDO.getCode(),codeDO.getScene())).thenReturn(codeDO);
 | 
			
		||||
 | 
			
		||||
        // 更新手机号
 | 
			
		||||
        String newMobile = randomNumbers(11);
 | 
			
		||||
        String newCode = randomNumbers(4);
 | 
			
		||||
        MbrUserUpdateMobileReqVO reqVO = new MbrUserUpdateMobileReqVO();
 | 
			
		||||
        reqVO.setMobile(newMobile);
 | 
			
		||||
        reqVO.setCode(newCode);
 | 
			
		||||
        reqVO.setOldMobile(oldMobile);
 | 
			
		||||
        reqVO.setOldCode(oldCode);
 | 
			
		||||
        mbrUserService.updateMobile(userDO.getId(),reqVO);
 | 
			
		||||
 | 
			
		||||
        assertEquals(mbrUserService.getUser(userDO.getId()).getMobile(),newMobile);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ========== 随机对象 ==========
 | 
			
		||||
 | 
			
		||||
    @SafeVarargs
 | 
			
		||||
    private static MbrUserDO randomMbrUserDO(Consumer<MbrUserDO>... consumers) {
 | 
			
		||||
        Consumer<MbrUserDO> consumer = (o) -> {
 | 
			
		||||
            o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
 | 
			
		||||
        };
 | 
			
		||||
        return randomPojo(MbrUserDO.class, ArrayUtils.append(consumer, consumers));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,127 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.userserver.modules.system.service;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService;
 | 
			
		||||
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
 | 
			
		||||
import cn.iocoder.yudao.userserver.BaseDbAndRedisUnitTest;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.MbrAuthResetPasswordReqVO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.MbrAuthUpdatePasswordReqVO;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.auth.impl.SysAuthServiceImpl;
 | 
			
		||||
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.springframework.boot.test.mock.mockito.MockBean;
 | 
			
		||||
import org.springframework.context.annotation.Import;
 | 
			
		||||
import org.springframework.data.redis.core.StringRedisTemplate;
 | 
			
		||||
import org.springframework.security.authentication.AuthenticationManager;
 | 
			
		||||
import org.springframework.security.crypto.password.PasswordEncoder;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
import static cn.hutool.core.util.RandomUtil.randomEle;
 | 
			
		||||
import static cn.hutool.core.util.RandomUtil.randomNumbers;
 | 
			
		||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
 | 
			
		||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
import static org.mockito.Mockito.when;
 | 
			
		||||
 | 
			
		||||
// TODO @芋艿:单测的 review,等逻辑都达成一致后
 | 
			
		||||
/**
 | 
			
		||||
 * {@link SysAuthService} 的单元测试类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 宋天
 | 
			
		||||
 */
 | 
			
		||||
@Import({SysAuthServiceImpl.class, YudaoRedisAutoConfiguration.class})
 | 
			
		||||
public class SysAuthServiceTest extends BaseDbAndRedisUnitTest {
 | 
			
		||||
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private AuthenticationManager authenticationManager;
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private MbrUserService userService;
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private SysSmsCodeService smsCodeService;
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private SysLoginLogCoreService loginLogCoreService;
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private SysUserSessionCoreService userSessionCoreService;
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private SysSocialCoreService socialService;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private StringRedisTemplate stringRedisTemplate;
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private PasswordEncoder passwordEncoder;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private MbrUserMapper mbrUserMapper;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SysAuthServiceImpl authService;
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testUpdatePassword_success(){
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        MbrUserDO userDO = randomMbrUserDO();
 | 
			
		||||
        mbrUserMapper.insert(userDO);
 | 
			
		||||
 | 
			
		||||
        // 新密码
 | 
			
		||||
        String newPassword = randomString();
 | 
			
		||||
 | 
			
		||||
        // 请求实体
 | 
			
		||||
        MbrAuthUpdatePasswordReqVO reqVO = MbrAuthUpdatePasswordReqVO.builder()
 | 
			
		||||
                .oldPassword(userDO.getPassword())
 | 
			
		||||
                .password(newPassword)
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        // 测试桩
 | 
			
		||||
        // 这两个相等是为了返回ture这个结果
 | 
			
		||||
        when(passwordEncoder.matches(reqVO.getOldPassword(),reqVO.getOldPassword())).thenReturn(true);
 | 
			
		||||
        when(passwordEncoder.encode(newPassword)).thenReturn(newPassword);
 | 
			
		||||
 | 
			
		||||
        // 更新用户密码
 | 
			
		||||
        authService.updatePassword(userDO.getId(),reqVO);
 | 
			
		||||
        assertEquals(mbrUserMapper.selectById(userDO.getId()).getPassword(),newPassword);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testResetPassword_success(){
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        MbrUserDO userDO = randomMbrUserDO();
 | 
			
		||||
        mbrUserMapper.insert(userDO);
 | 
			
		||||
 | 
			
		||||
        // 随机密码
 | 
			
		||||
        String password = randomNumbers(11);
 | 
			
		||||
        // 随机验证码
 | 
			
		||||
        String code = randomNumbers(4);
 | 
			
		||||
 | 
			
		||||
        // mock
 | 
			
		||||
        when(passwordEncoder.encode(password)).thenReturn(password);
 | 
			
		||||
 | 
			
		||||
        // 更新用户密码
 | 
			
		||||
        MbrAuthResetPasswordReqVO reqVO = new MbrAuthResetPasswordReqVO();
 | 
			
		||||
        reqVO.setMobile(userDO.getMobile());
 | 
			
		||||
        reqVO.setPassword(password);
 | 
			
		||||
        reqVO.setCode(code);
 | 
			
		||||
 | 
			
		||||
        authService.resetPassword(reqVO);
 | 
			
		||||
        assertEquals(mbrUserMapper.selectById(userDO.getId()).getPassword(),password);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // ========== 随机对象 ==========
 | 
			
		||||
 | 
			
		||||
    @SafeVarargs
 | 
			
		||||
    private static MbrUserDO randomMbrUserDO(Consumer<MbrUserDO>... consumers) {
 | 
			
		||||
        Consumer<MbrUserDO> consumer = (o) -> {
 | 
			
		||||
            o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
 | 
			
		||||
            o.setPassword(randomString());
 | 
			
		||||
        };
 | 
			
		||||
        return randomPojo(MbrUserDO.class, ArrayUtils.append(consumer, consumers));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
spring:
 | 
			
		||||
  main:
 | 
			
		||||
    lazy-initialization: true # 开启懒加载,加快速度
 | 
			
		||||
    banner-mode: off # 单元测试,禁用 Banner
 | 
			
		||||
 | 
			
		||||
--- #################### 数据库相关配置 ####################
 | 
			
		||||
 | 
			
		||||
spring:
 | 
			
		||||
  # 数据源配置项
 | 
			
		||||
  datasource:
 | 
			
		||||
    name: ruoyi-vue-pro
 | 
			
		||||
    url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
 | 
			
		||||
    driver-class-name: org.h2.Driver
 | 
			
		||||
    username: sa
 | 
			
		||||
    password:
 | 
			
		||||
    schema: classpath:sql/create_tables.sql # MySQL 转 H2 的语句,使用 https://www.jooq.org/translate/ 工具
 | 
			
		||||
    druid:
 | 
			
		||||
      async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
 | 
			
		||||
      initial-size: 1 # 单元测试,配置为 1,提升启动速度
 | 
			
		||||
 | 
			
		||||
  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
 | 
			
		||||
  redis:
 | 
			
		||||
    host: 127.0.0.1 # 地址
 | 
			
		||||
    port: 16379 # 端口(单元测试,使用 16379 端口)
 | 
			
		||||
    database: 0 # 数据库索引
 | 
			
		||||
 | 
			
		||||
mybatis:
 | 
			
		||||
  lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
 | 
			
		||||
 | 
			
		||||
--- #################### 定时任务相关配置 ####################
 | 
			
		||||
 | 
			
		||||
--- #################### 配置中心相关配置 ####################
 | 
			
		||||
 | 
			
		||||
--- #################### 服务保障相关配置 ####################
 | 
			
		||||
 | 
			
		||||
# Lock4j 配置项(单元测试,禁用 Lock4j)
 | 
			
		||||
 | 
			
		||||
# Resilience4j 配置项
 | 
			
		||||
 | 
			
		||||
--- #################### 监控相关配置 ####################
 | 
			
		||||
 | 
			
		||||
--- #################### 芋道相关配置 ####################
 | 
			
		||||
 | 
			
		||||
# 芋道配置项,设置当前项目所有自定义的配置
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 18 KiB  | 
@@ -1,4 +0,0 @@
 | 
			
		||||
<configuration>
 | 
			
		||||
    <!-- 引用 Spring Boot 的 logback 基础配置 -->
 | 
			
		||||
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
 | 
			
		||||
</configuration>
 | 
			
		||||
@@ -1,2 +0,0 @@
 | 
			
		||||
-- mbr 开头的 DB
 | 
			
		||||
DELETE FROM "mbr_user";
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
-- mbr 开头的 DB
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "mbr_user"  (
 | 
			
		||||
    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
 | 
			
		||||
    "nickname" varchar(30)  NOT NULL DEFAULT '' COMMENT '用户昵称',
 | 
			
		||||
    "avatar" varchar(255)  NOT NULL DEFAULT '' COMMENT '头像',
 | 
			
		||||
    "status" tinyint NOT NULL COMMENT '状态',
 | 
			
		||||
    "mobile" varchar(11)  NOT NULL COMMENT '手机号',
 | 
			
		||||
    "password" varchar(100)  NOT NULL DEFAULT '' COMMENT '密码',
 | 
			
		||||
    "register_ip" varchar(32)  NOT NULL COMMENT '注册 IP',
 | 
			
		||||
    "login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP',
 | 
			
		||||
    "login_date" datetime NULL DEFAULT NULL COMMENT '最后登录时间',
 | 
			
		||||
    "creator" varchar(64)  NULL DEFAULT '' COMMENT '创建者',
 | 
			
		||||
    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
 | 
			
		||||
    "updater" varchar(64)  NULL DEFAULT '' COMMENT '更新者',
 | 
			
		||||
    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
 | 
			
		||||
    "deleted" bit(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
 | 
			
		||||
    "tenant_id" bigint not null default  '0',
 | 
			
		||||
    PRIMARY KEY ("id")
 | 
			
		||||
) COMMENT '会员表';
 | 
			
		||||
 | 
			
		||||
-- inf 开头的 DB
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "inf_file" (
 | 
			
		||||
    "id" varchar(188) NOT NULL,
 | 
			
		||||
    "type" varchar(63) DEFAULT NULL,
 | 
			
		||||
    "content" blob NOT NULL,
 | 
			
		||||
    "creator" varchar(64) DEFAULT '',
 | 
			
		||||
    "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
    "updater" varchar(64) DEFAULT '',
 | 
			
		||||
    "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
    "deleted" bit NOT NULL DEFAULT FALSE,
 | 
			
		||||
    PRIMARY KEY ("id")
 | 
			
		||||
) COMMENT '文件表';
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user