From 3d6bd5e4eea7be552f1aff0007ae92e2cae22db0 Mon Sep 17 00:00:00 2001 From: YunaiV <> Date: Fri, 3 Jul 2020 19:12:56 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99=E7=94=A8=E6=88=B7=E5=BF=AB?= =?UTF-8?q?=E9=80=9F=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/framework/util/DigestUtils.java | 14 +++ .../util/{MallUtil.java => MallUtils.java} | 4 +- .../framework/util/ServiceExceptionUtil.java | 7 +- .../{StringUtil.java => StringUtils.java} | 22 ++-- .../common/framework/validator/Mobile.java | 28 +++++ .../framework/validator/MobileValidator.java | 19 ++++ .../common/framework/vo/CommonResult.java | 39 ++++++- .../mall/mybatis/query/QueryWrapperX.java | 26 +++++ .../core/handler/GlobalExceptionHandler.java | 4 +- .../interceptor/AccessLogInterceptor.java | 4 +- .../dao/oauth2/OAuth2MobileCodeMapper.java | 25 ---- .../mall/system/biz/dao/user/UserMapper.java | 9 -- .../operation/aspect/OperationLogAspect.java | 4 +- .../authorization/RoleServiceImpl.java | 4 +- .../oauth2/OAuth2MobileCodeService.java | 16 --- .../oauth2/OAuth2MobileCodeServiceImpl.java | 89 --------------- .../oauth2/UsersOAuth2Controller.java | 42 ------- user-service-project/user-service-api/pom.xml | 10 ++ .../userservice/enums/UserErrorCodeEnum.java | 72 ++++++++++++ .../enums/sms/UserSmsSceneEnum.java | 39 +++++++ .../userservice/rpc/sms/UserSmsCodeRpc.java | 16 +++ .../rpc/sms/vo/UserSendSmsCodeDTO.java | 37 ++++++ .../rpc/sms/vo/UserVerifySmsCodeDTO.java | 42 +++++++ .../mall/userservice/rpc/user/UserRpc.java | 3 + .../rpc/user/dto/UserCreateDTO.java | 35 ++++++ .../mall/userservice/rpc/user/vo/UserVO.java | 2 + user-service-project/user-service-app/pom.xml | 13 +++ .../userservice/UserServiceApplication.java | 2 +- .../userservice/convert/user/UserConvert.java | 5 + .../mysql/dataobject/sms/UserSmsCodeDO.java | 17 ++- .../dal/mysql/dataobject/user/UserDO.java | 4 + .../dal/mysql/mapper/package-info.java | 1 - .../mysql/mapper/sms/UserSmsCodeMapper.java | 27 +++++ .../dal/mysql/mapper/user/UserMapper.java | 7 +- .../manager/sms/UserSmsCodeManager.java | 27 +++++ .../userservice/manager/user/UserManager.java | 12 ++ .../rpc/sms/UserSmsCodeRpcImpl.java | 28 +++++ .../userservice/rpc/user/UserRpcImpl.java | 8 +- .../service/sms/UserSmsCodeService.java | 107 ++++++++++++++++++ .../userservice/service/user/UserService.java | 35 ++++++ .../userservice/service/user/bo/UserBO.java | 37 ++++++ .../service/user/bo/UserCreateBO.java | 39 +++++++ .../src/main/resources/application.properties | 5 + .../src/main/resources/application.yaml | 2 + user-web-app/pom.xml | 49 ++++++-- .../mall/userweb/UserWebApplication.java | 13 +++ .../passport/UserPassportController.java | 42 +++++++ .../dto/UserPassportLoginBySmsDTO.java | 24 ++++ .../dto/UserPassportSendSmsCodeDTO.java | 23 ++++ .../passport/vo/UserPassportVO.java | 72 ++++++++++++ .../controller/user/UserController.java | 4 + .../mall/userweb/convert/package-info.java | 1 + .../convert/passport/UserPassportConvert.java | 26 +++++ .../manager/passport/UserPassportManager.java | 40 +++++++ .../src/main/resources/application-dev.yml | 14 +++ .../src/main/resources/application-local.yml | 14 +++ .../src/main/resources/application.yml | 25 ++++ 57 files changed, 1107 insertions(+), 228 deletions(-) create mode 100644 common/common-framework/src/main/java/cn/iocoder/common/framework/util/DigestUtils.java rename common/common-framework/src/main/java/cn/iocoder/common/framework/util/{MallUtil.java => MallUtils.java} (96%) rename common/common-framework/src/main/java/cn/iocoder/common/framework/util/{StringUtil.java => StringUtils.java} (55%) create mode 100644 common/common-framework/src/main/java/cn/iocoder/common/framework/validator/Mobile.java create mode 100644 common/common-framework/src/main/java/cn/iocoder/common/framework/validator/MobileValidator.java delete mode 100644 system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/oauth2/OAuth2MobileCodeMapper.java delete mode 100644 system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/oauth2/OAuth2MobileCodeService.java delete mode 100644 system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/oauth2/OAuth2MobileCodeServiceImpl.java create mode 100644 user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/enums/UserErrorCodeEnum.java create mode 100644 user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/enums/sms/UserSmsSceneEnum.java create mode 100644 user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/UserSmsCodeRpc.java create mode 100644 user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/vo/UserSendSmsCodeDTO.java create mode 100644 user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/vo/UserVerifySmsCodeDTO.java create mode 100644 user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/dto/UserCreateDTO.java rename system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dataobject/oauth2/OAuth2MobileCodeDO.java => user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/dataobject/sms/UserSmsCodeDO.java (68%) delete mode 100644 user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/package-info.java create mode 100644 user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/sms/UserSmsCodeMapper.java create mode 100644 user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/manager/sms/UserSmsCodeManager.java create mode 100644 user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/rpc/sms/UserSmsCodeRpcImpl.java create mode 100644 user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/sms/UserSmsCodeService.java create mode 100644 user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/bo/UserCreateBO.java create mode 100644 user-service-project/user-service-app/src/main/resources/application.properties create mode 100644 user-web-app/src/main/java/cn/iocoder/mall/userweb/UserWebApplication.java create mode 100644 user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/UserPassportController.java create mode 100644 user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportLoginBySmsDTO.java create mode 100644 user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportSendSmsCodeDTO.java create mode 100644 user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/vo/UserPassportVO.java create mode 100644 user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/user/UserController.java create mode 100644 user-web-app/src/main/java/cn/iocoder/mall/userweb/convert/package-info.java create mode 100644 user-web-app/src/main/java/cn/iocoder/mall/userweb/convert/passport/UserPassportConvert.java create mode 100644 user-web-app/src/main/java/cn/iocoder/mall/userweb/manager/passport/UserPassportManager.java create mode 100644 user-web-app/src/main/resources/application-dev.yml create mode 100644 user-web-app/src/main/resources/application-local.yml create mode 100644 user-web-app/src/main/resources/application.yml diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/DigestUtils.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/DigestUtils.java new file mode 100644 index 000000000..63ad9ea00 --- /dev/null +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/DigestUtils.java @@ -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); + } + +} diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MallUtil.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MallUtils.java similarity index 96% rename from common/common-framework/src/main/java/cn/iocoder/common/framework/util/MallUtil.java rename to common/common-framework/src/main/java/cn/iocoder/common/framework/util/MallUtils.java index 38637c5ea..09b23a4be 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MallUtil.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MallUtils.java @@ -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 芋艿 多次调用会问题 diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/ServiceExceptionUtil.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/ServiceExceptionUtil.java index ca00b4b0b..cc84a8eeb 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/ServiceExceptionUtil.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/ServiceExceptionUtil.java @@ -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); } diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/StringUtil.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/StringUtils.java similarity index 55% rename from common/common-framework/src/main/java/cn/iocoder/common/framework/util/StringUtil.java rename to common/common-framework/src/main/java/cn/iocoder/common/framework/util/StringUtils.java index 53b705d88..a1da47551 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/StringUtil.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/StringUtils.java @@ -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 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 splitToInt(String toSplit, String delim) { - String[] stringArray = StringUtils.tokenizeToStringArray(toSplit, delim); + String[] stringArray = org.springframework.util.StringUtils.tokenizeToStringArray(toSplit, delim); List 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); + } + } diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/validator/Mobile.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/validator/Mobile.java new file mode 100644 index 000000000..9e3aab35d --- /dev/null +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/validator/Mobile.java @@ -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[] payload() default {}; + +} diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/validator/MobileValidator.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/validator/MobileValidator.java new file mode 100644 index 000000000..8397cace0 --- /dev/null +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/validator/MobileValidator.java @@ -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 { + + @Override + public void initialize(Mobile annotation) { + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return ValidationUtil.isMobile(value); + } + +} diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/vo/CommonResult.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/vo/CommonResult.java index 0ae81b349..86cbf63cd 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/vo/CommonResult.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/vo/CommonResult.java @@ -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 数据泛型 + */ public final class CommonResult 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 implements Serializable { this.data = data; } + public String getDetailMessage() { + return detailMessage; + } + + public CommonResult 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 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{" + diff --git a/common/mall-spring-boot-starter-mybatis/src/main/java/cn/iocoder/mall/mybatis/query/QueryWrapperX.java b/common/mall-spring-boot-starter-mybatis/src/main/java/cn/iocoder/mall/mybatis/query/QueryWrapperX.java index ec2048835..247cd43fd 100644 --- a/common/mall-spring-boot-starter-mybatis/src/main/java/cn/iocoder/mall/mybatis/query/QueryWrapperX.java +++ b/common/mall-spring-boot-starter-mybatis/src/main/java/cn/iocoder/mall/mybatis/query/QueryWrapperX.java @@ -44,4 +44,30 @@ public class QueryWrapperX extends QueryWrapper { return this; } + // ========== 重写父类方法,方便链式调用 ========== + + @Override + public QueryWrapperX eq(boolean condition, String column, Object val) { + super.eq(condition, column, val); + return this; + } + + @Override + public QueryWrapperX eq(String column, Object val) { + super.eq(column, val); + return this; + } + + @Override + public QueryWrapperX orderByDesc(String column) { + super.orderByDesc(true, column); + return this; + } + + @Override + public QueryWrapperX last(String lastSql) { + super.last(lastSql); + return this; + } + } diff --git a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/handler/GlobalExceptionHandler.java b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/handler/GlobalExceptionHandler.java index b2bdd31c7..5dd933263 100644 --- a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/handler/GlobalExceptionHandler.java +++ b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/handler/GlobalExceptionHandler.java @@ -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)) diff --git a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/interceptor/AccessLogInterceptor.java b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/interceptor/AccessLogInterceptor.java index 9f677d00b..0e7f7f65d 100644 --- a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/interceptor/AccessLogInterceptor.java +++ b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/interceptor/AccessLogInterceptor.java @@ -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)) diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/oauth2/OAuth2MobileCodeMapper.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/oauth2/OAuth2MobileCodeMapper.java deleted file mode 100644 index d994f9ce7..000000000 --- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/oauth2/OAuth2MobileCodeMapper.java +++ /dev/null @@ -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 { - - /** - * 获得手机号的最后一个手机验证码 - * - * @param mobile 手机号 - * @return 手机验证码 - */ - default OAuth2MobileCodeDO selectLastByMobile(String mobile) { - QueryWrapper query = new QueryWrapper() - .eq("mobile", mobile) - .orderByDesc("id") - .last("limit 1"); - return selectOne(query); - } - -} diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/user/UserMapper.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/user/UserMapper.java index fc636771f..64d103dd2 100644 --- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/user/UserMapper.java +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/user/UserMapper.java @@ -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 { - default UserDO selectByAccountId(Integer accountId) { - return selectOne(new QueryWrapper() - .eq("account_id", accountId) - ); - } - /** * 根据条件分页查询用户列表 * @param userPageDTO diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/aspect/OperationLogAspect.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/aspect/OperationLogAspect.java index a89b1f2e4..ff79f7d11 100644 --- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/aspect/OperationLogAspect.java +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/aspect/OperationLogAspect.java @@ -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)) diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/authorization/RoleServiceImpl.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/authorization/RoleServiceImpl.java index f3f1da4c8..8509e2247 100644 --- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/authorization/RoleServiceImpl.java +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/authorization/RoleServiceImpl.java @@ -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 编码被其它角色所使用 diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/oauth2/OAuth2MobileCodeService.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/oauth2/OAuth2MobileCodeService.java deleted file mode 100644 index 3cec386a8..000000000 --- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/oauth2/OAuth2MobileCodeService.java +++ /dev/null @@ -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); - -} diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/oauth2/OAuth2MobileCodeServiceImpl.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/oauth2/OAuth2MobileCodeServiceImpl.java deleted file mode 100644 index 5d08f2a89..000000000 --- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/oauth2/OAuth2MobileCodeServiceImpl.java +++ /dev/null @@ -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); - } - -} diff --git a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/controller/oauth2/UsersOAuth2Controller.java b/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/controller/oauth2/UsersOAuth2Controller.java index ea073027a..ff06d188d 100644 --- a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/controller/oauth2/UsersOAuth2Controller.java +++ b/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/controller/oauth2/UsersOAuth2Controller.java @@ -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 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 sendMobileCode(@RequestParam("mobile") String mobile, - HttpServletRequest request) { - // 执行发送验证码 - OAuth2MobileCodeSendDTO sendDTO = new OAuth2MobileCodeSendDTO() - .setMobile(mobile).setIp(HttpUtil.getIp(request)); - oauth2MobileCodeService.send(sendDTO); - // 返回成功 - return CommonResult.success(true); - } } diff --git a/user-service-project/user-service-api/pom.xml b/user-service-project/user-service-api/pom.xml index fb24cb58b..a23b3f8bf 100644 --- a/user-service-project/user-service-api/pom.xml +++ b/user-service-project/user-service-api/pom.xml @@ -16,6 +16,16 @@ cn.iocoder.mall common-framework + + + + javax.validation + validation-api + + + org.projectlombok + lombok + diff --git a/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/enums/UserErrorCodeEnum.java b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/enums/UserErrorCodeEnum.java new file mode 100644 index 000000000..b4348cb03 --- /dev/null +++ b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/enums/UserErrorCodeEnum.java @@ -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 { + + // ========== 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; + } + + +} diff --git a/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/enums/sms/UserSmsSceneEnum.java b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/enums/sms/UserSmsSceneEnum.java new file mode 100644 index 000000000..72952da81 --- /dev/null +++ b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/enums/sms/UserSmsSceneEnum.java @@ -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; + } + +} diff --git a/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/UserSmsCodeRpc.java b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/UserSmsCodeRpc.java new file mode 100644 index 000000000..b4d5b5893 --- /dev/null +++ b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/UserSmsCodeRpc.java @@ -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 sendSmsCode(UserSendSmsCodeDTO sendSmsCodeDTO); + + CommonResult verifySmsCode(UserVerifySmsCodeDTO verifySmsCodeDTO); + +} diff --git a/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/vo/UserSendSmsCodeDTO.java b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/vo/UserSendSmsCodeDTO.java new file mode 100644 index 000000000..1f77484e4 --- /dev/null +++ b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/vo/UserSendSmsCodeDTO.java @@ -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; + +} diff --git a/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/vo/UserVerifySmsCodeDTO.java b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/vo/UserVerifySmsCodeDTO.java new file mode 100644 index 000000000..499498785 --- /dev/null +++ b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/sms/vo/UserVerifySmsCodeDTO.java @@ -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; + +} diff --git a/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/UserRpc.java b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/UserRpc.java index 24532157e..702a87599 100644 --- a/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/UserRpc.java +++ b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/UserRpc.java @@ -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 getUser(Integer id); + CommonResult createUserIfAbsent(UserCreateDTO createDTO); + } diff --git a/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/dto/UserCreateDTO.java b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/dto/UserCreateDTO.java new file mode 100644 index 000000000..23d3916fc --- /dev/null +++ b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/dto/UserCreateDTO.java @@ -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; + +} diff --git a/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/vo/UserVO.java b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/vo/UserVO.java index a464d0193..f0a7cd6bb 100644 --- a/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/vo/UserVO.java +++ b/user-service-project/user-service-api/src/main/java/cn/iocoder/mall/userservice/rpc/user/vo/UserVO.java @@ -1,5 +1,7 @@ package cn.iocoder.mall.userservice.rpc.user.vo; +import cn.iocoder.common.framework.constant.CommonStatusEnum; + import java.io.Serializable; /** diff --git a/user-service-project/user-service-app/pom.xml b/user-service-project/user-service-app/pom.xml index cd0e0de2c..773f80004 100644 --- a/user-service-project/user-service-app/pom.xml +++ b/user-service-project/user-service-app/pom.xml @@ -69,6 +69,19 @@ org.mapstruct mapstruct-jdk8 + + + + + + + + + + + + + diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/UserServiceApplication.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/UserServiceApplication.java index 19604c82f..418096767 100644 --- a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/UserServiceApplication.java +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/UserServiceApplication.java @@ -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) { diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/convert/user/UserConvert.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/convert/user/UserConvert.java index dbf26f3b6..7b5d028b5 100644 --- a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/convert/user/UserConvert.java +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/convert/user/UserConvert.java @@ -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); + } diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dataobject/oauth2/OAuth2MobileCodeDO.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/dataobject/sms/UserSmsCodeDO.java similarity index 68% rename from system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dataobject/oauth2/OAuth2MobileCodeDO.java rename to user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/dataobject/sms/UserSmsCodeDO.java index 88637d991..1ad03ecbf 100644 --- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dataobject/oauth2/OAuth2MobileCodeDO.java +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/dataobject/sms/UserSmsCodeDO.java @@ -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; } diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/dataobject/user/UserDO.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/dataobject/user/UserDO.java index 1d074a238..9156de8f9 100644 --- a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/dataobject/user/UserDO.java +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/dataobject/user/UserDO.java @@ -46,5 +46,9 @@ public class UserDO extends DeletableDO { * {@link #password} 的盐 */ private String passwordSalt; + /** + * 注册 IP + */ + private String createIp; } diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/package-info.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/package-info.java deleted file mode 100644 index 2fcffaf35..000000000 --- a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.mall.userservice.dal.mysql.mapper; diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/sms/UserSmsCodeMapper.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/sms/UserSmsCodeMapper.java new file mode 100644 index 000000000..fce05e6ae --- /dev/null +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/sms/UserSmsCodeMapper.java @@ -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 { + + /** + * 获得手机号的最后一个手机验证码 + * + * @param mobile 手机号 + * @param scene 发送场景,选填 + * @return 手机验证码 + */ + default UserSmsCodeDO selectLastByMobile(String mobile, Integer scene) { + QueryWrapperX query = new QueryWrapperX() + .eq("mobile", mobile) + .eqIfPresent("scene", scene) + .orderByDesc("id") + .last("limit 1"); + return selectOne(query); + } + +} diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/user/UserMapper.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/user/UserMapper.java index f4c414b68..14d535dbd 100644 --- a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/user/UserMapper.java +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/dal/mysql/mapper/user/UserMapper.java @@ -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 { - + default UserDO selectByMobile(String mobile) { + return selectOne(new QueryWrapper() + .eq("mobile", mobile) + ); + } } diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/manager/sms/UserSmsCodeManager.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/manager/sms/UserSmsCodeManager.java new file mode 100644 index 000000000..e88170d29 --- /dev/null +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/manager/sms/UserSmsCodeManager.java @@ -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()); + } + +} diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/manager/user/UserManager.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/manager/user/UserManager.java index 5965853d9..671bbb862 100644 --- a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/manager/user/UserManager.java +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/manager/user/UserManager.java @@ -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; + } + } diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/rpc/sms/UserSmsCodeRpcImpl.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/rpc/sms/UserSmsCodeRpcImpl.java new file mode 100644 index 000000000..95c56bad1 --- /dev/null +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/rpc/sms/UserSmsCodeRpcImpl.java @@ -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 sendSmsCode(UserSendSmsCodeDTO sendSmsCodeDTO) { + userSmsCodeManager.sendSmsCode(sendSmsCodeDTO); + return CommonResult.success(true); + } + + @Override + public CommonResult verifySmsCode(UserVerifySmsCodeDTO verifySmsCodeDTO) { + userSmsCodeManager.verifySmsCode(verifySmsCodeDTO); + return CommonResult.success(true); + } + +} diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/rpc/user/UserRpcImpl.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/rpc/user/UserRpcImpl.java index c00858f8f..0f048660b 100644 --- a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/rpc/user/UserRpcImpl.java +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/rpc/user/UserRpcImpl.java @@ -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 createUserIfAbsent(UserCreateDTO createDTO) { + return success(userManager.createUserIfAbsent(createDTO)); + } + } diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/sms/UserSmsCodeService.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/sms/UserSmsCodeService.java new file mode 100644 index 000000000..8e7ef0095 --- /dev/null +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/sms/UserSmsCodeService.java @@ -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); + } + +} diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/UserService.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/UserService.java index a95b640a8..f7939a92a 100644 --- a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/UserService.java +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/UserService.java @@ -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); + } + } diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/bo/UserBO.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/bo/UserBO.java index f027876c1..b8f4eaea2 100644 --- a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/bo/UserBO.java +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/bo/UserBO.java @@ -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; + } diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/bo/UserCreateBO.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/bo/UserCreateBO.java new file mode 100644 index 000000000..fca2d118e --- /dev/null +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/user/bo/UserCreateBO.java @@ -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; + +} diff --git a/user-service-project/user-service-app/src/main/resources/application.properties b/user-service-project/user-service-app/src/main/resources/application.properties new file mode 100644 index 000000000..6d7892da2 --- /dev/null +++ b/user-service-project/user-service-app/src/main/resources/application.properties @@ -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 diff --git a/user-service-project/user-service-app/src/main/resources/application.yaml b/user-service-project/user-service-app/src/main/resources/application.yaml index 831ab6dca..774ddf2d7 100644 --- a/user-service-project/user-service-app/src/main/resources/application.yaml +++ b/user-service-project/user-service-app/src/main/resources/application.yaml @@ -35,3 +35,5 @@ dubbo: filter: -exception UserRpc: version: 1.0.0 + UserSmsCodeRpc: + version: 1.0.0 diff --git a/user-web-app/pom.xml b/user-web-app/pom.xml index 3fabce8c2..4b365e4f1 100644 --- a/user-web-app/pom.xml +++ b/user-web-app/pom.xml @@ -11,16 +11,20 @@ user-web-app - - - - cn.iocoder.mall - mall-dependencies - 1.0-SNAPSHOT - pom - import - + + + + + cn.iocoder.mall + mall-dependencies + 1.0-SNAPSHOT + pom + import + + + + org.springframework.boot @@ -45,6 +49,33 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery + + + + org.projectlombok + lombok + + + + org.mapstruct + mapstruct + + + org.mapstruct + mapstruct-jdk8 + + + + + + + + + + + + + diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/UserWebApplication.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/UserWebApplication.java new file mode 100644 index 000000000..43c1de6a8 --- /dev/null +++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/UserWebApplication.java @@ -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); + } + +} diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/UserPassportController.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/UserPassportController.java new file mode 100644 index 000000000..8c888f71f --- /dev/null +++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/UserPassportController.java @@ -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 loginBySms(UserPassportLoginBySmsDTO loginBySmsDTO, + HttpServletRequest request) { + return CommonResult.success(userPassportManager.loginBySms(loginBySmsDTO, HttpUtil.getIp(request))); + } + + @PostMapping("/send_sms_code") + @ApiOperation("发送手机验证码") +// @RequiresNone TODO 晚点加上 + public CommonResult sendSmsCode(UserPassportSendSmsCodeDTO sendSmsCodeDTO, + HttpServletRequest request) { + userPassportManager.sendSmsCode(sendSmsCodeDTO, HttpUtil.getIp(request)); + // 返回成功 + return CommonResult.success(true); + } + +} diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportLoginBySmsDTO.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportLoginBySmsDTO.java new file mode 100644 index 000000000..ddb4e6d93 --- /dev/null +++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportLoginBySmsDTO.java @@ -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; + +} diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportSendSmsCodeDTO.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportSendSmsCodeDTO.java new file mode 100644 index 000000000..1880f70e3 --- /dev/null +++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportSendSmsCodeDTO.java @@ -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; + +} diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/vo/UserPassportVO.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/vo/UserPassportVO.java new file mode 100644 index 000000000..039fdcb17 --- /dev/null +++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/vo/UserPassportVO.java @@ -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; + +} diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/user/UserController.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/user/UserController.java new file mode 100644 index 000000000..b3bfea735 --- /dev/null +++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/user/UserController.java @@ -0,0 +1,4 @@ +package cn.iocoder.mall.userweb.controller.user; + +public class UserController { +} diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/convert/package-info.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/convert/package-info.java new file mode 100644 index 000000000..10838ccc1 --- /dev/null +++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/convert/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.mall.userweb.convert; diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/convert/passport/UserPassportConvert.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/convert/passport/UserPassportConvert.java new file mode 100644 index 000000000..2fb8ff27f --- /dev/null +++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/convert/passport/UserPassportConvert.java @@ -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); + +} diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/manager/passport/UserPassportManager.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/manager/passport/UserPassportManager.java new file mode 100644 index 000000000..72f755bc7 --- /dev/null +++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/manager/passport/UserPassportManager.java @@ -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 verifySmsCodeResult = userSmsCodeRpc.verifySmsCode(UserPassportConvert.INSTANCE.convert(loginBySmsDTO) + .setScene(UserSmsSceneEnum.LOGIN_BY_SMS.getValue()).setIp(ip)); + verifySmsCodeResult.checkError(); + // 获得用户 + CommonResult 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 sendSmsCodeResult = userSmsCodeRpc.sendSmsCode(UserPassportConvert.INSTANCE.convert(sendSmsCodeDTO).setIp(ip)); + sendSmsCodeResult.checkError(); + } + +} diff --git a/user-web-app/src/main/resources/application-dev.yml b/user-web-app/src/main/resources/application-dev.yml new file mode 100644 index 000000000..4cfe1567c --- /dev/null +++ b/user-web-app/src/main/resources/application-dev.yml @@ -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 服务注册中心的地址 diff --git a/user-web-app/src/main/resources/application-local.yml b/user-web-app/src/main/resources/application-local.yml new file mode 100644 index 000000000..2e9e99973 --- /dev/null +++ b/user-web-app/src/main/resources/application-local.yml @@ -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 服务注册中心的地址 diff --git a/user-web-app/src/main/resources/application.yml b/user-web-app/src/main/resources/application.yml new file mode 100644 index 000000000..5022b4b3f --- /dev/null +++ b/user-web-app/src/main/resources/application.yml @@ -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