重写用户快速登录逻辑
This commit is contained in:
parent
ee7cf3e871
commit
3d6bd5e4ee
@ -0,0 +1,14 @@
|
||||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
|
||||
/**
|
||||
* 加解密工具类
|
||||
*/
|
||||
public class DigestUtils {
|
||||
|
||||
public static String bcrypt(String key, String salt) {
|
||||
return BCrypt.hashpw(key, salt);
|
||||
}
|
||||
|
||||
}
|
@ -7,7 +7,7 @@ import org.apache.skywalking.apm.toolkit.trace.TraceContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MallUtil {
|
||||
public class MallUtils {
|
||||
|
||||
public static Integer getUserId(ServletRequest request) {
|
||||
return (Integer) request.getAttribute(MallConstants.REQUEST_ATTR_USER_ID_KEY);
|
||||
@ -46,7 +46,7 @@ public class MallUtil {
|
||||
*/
|
||||
public static String getTraceId() {
|
||||
String traceId = TraceContext.traceId();
|
||||
if (StringUtil.hasText(traceId)) {
|
||||
if (StringUtils.hasText(traceId)) {
|
||||
return traceId;
|
||||
}
|
||||
// TODO 芋艿 多次调用会问题
|
@ -85,7 +85,7 @@ public class ServiceExceptionUtil {
|
||||
* @return 异常
|
||||
*/
|
||||
public static ServiceException exception(Integer code) {
|
||||
return new ServiceException(code, messages.get(code));
|
||||
return exception(code, messages.get(code));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,11 +96,10 @@ public class ServiceExceptionUtil {
|
||||
* @return 异常
|
||||
*/
|
||||
public static ServiceException exception(Integer code, Object... params) {
|
||||
String message = doFormat(code, messages.get(code), params);
|
||||
return new ServiceException(code, message);
|
||||
return exception0(code, messages.get(code), params);
|
||||
}
|
||||
|
||||
public static ServiceException exception(Integer code, String messagePattern, Object... params) {
|
||||
public static ServiceException exception0(Integer code, String messagePattern, Object... params) {
|
||||
String message = doFormat(code, messagePattern, params);
|
||||
return new ServiceException(code, message);
|
||||
}
|
||||
|
@ -1,29 +1,26 @@
|
||||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class StringUtil {
|
||||
public class StringUtils {
|
||||
|
||||
public static boolean hasText(String str) {
|
||||
return StringUtils.hasText(str);
|
||||
return org.springframework.util.StringUtils.hasText(str);
|
||||
}
|
||||
|
||||
public static String join(Collection<?> coll, String delim) {
|
||||
return StringUtils.collectionToDelimitedString(coll, delim);
|
||||
return org.springframework.util.StringUtils.collectionToDelimitedString(coll, delim);
|
||||
}
|
||||
|
||||
public static List<String> split(String toSplit, String delim) {
|
||||
String[] stringArray = StringUtils.tokenizeToStringArray(toSplit, delim);
|
||||
String[] stringArray = org.springframework.util.StringUtils.tokenizeToStringArray(toSplit, delim);
|
||||
return Arrays.asList(stringArray);
|
||||
}
|
||||
|
||||
public static List<Integer> splitToInt(String toSplit, String delim) {
|
||||
String[] stringArray = StringUtils.tokenizeToStringArray(toSplit, delim);
|
||||
String[] stringArray = org.springframework.util.StringUtils.tokenizeToStringArray(toSplit, delim);
|
||||
List<Integer> array = new ArrayList<>(stringArray.length);
|
||||
for (String string : stringArray) {
|
||||
array.add(Integer.valueOf(string));
|
||||
@ -34,4 +31,9 @@ public class StringUtil {
|
||||
public static String substring(String str, int start) {
|
||||
return org.apache.commons.lang3.StringUtils.substring(str, start);
|
||||
}
|
||||
|
||||
public static String uuid(boolean isSimple) {
|
||||
return UUID.fastUUID().toString(isSimple);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.iocoder.common.framework.validator;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({
|
||||
ElementType.METHOD,
|
||||
ElementType.FIELD,
|
||||
ElementType.ANNOTATION_TYPE,
|
||||
ElementType.CONSTRUCTOR,
|
||||
ElementType.PARAMETER,
|
||||
ElementType.TYPE_USE
|
||||
})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Constraint(
|
||||
validatedBy = InEnumValidator.class
|
||||
)
|
||||
public @interface Mobile {
|
||||
|
||||
String message() default "手机号格式不正确";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.iocoder.common.framework.validator;
|
||||
|
||||
import cn.iocoder.common.framework.util.ValidationUtil;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
public class MobileValidator implements ConstraintValidator<Mobile, String> {
|
||||
|
||||
@Override
|
||||
public void initialize(Mobile annotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||
return ValidationUtil.isMobile(value);
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +1,36 @@
|
||||
package cn.iocoder.common.framework.vo;
|
||||
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 通用返回
|
||||
*
|
||||
* @param <T> 数据泛型
|
||||
*/
|
||||
public final class CommonResult<T> implements Serializable {
|
||||
|
||||
private static Integer CODE_SUCCESS = 0;
|
||||
private static final Integer CODE_SUCCESS = 0;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private Integer code;
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private String message;
|
||||
/**
|
||||
* 返回数据
|
||||
*/
|
||||
private T data;
|
||||
/**
|
||||
* 错误提示,用户可阅读
|
||||
*/
|
||||
private String message;
|
||||
/**
|
||||
* 错误明细,内部调试错误
|
||||
*/
|
||||
private String detailMessage;
|
||||
|
||||
/**
|
||||
* 将传入的 result 对象,转换成另外一个泛型结果的对象
|
||||
@ -75,6 +85,15 @@ public final class CommonResult<T> implements Serializable {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getDetailMessage() {
|
||||
return detailMessage;
|
||||
}
|
||||
|
||||
public CommonResult<T> setDetailMessage(String detailMessage) {
|
||||
this.detailMessage = detailMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isSuccess() {
|
||||
return CODE_SUCCESS.equals(code);
|
||||
@ -85,6 +104,16 @@ public final class CommonResult<T> implements Serializable {
|
||||
return !isSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有异常。如果有,则抛出 {@link cn.iocoder.common.framework.exception.ServiceException} 异常
|
||||
*/
|
||||
public void checkError() {
|
||||
if (isSuccess()) {
|
||||
return;
|
||||
}
|
||||
throw ServiceExceptionUtil.exception0(code, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CommonResult{" +
|
||||
|
@ -44,4 +44,30 @@ public class QueryWrapperX<T> extends QueryWrapper<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
// ========== 重写父类方法,方便链式调用 ==========
|
||||
|
||||
@Override
|
||||
public QueryWrapperX<T> eq(boolean condition, String column, Object val) {
|
||||
super.eq(condition, column, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryWrapperX<T> eq(String column, Object val) {
|
||||
super.eq(column, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryWrapperX<T> orderByDesc(String column) {
|
||||
super.orderByDesc(true, column);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryWrapperX<T> last(String lastSql) {
|
||||
super.last(lastSql);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import cn.iocoder.common.framework.constant.SysErrorCodeEnum;
|
||||
import cn.iocoder.common.framework.exception.ServiceException;
|
||||
import cn.iocoder.common.framework.util.ExceptionUtil;
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.common.framework.util.MallUtil;
|
||||
import cn.iocoder.common.framework.util.MallUtils;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.system.rpc.api.systemlog.SystemLogRPC;
|
||||
import cn.iocoder.mall.system.rpc.request.systemlog.ExceptionLogAddRequest;
|
||||
@ -108,7 +108,7 @@ public class GlobalExceptionHandler {
|
||||
exceptionLog.setExceptionMethodName(stackTraceElement.getMethodName());
|
||||
exceptionLog.setExceptionLineNumber(stackTraceElement.getLineNumber());
|
||||
// 设置其它字段
|
||||
exceptionLog.setTraceId(MallUtil.getTraceId())
|
||||
exceptionLog.setTraceId(MallUtils.getTraceId())
|
||||
.setApplicationName(applicationName)
|
||||
.setUri(request.getRequestURI()) // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。
|
||||
.setQueryString(HttpUtil.buildQueryString(request))
|
||||
|
@ -1,7 +1,7 @@
|
||||
package cn.iocoder.mall.web.core.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.common.framework.util.MallUtil;
|
||||
import cn.iocoder.common.framework.util.MallUtils;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.system.rpc.api.systemlog.SystemLogRPC;
|
||||
import cn.iocoder.mall.system.rpc.request.systemlog.AccessLogAddRequest;
|
||||
@ -63,7 +63,7 @@ public class AccessLogInterceptor extends HandlerInterceptorAdapter {
|
||||
accessLog.setErrorCode(result.getCode())
|
||||
.setErrorMessage(result.getMessage());
|
||||
// 设置其它字段
|
||||
accessLog.setTraceId(MallUtil.getTraceId())
|
||||
accessLog.setTraceId(MallUtils.getTraceId())
|
||||
.setApplicationName(applicationName)
|
||||
.setUri(request.getRequestURI()) // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。
|
||||
.setQueryString(HttpUtil.buildQueryString(request))
|
||||
|
@ -1,25 +0,0 @@
|
||||
package cn.iocoder.mall.system.biz.dao.oauth2;
|
||||
|
||||
import cn.iocoder.mall.system.biz.dataobject.oauth2.OAuth2MobileCodeDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface OAuth2MobileCodeMapper extends BaseMapper<OAuth2MobileCodeDO> {
|
||||
|
||||
/**
|
||||
* 获得手机号的最后一个手机验证码
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @return 手机验证码
|
||||
*/
|
||||
default OAuth2MobileCodeDO selectLastByMobile(String mobile) {
|
||||
QueryWrapper<OAuth2MobileCodeDO> query = new QueryWrapper<OAuth2MobileCodeDO>()
|
||||
.eq("mobile", mobile)
|
||||
.orderByDesc("id")
|
||||
.last("limit 1");
|
||||
return selectOne(query);
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +1,10 @@
|
||||
package cn.iocoder.mall.system.biz.dao.user;
|
||||
|
||||
import cn.iocoder.mall.mybatis.query.QueryWrapperX;
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.RoleDO;
|
||||
import cn.iocoder.mall.system.biz.dataobject.user.UserDO;
|
||||
import cn.iocoder.mall.system.biz.dto.user.UserPageDTO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Repository;
|
||||
@ -15,12 +12,6 @@ import org.springframework.stereotype.Repository;
|
||||
@Repository
|
||||
public interface UserMapper extends BaseMapper<UserDO> {
|
||||
|
||||
default UserDO selectByAccountId(Integer accountId) {
|
||||
return selectOne(new QueryWrapper<UserDO>()
|
||||
.eq("account_id", accountId)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件分页查询用户列表
|
||||
* @param userPageDTO
|
||||
|
@ -4,7 +4,7 @@ import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.common.framework.util.MallUtil;
|
||||
import cn.iocoder.common.framework.util.MallUtils;
|
||||
import cn.iocoder.mall.system.biz.log.operation.annotation.OperationLogging;
|
||||
import cn.iocoder.mall.system.biz.log.operation.enums.LogStatus;
|
||||
import cn.iocoder.mall.system.biz.log.operation.event.OperationLogEvent;
|
||||
@ -98,7 +98,7 @@ public class OperationLogAspect {
|
||||
.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
|
||||
|
||||
return new OperationLogDTO()
|
||||
.setTraceId(MallUtil.getTraceId())
|
||||
.setTraceId(MallUtils.getTraceId())
|
||||
.setUri(URLUtil.getPath(request.getRequestURI()))
|
||||
.setUserAgent(HttpUtil.getUserAgent(request))
|
||||
.setIp(HttpUtil.getIp(request))
|
||||
|
@ -1,7 +1,7 @@
|
||||
package cn.iocoder.mall.system.biz.service.authorization;
|
||||
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.common.framework.util.StringUtil;
|
||||
import cn.iocoder.common.framework.util.StringUtils;
|
||||
import cn.iocoder.common.framework.vo.PageResult;
|
||||
import cn.iocoder.mall.mybatis.enums.DeletedStatusEnum;
|
||||
import cn.iocoder.mall.system.biz.bo.authorization.RoleBO;
|
||||
@ -127,7 +127,7 @@ public class RoleServiceImpl implements RoleService {
|
||||
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.ROLE_NAME_DUPLICATE, name);
|
||||
}
|
||||
// 2. 是否存在相同编码的角色
|
||||
if (!StringUtil.hasText(code)) {
|
||||
if (!StringUtils.hasText(code)) {
|
||||
return;
|
||||
}
|
||||
// 该 code 编码被其它角色所使用
|
||||
|
@ -1,16 +0,0 @@
|
||||
package cn.iocoder.mall.system.biz.service.oauth2;
|
||||
|
||||
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeSendDTO;
|
||||
|
||||
/**
|
||||
* OAuth2 手机验证码 Service 接口
|
||||
*
|
||||
* 我们将手机验证码登陆的方式,作为一种拓展的 OAuth2 的认证方式。因此,我们放在了 `oauth2` 包下
|
||||
*/
|
||||
public interface OAuth2MobileCodeService {
|
||||
|
||||
void send(OAuth2MobileCodeSendDTO sendDTO);
|
||||
|
||||
void use(String mobile, String code);
|
||||
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
package cn.iocoder.mall.system.biz.service.oauth2;
|
||||
|
||||
import cn.iocoder.common.framework.constant.SysErrorCodeEnum;
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.common.framework.util.ValidationUtil;
|
||||
import cn.iocoder.mall.system.biz.dao.oauth2.OAuth2MobileCodeMapper;
|
||||
import cn.iocoder.mall.system.biz.dataobject.oauth2.OAuth2MobileCodeDO;
|
||||
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeSendDTO;
|
||||
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.system.biz.enums.SystemErrorCodeEnum.*;
|
||||
|
||||
@Service
|
||||
public class OAuth2MobileCodeServiceImpl implements OAuth2MobileCodeService {
|
||||
|
||||
/**
|
||||
* 每条验证码的过期时间,单位:毫秒
|
||||
*/
|
||||
@Value("${modules.oauth2-mobile-code-service.code-expire-time-millis}")
|
||||
private int codeExpireTimes;
|
||||
/**
|
||||
* 每日发送最大数量
|
||||
*/
|
||||
@Value("${modules.oauth2-mobile-code-service.send-maximum-quantity-per-day}")
|
||||
private int sendMaximumQuantityPerDay;
|
||||
/**
|
||||
* 短信发送频率,单位:毫秒
|
||||
*/
|
||||
@Value("${modules.oauth2-mobile-code-service.send-frequency}")
|
||||
private int sendFrequency;
|
||||
|
||||
@Autowired
|
||||
private OAuth2MobileCodeMapper oauth2MobileCodeMapper;
|
||||
|
||||
@Override
|
||||
public void send(OAuth2MobileCodeSendDTO sendDTO) {
|
||||
if (!ValidationUtil.isMobile(sendDTO.getMobile())) {
|
||||
throw ServiceExceptionUtil.exception(SysErrorCodeEnum.VALIDATION_REQUEST_PARAM_ERROR.getCode(), "手机格式不正确"); // TODO 有点搓
|
||||
}
|
||||
// 校验是否可以发送验证码
|
||||
OAuth2MobileCodeDO lastMobileCodePO = oauth2MobileCodeMapper.selectLastByMobile(sendDTO.getMobile());
|
||||
if (lastMobileCodePO != null) {
|
||||
if (lastMobileCodePO.getTodayIndex() >= sendMaximumQuantityPerDay) { // 超过当天发送的上限。
|
||||
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
|
||||
}
|
||||
if (System.currentTimeMillis() - lastMobileCodePO.getCreateTime().getTime() < sendFrequency) { // 发送过于频繁
|
||||
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_SEND_TOO_FAST);
|
||||
}
|
||||
// TODO 提升,每个 IP 每天可发送数量
|
||||
// TODO 提升,每个 IP 每小时可发送数量
|
||||
}
|
||||
// 创建验证码记录
|
||||
OAuth2MobileCodeDO newMobileCodePO = new OAuth2MobileCodeDO().setMobile(sendDTO.getMobile())
|
||||
.setCode("9999") // TODO 芋艿,随机 4 位验证码 or 6 位验证码
|
||||
.setTodayIndex(lastMobileCodePO != null ? lastMobileCodePO.getTodayIndex() : 1)
|
||||
.setCreateIp(sendDTO.getIp())
|
||||
.setUsed(false);
|
||||
newMobileCodePO.setCreateTime(new Date());
|
||||
oauth2MobileCodeMapper.insert(newMobileCodePO);
|
||||
// TODO 发送验证码短信
|
||||
}
|
||||
|
||||
@Override
|
||||
public void use(String mobile, String code) {
|
||||
// 校验验证码
|
||||
OAuth2MobileCodeDO mobileCodeDO = oauth2MobileCodeMapper.selectLastByMobile(mobile);
|
||||
if (mobileCodeDO == null) { // 若验证码不存在,抛出异常
|
||||
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_NOT_FOUND);
|
||||
}
|
||||
if (System.currentTimeMillis() - mobileCodeDO.getCreateTime().getTime() >= codeExpireTimes) { // 验证码已过期
|
||||
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_EXPIRED);
|
||||
}
|
||||
if (mobileCodeDO.getUsed()) { // 验证码已使用
|
||||
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_USED);
|
||||
}
|
||||
if (!mobileCodeDO.getCode().equals(code)) {
|
||||
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_NOT_CORRECT);
|
||||
}
|
||||
// 使用验证码
|
||||
OAuth2MobileCodeDO update = new OAuth2MobileCodeDO().setId(mobileCodeDO.getId())
|
||||
.setUsed(true).setUsedTime(new Date()); // TODO usedIp
|
||||
oauth2MobileCodeMapper.updateById(update);
|
||||
}
|
||||
|
||||
}
|
@ -1,29 +1,14 @@
|
||||
package cn.iocoder.mall.system.rest.controller.oauth2;
|
||||
|
||||
import cn.iocoder.common.framework.constant.MallConstants;
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.security.core.annotation.RequiresNone;
|
||||
import cn.iocoder.mall.system.biz.bo.user.UserAuthenticateBO;
|
||||
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeAuthenticateDTO;
|
||||
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeSendDTO;
|
||||
import cn.iocoder.mall.system.biz.service.oauth2.OAuth2MobileCodeService;
|
||||
import cn.iocoder.mall.system.biz.service.oauth2.OAuth2Service;
|
||||
import cn.iocoder.mall.system.biz.service.user.UserService;
|
||||
import cn.iocoder.mall.system.rest.convert.oauth2.UsersOAuth2Convert;
|
||||
import cn.iocoder.mall.system.rest.request.oauth2.UsersOAuth2MobileCodeAuthenticateRequest;
|
||||
import cn.iocoder.mall.system.rest.response.user.UsersOAuth2AuthenticateResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(MallConstants.ROOT_PATH_USER + "/oauth2")
|
||||
@Api(tags = "用户 - OAuth2 API")
|
||||
@ -36,33 +21,6 @@ public class UsersOAuth2Controller {
|
||||
@Autowired
|
||||
private OAuth2MobileCodeService oauth2MobileCodeService;
|
||||
|
||||
@PostMapping("/mobile-code-authenticate")
|
||||
@ApiOperation("手机验证码认证")
|
||||
@RequiresNone
|
||||
public CommonResult<UsersOAuth2AuthenticateResponse> mobileCodeAuthenticate(UsersOAuth2MobileCodeAuthenticateRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
// 执行认证
|
||||
OAuth2MobileCodeAuthenticateDTO authenticateDTO = UsersOAuth2Convert.INSTANCE.convert(request)
|
||||
.setIp(HttpUtil.getIp(httpRequest));
|
||||
UserAuthenticateBO userAuthenticateBO = userService.authenticate(authenticateDTO);
|
||||
// 转换返回
|
||||
return CommonResult.success(
|
||||
UsersOAuth2Convert.INSTANCE.convert(userAuthenticateBO)
|
||||
);
|
||||
}
|
||||
|
||||
@PostMapping("/send-mobile-code")
|
||||
@ApiOperation("发送手机验证码")
|
||||
@ApiImplicitParam(name = "mobile", value = "手机号", required = true, example = "15601691234")
|
||||
@RequiresNone
|
||||
public CommonResult<Boolean> sendMobileCode(@RequestParam("mobile") String mobile,
|
||||
HttpServletRequest request) {
|
||||
// 执行发送验证码
|
||||
OAuth2MobileCodeSendDTO sendDTO = new OAuth2MobileCodeSendDTO()
|
||||
.setMobile(mobile).setIp(HttpUtil.getIp(request));
|
||||
oauth2MobileCodeService.send(sendDTO);
|
||||
// 返回成功
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package cn.iocoder.mall.userservice.rpc.user.vo;
|
||||
|
||||
import cn.iocoder.common.framework.constant.CommonStatusEnum;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
|
@ -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>
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.iocoder.mall.system.biz.dataobject.oauth2;
|
||||
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;
|
||||
@ -9,13 +10,13 @@ import lombok.experimental.Accessors;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* OAuth2 手机验证码
|
||||
* 手机验证码 DO
|
||||
*/
|
||||
@TableName("oauth2_mobile_code")
|
||||
@TableName("user_sms_code")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class OAuth2MobileCodeDO extends BaseDO {
|
||||
public class UserSmsCodeDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
@ -29,6 +30,12 @@ public class OAuth2MobileCodeDO extends BaseDO {
|
||||
* 验证码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 发送场景
|
||||
*
|
||||
* 外键 {@link UserSmsSceneEnum}
|
||||
*/
|
||||
private Integer scene;
|
||||
/**
|
||||
* 创建 IP
|
||||
*/
|
||||
@ -48,6 +55,6 @@ public class OAuth2MobileCodeDO extends BaseDO {
|
||||
/**
|
||||
* 使用 IP
|
||||
*/
|
||||
private Date usedIp;
|
||||
private String usedIp;
|
||||
|
||||
}
|
@ -46,5 +46,9 @@ public class UserDO extends DeletableDO {
|
||||
* {@link #password} 的盐
|
||||
*/
|
||||
private String passwordSalt;
|
||||
/**
|
||||
* 注册 IP
|
||||
*/
|
||||
private String createIp;
|
||||
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
package cn.iocoder.mall.userservice.dal.mysql.mapper;
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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
|
@ -35,3 +35,5 @@ dubbo:
|
||||
filter: -exception
|
||||
UserRpc:
|
||||
version: 1.0.0
|
||||
UserSmsCodeRpc:
|
||||
version: 1.0.0
|
||||
|
@ -11,16 +11,20 @@
|
||||
|
||||
<artifactId>user-web-app</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- onemall 基础 bom 文件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>mall-dependencies</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- onemall 基础 bom 文件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>mall-dependencies</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -45,6 +49,33 @@
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<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>
|
||||
|
@ -0,0 +1,13 @@
|
||||
package cn.iocoder.mall.userweb;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class UserWebApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(UserWebApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package cn.iocoder.mall.userweb.controller.passport;
|
||||
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.userweb.controller.passport.dto.UserPassportLoginBySmsDTO;
|
||||
import cn.iocoder.mall.userweb.controller.passport.dto.UserPassportSendSmsCodeDTO;
|
||||
import cn.iocoder.mall.userweb.controller.passport.vo.UserPassportVO;
|
||||
import cn.iocoder.mall.userweb.manager.passport.UserPassportManager;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/passport")
|
||||
public class UserPassportController {
|
||||
|
||||
@Autowired
|
||||
private UserPassportManager userPassportManager;
|
||||
|
||||
@PostMapping("/login_by_sms")
|
||||
@ApiOperation("手机验证码登陆")
|
||||
// @RequiresNone TODO 晚点加上
|
||||
public CommonResult<UserPassportVO> loginBySms(UserPassportLoginBySmsDTO loginBySmsDTO,
|
||||
HttpServletRequest request) {
|
||||
return CommonResult.success(userPassportManager.loginBySms(loginBySmsDTO, HttpUtil.getIp(request)));
|
||||
}
|
||||
|
||||
@PostMapping("/send_sms_code")
|
||||
@ApiOperation("发送手机验证码")
|
||||
// @RequiresNone TODO 晚点加上
|
||||
public CommonResult<Boolean> sendSmsCode(UserPassportSendSmsCodeDTO sendSmsCodeDTO,
|
||||
HttpServletRequest request) {
|
||||
userPassportManager.sendSmsCode(sendSmsCodeDTO, HttpUtil.getIp(request));
|
||||
// 返回成功
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package cn.iocoder.mall.userweb.controller.passport.dto;
|
||||
|
||||
import cn.iocoder.common.framework.validator.Mobile;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
@ApiModel("用户短信验证码登陆 DTO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class UserPassportLoginBySmsDTO implements Serializable {
|
||||
|
||||
@ApiModelProperty(value = "手机号", example = "15601691234")
|
||||
@Mobile
|
||||
private String mobile;
|
||||
@ApiModelProperty(value = "验证码", example = "1234")
|
||||
@NotNull(message = "验证码不能为空")
|
||||
private String code;
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package cn.iocoder.mall.userweb.controller.passport.dto;
|
||||
|
||||
import cn.iocoder.common.framework.validator.Mobile;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@ApiModel("发送手机验证码 DTO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class UserPassportSendSmsCodeDTO {
|
||||
|
||||
@ApiModelProperty(value = "手机号", example = "15601691234")
|
||||
@Mobile
|
||||
private String mobile;
|
||||
@ApiModelProperty(value = "发送场景", example = "1", notes = "对应 UserSmsSceneEnum 枚举")
|
||||
@NotNull(message = "发送场景不能为空")
|
||||
private Integer scene;
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package cn.iocoder.mall.userweb.controller.passport.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 用户通信证信息
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class UserPassportVO {
|
||||
|
||||
/**
|
||||
* 认证信息
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class Authentication {
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
private String accessToken;
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
private String refreshToken;
|
||||
/**
|
||||
* 账号编号
|
||||
*/
|
||||
private Integer accountId;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expiresTime;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class User {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private User user;
|
||||
/**
|
||||
* 认证信息
|
||||
*/
|
||||
private Authentication authorization;
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package cn.iocoder.mall.userweb.controller.user;
|
||||
|
||||
public class UserController {
|
||||
}
|
@ -0,0 +1 @@
|
||||
package cn.iocoder.mall.userweb.convert;
|
@ -0,0 +1,26 @@
|
||||
package cn.iocoder.mall.userweb.convert.passport;
|
||||
|
||||
import cn.iocoder.mall.userservice.rpc.sms.vo.UserSendSmsCodeDTO;
|
||||
import cn.iocoder.mall.userservice.rpc.sms.vo.UserVerifySmsCodeDTO;
|
||||
import cn.iocoder.mall.userservice.rpc.user.dto.UserCreateDTO;
|
||||
import cn.iocoder.mall.userservice.rpc.user.vo.UserVO;
|
||||
import cn.iocoder.mall.userweb.controller.passport.dto.UserPassportLoginBySmsDTO;
|
||||
import cn.iocoder.mall.userweb.controller.passport.dto.UserPassportSendSmsCodeDTO;
|
||||
import cn.iocoder.mall.userweb.controller.passport.vo.UserPassportVO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface UserPassportConvert {
|
||||
|
||||
UserPassportConvert INSTANCE = Mappers.getMapper(UserPassportConvert.class);
|
||||
|
||||
UserVerifySmsCodeDTO convert(UserPassportLoginBySmsDTO bean);
|
||||
|
||||
UserCreateDTO convert02(UserPassportLoginBySmsDTO bean);
|
||||
|
||||
UserPassportVO convert(UserVO userVO);
|
||||
|
||||
UserSendSmsCodeDTO convert(UserPassportSendSmsCodeDTO bean);
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package cn.iocoder.mall.userweb.manager.passport;
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.userservice.enums.sms.UserSmsSceneEnum;
|
||||
import cn.iocoder.mall.userservice.rpc.sms.UserSmsCodeRpc;
|
||||
import cn.iocoder.mall.userservice.rpc.user.UserRpc;
|
||||
import cn.iocoder.mall.userservice.rpc.user.vo.UserVO;
|
||||
import cn.iocoder.mall.userweb.controller.passport.dto.UserPassportLoginBySmsDTO;
|
||||
import cn.iocoder.mall.userweb.controller.passport.dto.UserPassportSendSmsCodeDTO;
|
||||
import cn.iocoder.mall.userweb.controller.passport.vo.UserPassportVO;
|
||||
import cn.iocoder.mall.userweb.convert.passport.UserPassportConvert;
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class UserPassportManager {
|
||||
|
||||
@Reference(version = "${dubbo.consumer.UserSmsCodeRpc.version}", validation = "false")
|
||||
private UserSmsCodeRpc userSmsCodeRpc;
|
||||
@Reference(version = "${dubbo.consumer.UserRpc.version}", validation = "false")
|
||||
private UserRpc userRpc;
|
||||
|
||||
public UserPassportVO loginBySms(UserPassportLoginBySmsDTO loginBySmsDTO, String ip) {
|
||||
// 校验验证码
|
||||
CommonResult<Boolean> verifySmsCodeResult = userSmsCodeRpc.verifySmsCode(UserPassportConvert.INSTANCE.convert(loginBySmsDTO)
|
||||
.setScene(UserSmsSceneEnum.LOGIN_BY_SMS.getValue()).setIp(ip));
|
||||
verifySmsCodeResult.checkError();
|
||||
// 获得用户
|
||||
CommonResult<UserVO> createUserResult = userRpc.createUserIfAbsent(UserPassportConvert.INSTANCE.convert02(loginBySmsDTO).setIp(ip));
|
||||
createUserResult.checkError();
|
||||
// 创建访问令牌
|
||||
return UserPassportConvert.INSTANCE.convert(createUserResult.getData());
|
||||
}
|
||||
|
||||
public void sendSmsCode(UserPassportSendSmsCodeDTO sendSmsCodeDTO, String ip) {
|
||||
CommonResult<Boolean> sendSmsCodeResult = userSmsCodeRpc.sendSmsCode(UserPassportConvert.INSTANCE.convert(sendSmsCodeDTO).setIp(ip));
|
||||
sendSmsCodeResult.checkError();
|
||||
}
|
||||
|
||||
}
|
14
user-web-app/src/main/resources/application-dev.yml
Normal file
14
user-web-app/src/main/resources/application-dev.yml
Normal file
@ -0,0 +1,14 @@
|
||||
spring:
|
||||
# Spring Cloud 配置项
|
||||
cloud:
|
||||
nacos:
|
||||
# Spring Cloud Nacos Discovery 配置项
|
||||
discovery:
|
||||
server-addr: 400-infra.server.iocoder.cn:8848 # Nacos 服务器地址
|
||||
namespace: dev # Nacos 命名空间
|
||||
|
||||
# Dubbo 配置项
|
||||
dubbo:
|
||||
# Dubbo 注册中心
|
||||
registry:
|
||||
address: spring-cloud://400-infra.server.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址
|
14
user-web-app/src/main/resources/application-local.yml
Normal file
14
user-web-app/src/main/resources/application-local.yml
Normal file
@ -0,0 +1,14 @@
|
||||
spring:
|
||||
# Spring Cloud 配置项
|
||||
cloud:
|
||||
nacos:
|
||||
# Spring Cloud Nacos Discovery 配置项
|
||||
discovery:
|
||||
server-addr: 400-infra.server.iocoder.cn:8848 # Nacos 服务器地址
|
||||
namespace: local # Nacos 命名空间
|
||||
|
||||
# Dubbo 配置项
|
||||
dubbo:
|
||||
# Dubbo 注册中心
|
||||
registry:
|
||||
address: spring-cloud://400-infra.server.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址
|
25
user-web-app/src/main/resources/application.yml
Normal file
25
user-web-app/src/main/resources/application.yml
Normal file
@ -0,0 +1,25 @@
|
||||
# 服务器的配置项
|
||||
server:
|
||||
port: 18082
|
||||
servlet:
|
||||
context-path: /user-api/
|
||||
|
||||
spring:
|
||||
# Application 的配置项
|
||||
application:
|
||||
name: user-web
|
||||
# Profile 的配置项
|
||||
profiles:
|
||||
active: local
|
||||
|
||||
# Dubbo 配置项
|
||||
dubbo:
|
||||
# Spring Cloud Alibaba Dubbo 专属配置
|
||||
cloud:
|
||||
subscribed-services: 'user-service' # 设置订阅的应用列表,默认为 * 订阅所有应用
|
||||
# Dubbo 服务消费者的配置
|
||||
consumer:
|
||||
UserSmsCodeRpc:
|
||||
version: 1.0.0
|
||||
UserRpc:
|
||||
version: 1.0.0
|
Loading…
Reference in New Issue
Block a user