重写用户快速登录逻辑

This commit is contained in:
YunaiV
2020-07-03 19:12:56 +08:00
parent ee7cf3e871
commit 3d6bd5e4ee
57 changed files with 1107 additions and 228 deletions

View File

@@ -16,6 +16,16 @@
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,72 @@
package cn.iocoder.mall.userservice.enums;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
/**
* 错误码枚举类
*
* system 系统,使用 1-002-000-000 段
*/
public enum UserErrorCodeEnum implements ServiceExceptionUtil.Enumerable<UserErrorCodeEnum> {
// ========== OAUTH2 模块 ==========
OAUTH2_UNKNOWN(1001001000, "未知错误"), // 预留
// 预留 1001001001 ~ 1001001099 错误码,方便前端
OAUTH2_ACCESS_TOKEN_NOT_FOUND(1001001001, "访问令牌不存在"),
OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED(1001001002, "访问令牌已过期"),
OAUTH2_ACCESS_TOKEN_INVALID(1001001003, "访问令牌已失效"),
OAUTH2_NOT_AUTHENTICATE(1001001004, "账号未登陆"),
OAUTH2_REFRESH_TOKEN_NOT_FOUND(1001001005, "刷新令牌不存在"),
OAUTH_REFRESH_TOKEN_EXPIRED(1001001006, "访问令牌已过期"),
OAUTH_REFRESH_TOKEN_INVALID(1001001007, "刷新令牌已失效"),
// 其它 1001001100 开始
OAUTH2_ACCOUNT_NOT_FOUND(1001001100, "账号不存在"),
OAUTH2_ACCOUNT_PASSWORD_ERROR(1001001101, "密码不正确"),
// ========== 用户手机验证码模块 ==========
USER_SMS_CODE_NOT_FOUND(1001001200, "验证码不存在"),
USER_SMS_CODE_EXPIRED(1001001201, "验证码已过期"),
USER_SMS_CODE_USED(1001001202, "验证码已使用"),
USER_SMS_CODE_NOT_CORRECT(1001001203, "验证码不正确"),
USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY(1001001204, "超过每日短信发送数量"),
USER_SMS_CODE_SEND_TOO_FAST(1001001205, "短信发送过于频率"),
// ========== 用户地址 ==========
USER_ADDRESS_NOT_EXISTENT(1001004000, "用户地址不存在!"),
USER_ADDRESS_IS_DELETED(1001004001, "用户地址已被删除!"),
USER_GET_ADDRESS_NOT_EXISTS(1001004002, "获取的地址不存在!"),
// ========== 用户信息模块 1004004100 ==========
USER_NOT_EXISTS(1004004100, "用户不存在"),
USER_STATUS_NOT_EXISTS(1004004101, "用户状态不存在"),
USER_STATUS_EQUALS(1004004101, "用户已经是该状态"),
;
private final int code;
private final String message;
UserErrorCodeEnum(int code, String message) {
this.code = code;
this.message = message;
}
@Override
public int getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
// TODO: 2020-05-22 封装成start的时候直接在start中定义一个统一的枚举从中取值
@Override
public int getGroup() {
return 0;
}
}

View File

@@ -0,0 +1,39 @@
package cn.iocoder.mall.userservice.enums.sms;
import cn.iocoder.common.framework.core.IntArrayValuable;
import java.util.Arrays;
/**
* 用户短信验证码发送场景的枚举
*/
public enum UserSmsSceneEnum implements IntArrayValuable {
LOGIN_BY_SMS(1, "手机号登陆"),
CHANGE_MOBILE_BY_SMS(2, "更换手机号"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserSmsSceneEnum::getValue).toArray();
private final Integer value;
private final String name;
UserSmsSceneEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
@Override
public int[] array() {
return ARRAYS;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.mall.userservice.rpc.sms;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.userservice.rpc.sms.vo.UserSendSmsCodeDTO;
import cn.iocoder.mall.userservice.rpc.sms.vo.UserVerifySmsCodeDTO;
/**
* 用户短信验证码 Rpc 接口
*/
public interface UserSmsCodeRpc {
CommonResult<Boolean> sendSmsCode(UserSendSmsCodeDTO sendSmsCodeDTO);
CommonResult<Boolean> verifySmsCode(UserVerifySmsCodeDTO verifySmsCodeDTO);
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.mall.userservice.rpc.sms.vo;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.userservice.enums.sms.UserSmsSceneEnum;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 用户发送短信验证码 DTO
*/
@Data
@Accessors(chain = true)
@Builder
public class UserSendSmsCodeDTO implements Serializable {
/**
* 手机号码
*/
@NotNull(message = "手机号码不能为空")
private String mobile;
/**
* IP
*/
@NotNull(message = "IP 不能为空")
private String ip;
/**
* 发送场景
*/
@NotNull(message = "发送场景不能为空")
@InEnum(value = UserSmsSceneEnum.class, message = "发送场景不能为空")
private Integer scene;
}

View File

@@ -0,0 +1,42 @@
package cn.iocoder.mall.userservice.rpc.sms.vo;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.userservice.enums.sms.UserSmsSceneEnum;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 用户校验验证码 DTO
*/
@Data
@Accessors(chain = true)
@Builder
public class UserVerifySmsCodeDTO implements Serializable {
/**
* 手机号码
*/
@NotNull(message = "手机号码不能为空")
private String mobile;
/**
* IP
*/
@NotNull(message = "IP 不能为空")
private String ip;
/**
* 发送场景
*/
@NotNull(message = "发送场景不能为空")
@InEnum(value = UserSmsSceneEnum.class, message = "发送场景不能为空")
private Integer scene;
/**
* 验证码
*/
@NotNull(message = "验证码不能为空")
private String code;
}

View File

@@ -1,10 +1,13 @@
package cn.iocoder.mall.userservice.rpc.user;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.userservice.rpc.user.dto.UserCreateDTO;
import cn.iocoder.mall.userservice.rpc.user.vo.UserVO;
public interface UserRpc {
CommonResult<UserVO> getUser(Integer id);
CommonResult<UserVO> createUserIfAbsent(UserCreateDTO createDTO);
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.mall.userservice.rpc.user.dto;
import cn.iocoder.common.framework.validator.Mobile;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 用户创建 DTO
*/
@Data
@Accessors(chain = true)
public class UserCreateDTO implements Serializable {
/**
* 手机号
*/
@NotNull(message = "手机号不能为空")
@Mobile(message = "手机格式不正确")
private String mobile;
/**
* 密码
*
* 允许为空,自动生成
*/
private String password;
/**
* IP
*/
@NotNull(message = "IP 不能为空")
private String ip;
}

View File

@@ -1,5 +1,7 @@
package cn.iocoder.mall.userservice.rpc.user.vo;
import cn.iocoder.common.framework.constant.CommonStatusEnum;
import java.io.Serializable;
/**

View File

@@ -69,6 +69,19 @@
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>javax.el</groupId>-->
<!-- <artifactId>javax.el-api</artifactId>-->
<!-- <version>3.0.0</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>javax.el</groupId>-->
<!-- <artifactId>javax.el-api</artifactId>-->
<!-- <version>3.0.1-b06</version>-->
<!-- </dependency>-->
</dependencies>
</project>

View File

@@ -3,7 +3,7 @@ package cn.iocoder.mall.userservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.userservice"})
@SpringBootApplication
public class UserServiceApplication {
public static void main(String[] args) {

View File

@@ -3,7 +3,9 @@ package cn.iocoder.mall.userservice.convert.user;
import cn.iocoder.mall.userservice.service.user.bo.UserBO;
import cn.iocoder.mall.userservice.dal.mysql.dataobject.user.UserDO;
import cn.iocoder.mall.userservice.rpc.user.vo.UserVO;
import cn.iocoder.mall.userservice.service.user.bo.UserCreateBO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
@@ -15,4 +17,7 @@ public interface UserConvert {
UserBO convert(UserDO bean);
@Mapping(source = "ip", target = "createIp")
UserDO convert(UserCreateBO bean);
}

View File

@@ -0,0 +1,60 @@
package cn.iocoder.mall.userservice.dal.mysql.dataobject.sms;
import cn.iocoder.mall.mybatis.dataobject.BaseDO;
import cn.iocoder.mall.userservice.enums.sms.UserSmsSceneEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 手机验证码 DO
*/
@TableName("user_sms_code")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class UserSmsCodeDO extends BaseDO {
/**
* 编号
*/
private Integer id;
/**
* 手机号
*/
private String mobile;
/**
* 验证码
*/
private String code;
/**
* 发送场景
*
* 外键 {@link UserSmsSceneEnum}
*/
private Integer scene;
/**
* 创建 IP
*/
private String createIp;
/**
* 今日发送的第几条
*/
private Integer todayIndex;
/**
* 是否使用
*/
private Boolean used;
/**
* 使用时间
*/
private Date usedTime;
/**
* 使用 IP
*/
private String usedIp;
}

View File

@@ -46,5 +46,9 @@ public class UserDO extends DeletableDO {
* {@link #password} 的盐
*/
private String passwordSalt;
/**
* 注册 IP
*/
private String createIp;
}

View File

@@ -1 +0,0 @@
package cn.iocoder.mall.userservice.dal.mysql.mapper;

View File

@@ -0,0 +1,27 @@
package cn.iocoder.mall.userservice.dal.mysql.mapper.sms;
import cn.iocoder.mall.mybatis.query.QueryWrapperX;
import cn.iocoder.mall.userservice.dal.mysql.dataobject.sms.UserSmsCodeDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UserSmsCodeMapper extends BaseMapper<UserSmsCodeDO> {
/**
* 获得手机号的最后一个手机验证码
*
* @param mobile 手机号
* @param scene 发送场景,选填
* @return 手机验证码
*/
default UserSmsCodeDO selectLastByMobile(String mobile, Integer scene) {
QueryWrapperX<UserSmsCodeDO> query = new QueryWrapperX<UserSmsCodeDO>()
.eq("mobile", mobile)
.eqIfPresent("scene", scene)
.orderByDesc("id")
.last("limit 1");
return selectOne(query);
}
}

View File

@@ -1,12 +1,17 @@
package cn.iocoder.mall.userservice.dal.mysql.mapper.user;
import cn.iocoder.mall.userservice.dal.mysql.dataobject.user.UserDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper<UserDO> {
default UserDO selectByMobile(String mobile) {
return selectOne(new QueryWrapper<UserDO>()
.eq("mobile", mobile)
);
}
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.mall.userservice.manager.sms;
import cn.iocoder.mall.userservice.rpc.sms.vo.UserSendSmsCodeDTO;
import cn.iocoder.mall.userservice.rpc.sms.vo.UserVerifySmsCodeDTO;
import cn.iocoder.mall.userservice.service.sms.UserSmsCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserSmsCodeManager {
@Autowired
private UserSmsCodeService userSmsCodeService;
public void sendSmsCode(UserSendSmsCodeDTO sendSmsCodeDTO) {
// 生成短信验证码
String smsCode = userSmsCodeService.createSmsCode(sendSmsCodeDTO.getMobile(),
sendSmsCodeDTO.getScene(), sendSmsCodeDTO.getIp());
// TODO 调用发送验证码
}
public void verifySmsCode(UserVerifySmsCodeDTO verifySmsCodeDTO) {
userSmsCodeService.verifySmsCode(verifySmsCodeDTO.getMobile(), verifySmsCodeDTO.getCode(),
verifySmsCodeDTO.getScene(), verifySmsCodeDTO.getIp());
}
}

View File

@@ -1,5 +1,6 @@
package cn.iocoder.mall.userservice.manager.user;
import cn.iocoder.mall.userservice.rpc.user.dto.UserCreateDTO;
import cn.iocoder.mall.userservice.service.user.bo.UserBO;
import cn.iocoder.mall.userservice.service.user.UserService;
import cn.iocoder.mall.userservice.convert.user.UserConvert;
@@ -18,4 +19,15 @@ public class UserManager {
return UserConvert.INSTANCE.convert(userBO);
}
public UserVO createUserIfAbsent(UserCreateDTO createDTO) {
// 用户已经存在
UserBO userBO = userService.getUser(createDTO.getMobile());
if (userBO != null) {
return UserConvert.INSTANCE.convert(userBO);
}
// 用户不存在,则进行创建
return null;
}
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.mall.userservice.rpc.sms;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.userservice.manager.sms.UserSmsCodeManager;
import cn.iocoder.mall.userservice.rpc.sms.vo.UserSendSmsCodeDTO;
import cn.iocoder.mall.userservice.rpc.sms.vo.UserVerifySmsCodeDTO;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service(version = "${dubbo.provider.UserSmsCodeRpc.version}", validation = "false")
public class UserSmsCodeRpcImpl implements UserSmsCodeRpc {
@Autowired
private UserSmsCodeManager userSmsCodeManager;
@Override
public CommonResult<Boolean> sendSmsCode(UserSendSmsCodeDTO sendSmsCodeDTO) {
userSmsCodeManager.sendSmsCode(sendSmsCodeDTO);
return CommonResult.success(true);
}
@Override
public CommonResult<Boolean> verifySmsCode(UserVerifySmsCodeDTO verifySmsCodeDTO) {
userSmsCodeManager.verifySmsCode(verifySmsCodeDTO);
return CommonResult.success(true);
}
}

View File

@@ -2,13 +2,14 @@ package cn.iocoder.mall.userservice.rpc.user;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.userservice.manager.user.UserManager;
import cn.iocoder.mall.userservice.rpc.user.dto.UserCreateDTO;
import cn.iocoder.mall.userservice.rpc.user.vo.UserVO;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@Service(version = "${dubbo.provider.UserRpc.version}", validation = "true")
@Service(version = "${dubbo.provider.UserRpc.version}", validation = "false")
public class UserRpcImpl implements UserRpc {
@Autowired
@@ -19,4 +20,9 @@ public class UserRpcImpl implements UserRpc {
return success(userManager.getUser(id));
}
@Override
public CommonResult<UserVO> createUserIfAbsent(UserCreateDTO createDTO) {
return success(userManager.createUserIfAbsent(createDTO));
}
}

View File

@@ -0,0 +1,107 @@
package cn.iocoder.mall.userservice.service.sms;
import cn.iocoder.common.framework.constant.SysErrorCodeEnum;
import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.util.ValidationUtil;
import cn.iocoder.mall.userservice.dal.mysql.dataobject.sms.UserSmsCodeDO;
import cn.iocoder.mall.userservice.dal.mysql.mapper.sms.UserSmsCodeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Date;
import static cn.iocoder.mall.userservice.enums.UserErrorCodeEnum.*;
@Service
public class UserSmsCodeService {
/**
* 每条验证码的过期时间,单位:毫秒
*/
@Value("${modules.user-sms-code-service.code-expire-time-millis}")
private int codeExpireTimes;
/**
* 每日发送最大数量
*/
@Value("${modules.user-sms-code-service.send-maximum-quantity-per-day}")
private int sendMaximumQuantityPerDay;
/**
* 短信发送频率,单位:毫秒
*/
@Value("${modules.user-sms-code-service.send-frequency}")
private int sendFrequency;
@Autowired
private UserSmsCodeMapper userSmsCodeMapper;
/**
* 创建短信验证码,并返回它
*
* 注意,不包括发送逻辑
*
* @param mobile 手机号
* @param scene 发送场景
* @param ip IP
* @return 短信验证码
*/
public String createSmsCode(String mobile, Integer scene, String ip) {
if (!ValidationUtil.isMobile(mobile)) {
throw ServiceExceptionUtil.exception(SysErrorCodeEnum.VALIDATION_REQUEST_PARAM_ERROR.getCode(), "手机格式不正确"); // TODO 有点搓
}
// 校验是否可以发送验证码,不用筛选场景
UserSmsCodeDO lastUserSmsCodeDO = userSmsCodeMapper.selectLastByMobile(mobile, null);
if (lastUserSmsCodeDO != null) {
if (lastUserSmsCodeDO.getTodayIndex() >= sendMaximumQuantityPerDay) { // 超过当天发送的上限。
throw ServiceExceptionUtil.exception(USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
}
if (System.currentTimeMillis() - lastUserSmsCodeDO.getCreateTime().getTime() < sendFrequency) { // 发送过于频繁
throw ServiceExceptionUtil.exception(USER_SMS_CODE_SEND_TOO_FAST);
}
// TODO 提升,每个 IP 每天可发送数量
// TODO 提升,每个 IP 每小时可发送数量
}
// 创建验证码记录
UserSmsCodeDO newMobileCodePO = new UserSmsCodeDO().setMobile(mobile)
.setCode("9999") // TODO 芋艿,随机 4 位验证码 or 6 位验证码
.setScene(scene)
.setTodayIndex(lastUserSmsCodeDO != null ? lastUserSmsCodeDO.getTodayIndex() : 1)
.setCreateIp(ip).setUsed(false);
newMobileCodePO.setCreateTime(new Date());
userSmsCodeMapper.insert(newMobileCodePO);
return newMobileCodePO.getCode();
}
/**
* 验证短信验证码是否正确。
* 如果正确,则将验证码标记成已使用
* 如果错误,则抛出 {@link ServiceException} 异常
*
* @param mobile 手机号
* @param code 验证码
* @param scene 发送场景
* @param ip IP
*/
public void verifySmsCode(String mobile, String code, Integer scene, String ip) {
// 校验验证码
UserSmsCodeDO mobileCodeDO = userSmsCodeMapper.selectLastByMobile(mobile, scene);
if (mobileCodeDO == null) { // 若验证码不存在,抛出异常
throw ServiceExceptionUtil.exception(USER_SMS_CODE_NOT_FOUND);
}
if (System.currentTimeMillis() - mobileCodeDO.getCreateTime().getTime() >= codeExpireTimes) { // 验证码已过期
throw ServiceExceptionUtil.exception(USER_SMS_CODE_EXPIRED);
}
if (mobileCodeDO.getUsed()) { // 验证码已使用
throw ServiceExceptionUtil.exception(USER_SMS_CODE_USED);
}
if (!mobileCodeDO.getCode().equals(code)) {
throw ServiceExceptionUtil.exception(USER_SMS_CODE_NOT_CORRECT);
}
// 使用验证码
UserSmsCodeDO updateObj = new UserSmsCodeDO().setId(mobileCodeDO.getId())
.setUsed(true).setUsedTime(new Date()).setUsedIp(ip);
userSmsCodeMapper.updateById(updateObj);
}
}

View File

@@ -1,9 +1,12 @@
package cn.iocoder.mall.userservice.service.user;
import cn.iocoder.common.framework.util.DigestUtils;
import cn.iocoder.common.framework.util.StringUtils;
import cn.iocoder.mall.userservice.convert.user.UserConvert;
import cn.iocoder.mall.userservice.dal.mysql.dataobject.user.UserDO;
import cn.iocoder.mall.userservice.dal.mysql.mapper.user.UserMapper;
import cn.iocoder.mall.userservice.service.user.bo.UserBO;
import cn.iocoder.mall.userservice.service.user.bo.UserCreateBO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -18,4 +21,36 @@ public class UserService {
return UserConvert.INSTANCE.convert(userDO);
}
public UserBO getUser(String mobile) {
UserDO userDO = userMapper.selectByMobile(mobile);
return UserConvert.INSTANCE.convert(userDO);
}
public UserBO createUser(UserCreateBO createBO) {
UserDO userDO = UserConvert.INSTANCE.convert(createBO);
// 加密密码
String passwordSalt = genPasswordSalt();
String password = createBO.getPassword();
if (!StringUtils.hasText(password)) {
password = genPassword();
}
password = encodePassword(password, passwordSalt);
userDO.setPassword(password).setPasswordSalt(passwordSalt);
// 保存用户
userMapper.insert(userDO);
return UserConvert.INSTANCE.convert(userDO);
}
private String genPasswordSalt() {
return StringUtils.uuid(true);
}
private String genPassword() {
return StringUtils.uuid(true);
}
private String encodePassword(String password, String salt) {
return DigestUtils.bcrypt(password, salt);
}
}

View File

@@ -1,4 +1,41 @@
package cn.iocoder.mall.userservice.service.user.bo;
import cn.iocoder.common.framework.constant.CommonStatusEnum;
/**
* 用户信息
*/
public class UserBO {
/**
* 用户编号
*/
private Integer id;
/**
* 昵称
*/
private String nickname;
/**
* 头像
*/
private String avatar;
/**
* 用户状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 手机
*/
private String mobile;
/**
* 经过加密的密码串
*/
private String password;
/**
* {@link #password} 的盐
*/
private String passwordSalt;
}

View File

@@ -0,0 +1,39 @@
package cn.iocoder.mall.userservice.service.user.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
/**
* 用户创建 BO
*/
@Data
@Accessors(chain = true)
public class UserCreateBO {
/**
* 昵称,允许空
*/
private String nickname;
/**
* 头像,允许空
*/
private String avatar;
/**
* 手机
*/
@NotNull(message = "手机号不能为空")
private String mobile;
/**
* 原始密码,允许空
*
* 当为空时,会自动进行生成
*/
private String password;
/**
* IP 地址
*/
private String ip;
}

View File

@@ -0,0 +1,5 @@
##################### 业务模块 #####################
## UserSmsCodeService
modules.user-sms-code-service.code-expire-time-millis = 600000
modules.user-sms-code-service.send-maximum-quantity-per-day = 10
modules.user-sms-code-service.send-frequency = 60000

View File

@@ -35,3 +35,5 @@ dubbo:
filter: -exception
UserRpc:
version: 1.0.0
UserSmsCodeRpc:
version: 1.0.0