diff --git a/http-client.env.json b/http-client.env.json index c4686b274..c18fbb054 100644 --- a/http-client.env.json +++ b/http-client.env.json @@ -2,6 +2,10 @@ "local": { "baseUrl": "http://127.0.0.1:48080/api", "userServerUrl": "http://127.0.0.1:28080/api", - "token": "test1" + "token": "test1", + + "userApi": "http://127.0.0.1:48080/app-api", + "userToken": "test1", + "userTenentId": "1" } } diff --git a/pom.xml b/pom.xml index 61e1b3379..5ca94ea06 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ yudao-admin-server yudao-user-server yudao-core-service + yudao-module-member yudao-module-bpm @@ -21,13 +22,14 @@ https://github.com/YunaiV/ruoyi-vue-pro - 1.3.0-snapshot + 1.4.0-snapshot 1.8 ${java.version} ${java.version} 3.0.0-M5 3.8.0 + 1.18.20 1.4.1.Final UTF-8 @@ -55,6 +57,7 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + org.apache.maven.plugins maven-compiler-plugin diff --git a/yudao-admin-server/pom.xml b/yudao-admin-server/pom.xml index be72f792f..4cb8d93e2 100644 --- a/yudao-admin-server/pom.xml +++ b/yudao-admin-server/pom.xml @@ -13,10 +13,21 @@ jar yudao-admin-server - 管理后台 Server,提供其 API 接口 + + 后端 Server 的主项目,通过引入需要 yudao-module-xxx 的依赖, + 从而实现提供 RESTful API 给 yudao-ui-admin、yudao-ui-user 等前端项目。 + 本质上来说,它就是个空壳(容器)! + https://github.com/YunaiV/ruoyi-vue-pro + + + cn.iocoder.boot + yudao-module-member-impl + ${revision} + + cn.iocoder.boot diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/AdminServerApplication.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/AdminServerApplication.java index 810869768..35f6a1d8d 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/AdminServerApplication.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/AdminServerApplication.java @@ -4,7 +4,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${yudao.info.base-package} 和 ${yudao.core-service.base-package} -@SpringBootApplication(scanBasePackages = {"${yudao.info.base-package}", "${yudao.core-service.base-package}"}) +@SpringBootApplication(scanBasePackages = {"${yudao.info.base-package}", "${yudao.core-service.base-package}", + "${yudao.info.member-package}"}) // TODO 芋艿:重构 public class AdminServerApplication { public static void main(String[] args) { diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java index e99c106b4..2d911a675 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java @@ -23,19 +23,28 @@ public class SecurityConfiguration { public Customizer.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer() { return registry -> { // 验证码的接口 - registry.antMatchers(api("/system/captcha/**")).anonymous(); + registry.antMatchers(buildAdminApi("/system/captcha/**")).anonymous(); // 获得租户编号的接口 - registry.antMatchers(api("/system/tenant/get-id-by-name")).anonymous(); + registry.antMatchers(buildAdminApi("/system/tenant/get-id-by-name")).anonymous(); // Spring Boot Admin Server 的安全配置 registry.antMatchers(adminSeverContextPath).anonymous() .antMatchers(adminSeverContextPath + "/**").anonymous(); // 短信回调 API - registry.antMatchers(api("/system/sms/callback/**")).anonymous(); + registry.antMatchers(buildAdminApi("/system/sms/callback/**")).anonymous(); + + // 设置 App API 无需认证 + registry.antMatchers(buildAppApi("/**")).permitAll(); }; } - private String api(String url) { - return webProperties.getApiPrefix() + url; + private String buildAdminApi(String url) { + // TODO 芋艿:多模块 + return webProperties.getAdminApi().getPrefix() + url; + } + + private String buildAppApi(String url) { + // TODO 芋艿:多模块 + return webProperties.getAppApi().getPrefix() + url; } } diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java index 5b67c99d6..cf0592eff 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java @@ -26,6 +26,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; import lombok.extern.slf4j.Slf4j; import me.zhyd.oauth.model.AuthUser; import org.springframework.context.annotation.Lazy; @@ -60,8 +61,6 @@ import static java.util.Collections.singleton; @Slf4j public class SysAuthServiceImpl implements SysAuthService { - private static final UserTypeEnum USER_TYPE_ENUM = UserTypeEnum.ADMIN; - @Resource @Lazy // 延迟加载,因为存在相互依赖的问题 private AuthenticationManager authenticationManager; @@ -83,7 +82,6 @@ public class SysAuthServiceImpl implements SysAuthService { @Resource private SysSocialCoreService socialService; - @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 获取 username 对应的 SysUserDO @@ -157,7 +155,8 @@ public class SysAuthServiceImpl implements SysAuthService { try { // 调用 Spring Security 的 AuthenticationManager#authenticate(...) 方法,使用账号密码进行认证 // 在其内部,会调用到 loadUserByUsername 方法,获取 User 信息 - authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); + authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken( + username, password, getUserType())); // org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username); } catch (BadCredentialsException badCredentialsException) { this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS); @@ -216,7 +215,7 @@ public class SysAuthServiceImpl implements SysAuthService { // 如果未绑定 SysSocialUserDO 用户,则无法自动登录,进行报错 String unionId = socialService.getAuthUserUnionId(authUser); - List socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, USER_TYPE_ENUM); + List socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, getUserType()); if (CollUtil.isEmpty(socialUsers)) { throw exception(AUTH_THIRD_LOGIN_NOT_BIND); } @@ -232,7 +231,7 @@ public class SysAuthServiceImpl implements SysAuthService { LoginUser loginUser = this.buildLoginUser(user); // 绑定社交用户(更新) - socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, USER_TYPE_ENUM); + socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, getUserType()); // 缓存登录用户到 Redis 中,返回 sessionId 编号 return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); @@ -248,7 +247,7 @@ public class SysAuthServiceImpl implements SysAuthService { LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword()); // 绑定社交用户(新增) - socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, USER_TYPE_ENUM); + socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, getUserType()); // 缓存登录用户到 Redis 中,返回 sessionId 编号 return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); @@ -261,7 +260,7 @@ public class SysAuthServiceImpl implements SysAuthService { Assert.notNull(authUser, "授权用户不为空"); // 绑定社交用户(新增) - socialService.bindSocialUser(userId, reqVO.getType(), authUser, USER_TYPE_ENUM); + socialService.bindSocialUser(userId, reqVO.getType(), authUser, getUserType()); } @Override @@ -277,12 +276,17 @@ public class SysAuthServiceImpl implements SysAuthService { this.createLogoutLog(loginUser.getId(), loginUser.getUsername()); } + @Override + public UserTypeEnum getUserType() { + return UserTypeEnum.ADMIN; + } + private void createLogoutLog(Long userId, String username) { SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO(); reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType()); reqDTO.setTraceId(TracerUtils.getTraceId()); reqDTO.setUserId(userId); - reqDTO.setUserType(USER_TYPE_ENUM.getValue()); + reqDTO.setUserType(getUserType().getValue()); reqDTO.setUsername(username); reqDTO.setUserAgent(ServletUtils.getUserAgent()); reqDTO.setUserIp(ServletUtils.getClientIP()); diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java index 119b78e1e..1b6c4e473 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java @@ -24,6 +24,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -48,7 +49,7 @@ public class SysUserServiceImpl implements SysUserService { @Value("${sys.user.init-password:yudaoyuanma}") private String userInitPassword; - @Resource + @Resource(name = "sysUserMapper") // userMapper 存在重名 private SysUserMapper userMapper; @Resource diff --git a/yudao-admin-server/src/main/resources/application-local.yaml b/yudao-admin-server/src/main/resources/application-local.yaml index 2e26fd6fe..411f2a6a3 100644 --- a/yudao-admin-server/src/main/resources/application-local.yaml +++ b/yudao-admin-server/src/main/resources/application-local.yaml @@ -55,6 +55,19 @@ spring: username: root password: 123456 + activiti: + #1.false:默认值,activiti启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常 + #2.true:启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表 + #3.create_drop:启动时自动创建表,关闭时自动删除表 + #4.drop_create:启动时,删除旧表,再创建新表 + database-schema-update: true + #activiti7默认不生成历史信息表,需手动设置开启 + db-history-used: true + check-process-definitions: true + #full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数 + history-level: full + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: host: 127.0.0.1 # 地址 @@ -161,6 +174,18 @@ logging: cn.iocoder.yudao.coreservice.modules.system.dal.mysql: debug cn.iocoder.yudao.coreservice.modules.tool.dal.mysql: debug +--- #################### 微信公众号相关配置 #################### +wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 + mp: + # 公众号配置(必填) + app-id: wx041349c6f39b268b + secret: 5abee519483bc9f8cb37ce280e814bd0 + # 存储配置,解决 AccessToken 的跨节点的共享 + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + --- #################### 芋道相关配置 #################### # 芋道配置项,设置当前项目所有自定义的配置 diff --git a/yudao-admin-server/src/main/resources/application.yaml b/yudao-admin-server/src/main/resources/application.yaml index 7652dd5be..09bbc33ad 100644 --- a/yudao-admin-server/src/main/resources/application.yaml +++ b/yudao-admin-server/src/main/resources/application.yaml @@ -48,11 +48,17 @@ yudao: info: version: 1.0.0 base-package: cn.iocoder.yudao.adminserver + member-package: cn.iocoder.yudao.module.member core-service: base-package: cn.iocoder.yudao.coreservice web: - api-prefix: /api - controller-package: ${yudao.info.base-package} + admin-api: + prefix: /api + controller: ${yudao.info.base-package} + app-api: + prefix: /app-api + controller: cn.iocoder.yudao.module.member.controller.app + swagger: title: 管理后台 description: 提供管理员管理的所有功能 @@ -72,7 +78,13 @@ yudao: - cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants tenant: # 多租户相关配置项 tables: # 配置需要开启多租户的表;如果实体已经继承 TenantBaseDO 类,则无需重复配置 - url: + url: ## TODO 芋艿:迁移到 web 配置项下, admin-ui: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址 + sms-code: # 短信验证码相关的配置项 + expire-times: 10m + send-frequency: 1m + send-maximum-quantity-per-day: 10 + begin-code: 9999 # 这里配置 9999 的原因是,测试方便。 + end-code: 9999 # 这里配置 9999 的原因是,测试方便。 debug: false diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/infra/service/file/InfFileCoreService.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/infra/service/file/InfFileCoreService.java index a594bcef4..8ba8b57fe 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/infra/service/file/InfFileCoreService.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/infra/service/file/InfFileCoreService.java @@ -9,7 +9,6 @@ import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO; */ public interface InfFileCoreService { - /** * 保存文件,并返回文件的访问路径 * diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/convert/package-info.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/convert/package-info.java deleted file mode 100644 index 230bbc092..000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/convert/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 提供 POJO 类的实体转换 - * - * 目前使用 MapStruct 框架 - */ -package cn.iocoder.yudao.coreservice.modules.member.convert; diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/dal/mysql/user/MbrUserCoreMapper.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/dal/mysql/user/MbrUserCoreMapper.java deleted file mode 100644 index 27c0c9509..000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/dal/mysql/user/MbrUserCoreMapper.java +++ /dev/null @@ -1,10 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.member.dal.mysql.user; - -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface MbrUserCoreMapper extends BaseMapperX { - -} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/package-info.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/package-info.java deleted file mode 100644 index 17abe90bd..000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * member 包下,我们放会员业务. - * 例如说:会员中心等等 - * - * 缩写:mbr - */ -package cn.iocoder.yudao.coreservice.modules.member; diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/service/user/MbrUserCoreService.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/service/user/MbrUserCoreService.java deleted file mode 100644 index 934413956..000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/service/user/MbrUserCoreService.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.member.service.user; - -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; - -/** - * 前台用户 Core Service 接口 - * - * @author 芋道源码 - */ -public interface MbrUserCoreService { - /** - * 通过用户 ID 查询用户 - * - * @param id 用户ID - * @return 用户对象信息 - */ - MbrUserDO getUser(Long id); - -} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/service/user/impl/MbrUserCoreServiceImpl.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/service/user/impl/MbrUserCoreServiceImpl.java deleted file mode 100644 index 08ee22c76..000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/service/user/impl/MbrUserCoreServiceImpl.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.member.service.user.impl; - -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; -import cn.iocoder.yudao.coreservice.modules.member.dal.mysql.user.MbrUserCoreMapper; -import cn.iocoder.yudao.coreservice.modules.member.service.user.MbrUserCoreService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; - -/** - * User Core Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class MbrUserCoreServiceImpl implements MbrUserCoreService { - - @Resource - private MbrUserCoreMapper userCoreMapper; - - @Override - public MbrUserDO getUser(Long id) { - return userCoreMapper.selectById(id); - } - -} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/sms/impl/SysSmsCoreServiceImpl.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/sms/impl/SysSmsCoreServiceImpl.java index 3893ba340..787d2a1d9 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/sms/impl/SysSmsCoreServiceImpl.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/sms/impl/SysSmsCoreServiceImpl.java @@ -2,8 +2,6 @@ package cn.iocoder.yudao.coreservice.modules.system.service.sms.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; -import cn.iocoder.yudao.coreservice.modules.member.service.user.MbrUserCoreService; import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO; import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO; import cn.iocoder.yudao.coreservice.modules.system.mq.message.sms.SysSmsSendMessage; @@ -43,8 +41,6 @@ public class SysSmsCoreServiceImpl implements SysSmsCoreService { @Resource private SysUserCoreService sysUserCoreService; @Resource - private MbrUserCoreService mbrUserCoreService; - @Resource private SysSmsTemplateCoreService smsTemplateCoreService; @Resource private SysSmsLogCoreService smsLogCoreService; @@ -72,10 +68,11 @@ public class SysSmsCoreServiceImpl implements SysSmsCoreService { public Long sendSingleSmsToMember(String mobile, Long userId, String templateCode, Map templateParams) { // 如果 mobile 为空,则加载用户编号对应的手机号 if (StrUtil.isEmpty(mobile)) { - MbrUserDO user = mbrUserCoreService.getUser(userId); - if (user != null) { - mobile = user.getMobile(); - } +// MbrUserDO user = mbrUserCoreService.getUser(userId); +// if (user != null) { +// mobile = user.getMobile(); +// } + // TODO 芋艿:重构 } // 执行发送 return this.sendSingleSms(mobile, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams); diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/tool/package-info.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/tool/package-info.java deleted file mode 100644 index dd3c3e913..000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/tool/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * tool 包下,我们放研发工具,提升研发效率与质量。 - * 例如说:代码生成器、接口文档等等 - * - * 缩写:tool - */ -package cn.iocoder.yudao.coreservice.modules.tool; diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 512fefdc3..e241cb87a 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -14,7 +14,7 @@ https://github.com/YunaiV/ruoyi-vue-pro - 1.3.0-snapshot + 1.4.0-snapshot 2.4.12 diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java index 7318fb045..edaba1dd9 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.framework.common.enums; +import cn.hutool.core.lang.Matcher; +import cn.hutool.core.util.ArrayUtil; import lombok.AllArgsConstructor; import lombok.Getter; @@ -22,4 +24,8 @@ public enum UserTypeEnum { */ private final String name; + public static UserTypeEnum valueOf(Integer value) { + return ArrayUtil.firstMatch(userType -> userType.getValue().equals(value), UserTypeEnum.values()); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java index a7f23a391..6cda19f82 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java @@ -149,6 +149,7 @@ public class OperateLogAspect { private static void fillUserFields(OperateLogCreateReqDTO operateLogDTO) { operateLogDTO.setUserId(WebFrameworkUtils.getLoginUserId()); + operateLogDTO.setUserType(WebFrameworkUtils.getLoginUserType()); } private static void fillModuleFields(OperateLogCreateReqDTO operateLogDTO, diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java index 1c8f49a26..d676001f2 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java @@ -21,6 +21,9 @@ public class OperateLogCreateReqDTO { @ApiModelProperty(value = "用户编号", required = true, example = "1024") @NotNull(message = "用户编号不能为空") private Long userId; + @ApiModelProperty(value = "用户类型", required = true, example = "1") + @NotNull(message = "用户类型不能为空") + private Integer userType; @ApiModelProperty(value = "操作模块", required = true, example = "订单") @NotEmpty(message = "操作模块不能为空") diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java index b03fc58e4..d589ac768 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java @@ -15,7 +15,8 @@ import org.springframework.context.annotation.Configuration; * @author 芋道源码 */ @Configuration -@MapperScan(value = {"${yudao.info.base-package}", "${yudao.core-service.base-package}"}, annotationClass = Mapper.class, +@MapperScan(value = {"${yudao.info.base-package}", "${yudao.core-service.base-package}", "${yudao.info.member-package}"}, + annotationClass = Mapper.class, lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载,目前仅用于单元测试 public class YudaoMybatisAutoConfiguration { diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java index 684cfed9a..d5953823a 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java @@ -1,12 +1,14 @@ package cn.iocoder.yudao.framework.security.config; import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect; +import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; import cn.iocoder.yudao.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy; import cn.iocoder.yudao.framework.security.core.filter.JWTAuthenticationTokenFilter; import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl; import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; +import cn.iocoder.yudao.framework.web.config.WebProperties; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; import org.springframework.beans.factory.config.MethodInvokingFactoryBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -20,6 +22,7 @@ import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import javax.annotation.Resource; +import java.util.List; /** * Spring Security 自动配置类,主要用于相关组件的配置 @@ -29,7 +32,7 @@ import javax.annotation.Resource; * * @author 芋道源码 */ -@Configuration +@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(SecurityProperties.class) public class YudaoSecurityAutoConfiguration { @@ -64,8 +67,8 @@ public class YudaoSecurityAutoConfiguration { * 退出处理类 Bean */ @Bean - public LogoutSuccessHandler logoutSuccessHandler(SecurityAuthFrameworkService securityFrameworkService) { - return new LogoutSuccessHandlerImpl(securityProperties, securityFrameworkService); + public LogoutSuccessHandler logoutSuccessHandler(MultiUserDetailsAuthenticationProvider authenticationProvider) { + return new LogoutSuccessHandlerImpl(securityProperties, authenticationProvider); } /** @@ -83,9 +86,19 @@ public class YudaoSecurityAutoConfiguration { * Token 认证过滤器 Bean */ @Bean - public JWTAuthenticationTokenFilter authenticationTokenFilter(SecurityAuthFrameworkService securityFrameworkService, + public JWTAuthenticationTokenFilter authenticationTokenFilter(MultiUserDetailsAuthenticationProvider authenticationProvider, GlobalExceptionHandler globalExceptionHandler) { - return new JWTAuthenticationTokenFilter(securityProperties, securityFrameworkService, globalExceptionHandler); + return new JWTAuthenticationTokenFilter(securityProperties, authenticationProvider, globalExceptionHandler); + } + + /** + * 身份验证的 Provider Bean,通过它实现账号 + 密码的认证 + */ + @Bean + public MultiUserDetailsAuthenticationProvider authenticationProvider( + List securityFrameworkServices, + WebProperties webProperties, PasswordEncoder passwordEncoder) { + return new MultiUserDetailsAuthenticationProvider(securityFrameworkServices, webProperties, passwordEncoder); } /** diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java index a0f1c87cc..276ce1ed5 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.security.config; +import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; import cn.iocoder.yudao.framework.security.core.filter.JWTAuthenticationTokenFilter; import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; import cn.iocoder.yudao.framework.web.config.WebProperties; @@ -35,16 +36,8 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap @Resource private WebProperties webProperties; - /** - * 自定义用户【认证】逻辑 - */ @Resource - private SecurityAuthFrameworkService userDetailsService; - /** - * Spring Security 加密器 - */ - @Resource - private PasswordEncoder passwordEncoder; + private MultiUserDetailsAuthenticationProvider authenticationProvider; /** * 认证失败处理类 Bean */ @@ -65,13 +58,15 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap */ @Resource private JWTAuthenticationTokenFilter authenticationTokenFilter; + /** * 自定义的权限映射 Bean * * @see #configure(HttpSecurity) */ @Resource - private Customizer.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer; + private Customizer.ExpressionInterceptUrlRegistry> + authorizeRequestsCustomizer; /** * 由于 Spring Security 创建 AuthenticationManager 对象时,没声明 @Bean 注解,导致无法被注入 @@ -89,8 +84,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(userDetailsService) - .passwordEncoder(passwordEncoder); + auth.authenticationProvider(authenticationProvider); } /** @@ -123,16 +117,16 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap // 一堆自定义的 Spring Security 处理器 .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) .accessDeniedHandler(accessDeniedHandler).and() - .logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); // 登出 + .logout().logoutUrl(buildAdminApi("/logout")).logoutSuccessHandler(logoutSuccessHandler); // 登出 // 设置每个请求的权限 ①:全局共享规则 httpSecurity.authorizeRequests() // 登录的接口,可匿名访问 - .antMatchers(api("/login")).anonymous() + .antMatchers(buildAdminApi("/login")).anonymous() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() // 文件的获取接口,可匿名访问 - .antMatchers(api("/infra/file/get/**")).anonymous() + .antMatchers(buildAdminApi("/infra/file/get/**")).anonymous() // Swagger 接口文档 .antMatchers("/swagger-ui.html").anonymous() .antMatchers("/swagger-resources/**").anonymous() @@ -143,11 +137,11 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap .antMatchers("/actuator/**").anonymous() // Druid 监控 TODO 芋艿:等对接了 druid admin 后,在调整下。 .antMatchers("/druid/**").anonymous() - // oAuth2 auth2/login/gitee - .antMatchers(api("/auth2/login/**")).anonymous() - .antMatchers(api("/auth2/authorization/**")).anonymous() + // oAuth2 auth2/login/gitee TODO 芋艿:貌似可以删除 + .antMatchers(buildAdminApi("/auth2/login/**")).anonymous() + .antMatchers(buildAdminApi("/auth2/authorization/**")).anonymous() .antMatchers("/api/callback/**").anonymous() - // 设置每个请求的权限 ②:每个项目的自定义规则 + // 设置每个请求的权限 ②:每个项目的自定义规则 TODO 芋艿:改造成多个,方便每个模块自定义规则 .and().authorizeRequests(authorizeRequestsCustomizer) // 设置每个请求的权限 ③:兜底规则,必须认证 .authorizeRequests().anyRequest().authenticated() @@ -156,8 +150,14 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); } - private String api(String url) { - return webProperties.getApiPrefix() + url; + private String buildAdminApi(String url) { + // TODO 芋艿:多模块 + return webProperties.getAdminApi().getPrefix() + url; + } + + private String buildAppApi(String url) { + // TODO 芋艿:多模块 + return webProperties.getAppApi().getPrefix() + url; } } diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUserDetailsAuthenticationProvider.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUserDetailsAuthenticationProvider.java new file mode 100644 index 000000000..dc8533f96 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUserDetailsAuthenticationProvider.java @@ -0,0 +1,149 @@ +package cn.iocoder.yudao.framework.security.core.authentication; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; +import cn.iocoder.yudao.framework.web.config.WebProperties; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.password.PasswordEncoder; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 支持多用户类型的 AuthenticationProvider 实现类 + * + * 为什么不用 {@link org.springframework.security.authentication.ProviderManager} 呢? + * 原因是,需要每个用户类型实现对应的 {@link AuthenticationProvider} + authentication,略显麻烦。实际,也是可以实现的。 + * + * 另外,额外支持 verifyTokenAndRefresh 校验令牌、logout 登出、mockLogin 模拟登陆等操作。 + * 实际上,它就是 {@link SecurityAuthFrameworkService} 定义的三个接口。 + * 因为需要支持多种类型,所以需要根据请求的 URL,判断出对应的用户类型,从而使用对应的 SecurityAuthFrameworkService 是吸纳 + * + * @see cn.iocoder.yudao.framework.common.enums.UserTypeEnum + * @author 芋道源码 + */ +public class MultiUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { + + private final Map services = new HashMap<>(); + + private final WebProperties properties; + + private final PasswordEncoder passwordEncoder; + + public MultiUserDetailsAuthenticationProvider(List serviceList, + WebProperties properties, PasswordEncoder passwordEncoder) { + serviceList.forEach(service -> services.put(service.getUserType(), service)); + this.properties = properties; + this.passwordEncoder = passwordEncoder; + } + + // ========== AuthenticationProvider 相关 ========== + + @Override + protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) + throws AuthenticationException { + // 执行用户的加载 + return selectService(authentication).loadUserByUsername(username); + } + + private SecurityAuthFrameworkService selectService(UsernamePasswordAuthenticationToken authentication) { + // 第一步,获得用户类型 + UserTypeEnum userType = getUserType(authentication); + // 第二步,获得 SecurityAuthFrameworkService + SecurityAuthFrameworkService service = services.get(userType); + Assert.notNull(service, "用户类型({}) 找不到 SecurityAuthFrameworkService 实现类", userType); + return service; + } + + private UserTypeEnum getUserType(UsernamePasswordAuthenticationToken authentication) { + Assert.isInstanceOf(MultiUsernamePasswordAuthenticationToken.class, authentication); + MultiUsernamePasswordAuthenticationToken multiAuthentication = (MultiUsernamePasswordAuthenticationToken) authentication; + UserTypeEnum userType = multiAuthentication.getUserType(); + Assert.notNull(userType, "用户类型不能为空"); + return userType; + } + + @Override // copy 自 DaoAuthenticationProvider 的 additionalAuthenticationChecks 方法 + protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) + throws AuthenticationException { + // 校验 credentials + if (authentication.getCredentials() == null) { + this.logger.debug("Failed to authenticate since no credentials provided"); + throw new BadCredentialsException(this.messages.getMessage( + "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + } + // 校验 password + String presentedPassword = authentication.getCredentials().toString(); + if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { + this.logger.debug("Failed to authenticate since password does not match stored value"); + throw new BadCredentialsException(this.messages.getMessage( + "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + } + } + + // ========== SecurityAuthFrameworkService 相关 ========== + + /** + * 校验 token 的有效性,并获取用户信息 + * 通过后,刷新 token 的过期时间 + * + * @param request 请求 + * @param token token + * @return 用户信息 + */ + public LoginUser verifyTokenAndRefresh(HttpServletRequest request, String token) { + return selectService(request).verifyTokenAndRefresh(token); + } + + /** + * 模拟指定用户编号的 LoginUser + * + * @param request 请求 + * @param userId 用户编号 + * @return 登录用户 + */ + public LoginUser mockLogin(HttpServletRequest request, Long userId) { + return selectService(request).mockLogin(userId); + } + + /** + * 基于 token 退出登录 + * + * @param request 请求 + * @param token token + */ + public void logout(HttpServletRequest request, String token) { + selectService(request).logout(token); + } + + private SecurityAuthFrameworkService selectService(HttpServletRequest request) { + // 第一步,获得用户类型 + UserTypeEnum userType = getUserType(request); + // 第二步,获得 SecurityAuthFrameworkService + SecurityAuthFrameworkService service = services.get(userType); + Assert.notNull(service, "URI({}) 用户类型({}) 找不到 SecurityAuthFrameworkService 实现类", + request.getRequestURI(), userType); + return service; + } + + private UserTypeEnum getUserType(HttpServletRequest request) { + if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) { + return UserTypeEnum.ADMIN; + } + if (request.getRequestURI().startsWith(properties.getAppApi().getPrefix())) { + return UserTypeEnum.MEMBER; + } + throw new IllegalArgumentException(StrUtil.format("URI({}) 找不到匹配的用户类型", request.getRequestURI())); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUsernamePasswordAuthenticationToken.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUsernamePasswordAuthenticationToken.java new file mode 100644 index 000000000..f0bc8dfac --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUsernamePasswordAuthenticationToken.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.framework.security.core.authentication; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import lombok.Getter; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +/** + * 支持多用户的 UsernamePasswordAuthenticationToken 实现类 + * + * @author 芋道源码 + */ +@Getter +public class MultiUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken { + + /** + * 用户类型 + */ + private UserTypeEnum userType; + + public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials) { + super(principal, credentials); + } + + public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials, + Collection authorities) { + super(principal, credentials, authorities); + } + + public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials, UserTypeEnum userType) { + super(principal, credentials); + this.userType = userType; + } + + public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials, + Collection authorities, UserTypeEnum userType) { + super(principal, credentials, authorities); + this.userType = userType; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JWTAuthenticationTokenFilter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JWTAuthenticationTokenFilter.java index c83318b0d..804c88d35 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JWTAuthenticationTokenFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JWTAuthenticationTokenFilter.java @@ -2,17 +2,15 @@ package cn.iocoder.yudao.framework.security.core.filter; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.security.config.SecurityProperties; import cn.iocoder.yudao.framework.security.core.LoginUser; -import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; +import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; -import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Component; +import lombok.RequiredArgsConstructor; import org.springframework.web.filter.OncePerRequestFilter; -import javax.annotation.Resource; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -25,12 +23,12 @@ import java.io.IOException; * * @author 芋道源码 */ -@AllArgsConstructor +@RequiredArgsConstructor public class JWTAuthenticationTokenFilter extends OncePerRequestFilter { private final SecurityProperties securityProperties; - private final SecurityAuthFrameworkService authService; + private final MultiUserDetailsAuthenticationProvider authenticationProvider; private final GlobalExceptionHandler globalExceptionHandler; @@ -42,10 +40,10 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter { if (StrUtil.isNotEmpty(token)) { try { // 验证 token 有效性 - LoginUser loginUser = authService.verifyTokenAndRefresh(token); + LoginUser loginUser = authenticationProvider.verifyTokenAndRefresh(request, token); // 模拟 Login 功能,方便日常开发调试 if (loginUser == null) { - loginUser = this.mockLoginUser(token); + loginUser = this.mockLoginUser(request, token); } // 设置当前用户 if (loginUser != null) { @@ -67,10 +65,11 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter { * * 注意,在线上环境下,一定要关闭该功能!!! * + * @param request 请求 * @param token 模拟的 token,格式为 {@link SecurityProperties#getTokenSecret()} + 用户编号 * @return 模拟的 LoginUser */ - private LoginUser mockLoginUser(String token) { + private LoginUser mockLoginUser(HttpServletRequest request, String token) { if (!securityProperties.getMockEnable()) { return null; } @@ -79,7 +78,7 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter { return null; } Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length())); - return authService.mockLogin(userId); + return authenticationProvider.mockLogin(request, userId); } } diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/AbstractSignUpUrlAuthenticationSuccessHandler.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/AbstractSignUpUrlAuthenticationSuccessHandler.java deleted file mode 100644 index 01fdf0888..000000000 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/AbstractSignUpUrlAuthenticationSuccessHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * MIT License - * Copyright (c) 2020-2029 YongWu zheng (dcenter.top and gitee.com/pcore and github.com/ZeroOrInfinity) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package cn.iocoder.yudao.framework.security.core.handler; - -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; -import org.springframework.security.web.savedrequest.HttpSessionRequestCache; -import org.springframework.security.web.savedrequest.RequestCache; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author weir - */ -public class AbstractSignUpUrlAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { - private RequestCache requestCache = new HttpSessionRequestCache(); - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { - if (requestCache.getRequest(request, response) != null) { - requestCache.getRequest(request, response); - } - super.onAuthenticationSuccess(request,response,authentication); - } - - @Override - public void setRequestCache(RequestCache requestCache) { - this.requestCache = requestCache; - } -} diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/LogoutSuccessHandlerImpl.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/LogoutSuccessHandlerImpl.java index 1bf8091be..1a642304c 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/LogoutSuccessHandlerImpl.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/LogoutSuccessHandlerImpl.java @@ -2,16 +2,14 @@ package cn.iocoder.yudao.framework.security.core.handler; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.security.config.SecurityProperties; -import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; +import cn.iocoder.yudao.framework.security.config.SecurityProperties; +import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import lombok.AllArgsConstructor; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; -import org.springframework.stereotype.Component; -import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -26,14 +24,14 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { private final SecurityProperties securityProperties; - private final SecurityAuthFrameworkService securityFrameworkService; + private final MultiUserDetailsAuthenticationProvider authenticationProvider; @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { // 执行退出 String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); if (StrUtil.isNotBlank(token)) { - securityFrameworkService.logout(token); + authenticationProvider.logout(request, token); } // 返回成功 ServletUtils.writeJSON(response, CommonResult.success(null)); diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java index 28c8f6dc0..1f76e161f 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java @@ -1,10 +1,11 @@ package cn.iocoder.yudao.framework.security.core.service; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.security.core.LoginUser; import org.springframework.security.core.userdetails.UserDetailsService; /** - * Security 框架 Auth Service 接口,定义 security 组件需要的功能 + * Security 框架 Auth Service 接口,定义不同用户类型的 {@link UserTypeEnum} 需要实现的方法 * * @author 芋道源码 */ @@ -34,4 +35,11 @@ public interface SecurityAuthFrameworkService extends UserDetailsService { */ void logout(String token); + /** + * 获得用户类型。每个用户类型,对应一个 SecurityAuthFrameworkService 实现类。 + * + * @return 用户类型 + */ + UserTypeEnum getUserType(); + } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java index 57133c840..3295e34c1 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.apilog.core.filter; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.extra.servlet.ServletUtil; import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -25,6 +26,8 @@ import java.io.IOException; import java.util.Date; import java.util.Map; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.*; + /** * API 访问日志 Filter * @@ -42,7 +45,8 @@ public class ApiAccessLogFilter extends OncePerRequestFilter { @Override protected boolean shouldNotFilter(HttpServletRequest request) { // 只过滤 API 请求的地址 - return !request.getRequestURI().startsWith(webProperties.getApiPrefix()); + return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAppApi().getPrefix(), + webProperties.getAppApi().getPrefix()); } @Override @@ -73,7 +77,7 @@ public class ApiAccessLogFilter extends OncePerRequestFilter { this.buildApiAccessLogDTO(accessLog, request, beginTime, queryString, requestBody, ex); apiAccessLogFrameworkService.createApiAccessLogAsync(accessLog); } catch (Throwable th) { - log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), JsonUtils.toJsonString(accessLog), th); + log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), toJsonString(accessLog), th); } } @@ -99,7 +103,7 @@ public class ApiAccessLogFilter extends OncePerRequestFilter { accessLog.setApplicationName(applicationName); accessLog.setRequestUrl(request.getRequestURI()); Map requestParams = MapUtil.builder().put("query", queryString).put("body", requestBody).build(); - accessLog.setRequestParams(JsonUtils.toJsonString(requestParams)); + accessLog.setRequestParams(toJsonString(requestParams)); accessLog.setRequestMethod(request.getMethod()); accessLog.setUserAgent(ServletUtils.getUserAgent(request)); accessLog.setUserIp(ServletUtil.getClientIP(request)); diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/WebProperties.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/WebProperties.java index 2edc3e883..23a4aff4a 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/WebProperties.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/WebProperties.java @@ -5,6 +5,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.validation.annotation.Validated; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; +import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @ConfigurationProperties(prefix = "yudao.web") @@ -12,26 +14,37 @@ import javax.validation.constraints.NotNull; @Data public class WebProperties { - /** - * API 前缀,实现所有 Controller 提供的 RESTFul API 的统一前缀 - * - * - * 意义:通过该前缀,避免 Swagger、Actuator 意外通过 Nginx 暴露出来给外部,带来安全性问题 - * 这样,Nginx 只需要配置转发到 /api/* 的所有接口即可。 - * - * @see YudaoWebAutoConfiguration#configurePathMatch(PathMatchConfigurer) - */ - @NotNull(message = "API 前缀不能为空") - private String apiPrefix; + @NotNull(message = "APP API 不能为空") + private Api appApi; + @NotNull(message = "Admin API 不能为空") + private Api adminApi; - /** - * Controller 所在包 - * - * 主要目的是,给该 Controller 设置指定的 {@link #apiPrefix} - * - * 因为我们有多个 modules 包里会包含 Controller,所以只需要写到 cn.iocoder.yudao 这样的层级 - */ - @NotNull(message = "Controller 所在包不能为空") - private String controllerPackage; + @Data + @Valid + public static class Api { + + /** + * API 前缀,实现所有 Controller 提供的 RESTFul API 的统一前缀 + * + * + * 意义:通过该前缀,避免 Swagger、Actuator 意外通过 Nginx 暴露出来给外部,带来安全性问题 + * 这样,Nginx 只需要配置转发到 /api/* 的所有接口即可。 + * + * @see YudaoWebAutoConfiguration#configurePathMatch(PathMatchConfigurer) + */ + @NotEmpty(message = "API 前缀不能为空") + private String prefix; + + /** + * Controller 所在包 + * + * 主要目的是,给该 Controller 设置指定的 {@link #prefix} + * + * 因为我们有多个 modules 包里会包含 Controller,所以只需要写到 cn.iocoder.yudao 这样的层级 + */ + @NotEmpty(message = "Controller 所在包不能为空") + private String controller; + + } } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java index 5d390cdb1..c3bcb2009 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java @@ -38,10 +38,19 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer { @Override public void configurePathMatch(PathMatchConfigurer configurer) { - // 设置 API 前缀,仅仅匹配 controller 包下的 - configurer.addPathPrefix(webProperties.getApiPrefix(), clazz -> - clazz.isAnnotationPresent(RestController.class) - && clazz.getPackage().getName().startsWith(webProperties.getControllerPackage())); // 仅仅匹配 controller 包 + configurePathMatch(configurer, webProperties.getAdminApi()); + configurePathMatch(configurer, webProperties.getAppApi()); + } + + /** + * 设置 API 前缀,仅仅匹配 controller 包下的 + * + * @param configurer 配置 + * @param api API 配置 + */ + private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) { + configurer.addPathPrefix(api.getPrefix(), clazz -> clazz.isAnnotationPresent(RestController.class) + && clazz.getPackage().getName().startsWith(api.getController())); // 仅仅匹配 controller 包 } @Bean diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java index acbfb9c2b..273f34072 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java @@ -55,6 +55,11 @@ public class WebFrameworkUtils { return (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE); } + public static Integer getLoginUserType() { + HttpServletRequest request = getRequest(); + return getLoginUserType(request); + } + public static Long getLoginUserId() { HttpServletRequest request = getRequest(); return getLoginUserId(request); diff --git a/yudao-module-bpm/yudao-module-bpm-activiti/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/config/BpmActivitiConfiguration.java b/yudao-module-bpm/yudao-module-bpm-activiti/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/config/BpmActivitiConfiguration.java index 72210a3af..8e30c22db 100644 --- a/yudao-module-bpm/yudao-module-bpm-activiti/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/config/BpmActivitiConfiguration.java +++ b/yudao-module-bpm/yudao-module-bpm-activiti/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/config/BpmActivitiConfiguration.java @@ -10,10 +10,17 @@ import cn.iocoder.yudao.coreservice.modules.bpm.api.group.BpmUserGroupServiceApi import cn.iocoder.yudao.coreservice.modules.system.service.dept.SysDeptCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.permission.SysPermissionCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.user.SysUserCoreService; +import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmUserGroupService; +import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService; +import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService; +import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService; +import org.activiti.api.runtime.shared.identity.UserGroupManager; +import org.activiti.core.common.spring.identity.ActivitiUserGroupManagerImpl; import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.userdetails.UserDetailsService; import java.util.Collections; import java.util.List; @@ -27,7 +34,15 @@ import static org.activiti.spring.boot.ProcessEngineAutoConfiguration.BEHAVIOR_F public class BpmActivitiConfiguration { /** - * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类,主要设置各种监听器、用户组管理 + * 空用户组的 Bean + */ + @Bean + public UserGroupManager userGroupManager() { + return new EmptyUserGroupManager(); + } + + /** + * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类,主要设置各种监听器 */ @Bean public ProcessEngineConfigurationConfigurer bpmProcessEngineConfigurationConfigurer( @@ -35,8 +50,6 @@ public class BpmActivitiConfiguration { return configuration -> { // 注册监听器,例如说 BpmActivitiEventListener configuration.setEventListeners(Collections.singletonList(taskActivitiEventListener)); - // 用户组 - configuration.setUserGroupManager(new EmptyUserGroupManager()); }; } diff --git a/yudao-module-member/pom.xml b/yudao-module-member/pom.xml new file mode 100644 index 000000000..cf150725a --- /dev/null +++ b/yudao-module-member/pom.xml @@ -0,0 +1,24 @@ + + + + cn.iocoder.boot + yudao + ${revision} + + 4.0.0 + + yudao-module-member-api + yudao-module-member-impl + + yudao-module-member + pom + + ${artifactId} + + member 模块,我们放会员业务。 + 例如说:会员中心等等 + + + diff --git a/yudao-module-member/yudao-module-member-api/pom.xml b/yudao-module-member/yudao-module-member-api/pom.xml new file mode 100644 index 000000000..914d91082 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/pom.xml @@ -0,0 +1,26 @@ + + + + cn.iocoder.boot + yudao-module-member + ${revision} + + 4.0.0 + yudao-module-member-api + jar + + ${artifactId} + + member 模块 API,暴露给其它模块调用 + + + + + cn.iocoder.boot + yudao-common + + + + diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java new file mode 100644 index 000000000..56cd9857f --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java @@ -0,0 +1,4 @@ +/** + * member API 包,定义暴露给其它模块的 API + */ +package cn.iocoder.yudao.module.member.api; diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/UserApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/UserApi.java new file mode 100644 index 000000000..ba742a259 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/UserApi.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.member.api.user; + +import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO; + +/** + * 会员用户的 API 接口 + * + * @author 芋道源码 + */ +public interface UserApi { + + /** + * 获得会员用户信息 + * + * @param id 用户编号 + * @return 用户信息 + */ + UserRespDTO getUser(Long id); + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserRespDTO.java new file mode 100644 index 000000000..ef2ecde49 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserRespDTO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.member.api.user.dto; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; + +/** + * 用户信息 Response DTO + * + * @author 芋道源码 + */ +public class UserRespDTO { + + /** + * 用户ID + */ + private Long id; + /** + * 用户昵称 + */ + private String nickname; + /** + * 帐号状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + + /** + * 手机 + */ + private String mobile; + +} diff --git a/yudao-module-member/yudao-module-member-impl/pom.xml b/yudao-module-member/yudao-module-member-impl/pom.xml new file mode 100644 index 000000000..ce7ff84a0 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/pom.xml @@ -0,0 +1,101 @@ + + + + cn.iocoder.boot + yudao-module-member + ${revision} + + 4.0.0 + yudao-module-member-impl + jar + + ${artifactId} + + member 模块,我们放会员业务。 + 例如说:会员中心等等 + + + + + cn.iocoder.boot + yudao-module-member-api + ${revision} + + + + + cn.iocoder.boot + yudao-core-service + + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-operatelog + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-sms + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-weixin + + + + + cn.iocoder.boot + yudao-spring-boot-starter-security + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mybatis + + + + cn.iocoder.boot + yudao-spring-boot-starter-redis + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mq + + + + + cn.iocoder.boot + yudao-spring-boot-starter-test + test + + + + + + + + ${artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + repackage + + + + + + + + diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java new file mode 100644 index 000000000..5f97979b8 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.member.api; diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/api/user/UserApiImpl.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/api/user/UserApiImpl.java new file mode 100644 index 000000000..656642e51 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/api/user/UserApiImpl.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.member.api.user; + +import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO; +import cn.iocoder.yudao.module.member.convert.user.UserConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; +import cn.iocoder.yudao.module.member.service.user.UserService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +/** + * 会员用户的 API 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class UserApiImpl implements UserApi { + + @Resource + private UserService userService; + + @Override + public UserRespDTO getUser(Long id) { + UserDO user = userService.getUser(id); + return UserConvert.INSTANCE.convert2(user); + } + +} diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/package-info.java new file mode 100644 index 000000000..652bbb6f1 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.member.controller.admin.address; diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/package-info.java new file mode 100644 index 000000000..23b3c23c4 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.member.controller.admin.user; diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/package-info.java new file mode 100644 index 000000000..c8c102186 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.member.controller.app.address; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.http b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http similarity index 91% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.http rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http index c9cadedcc..dbe82122a 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.http +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http @@ -1,6 +1,7 @@ ### 请求 /login 接口 => 成功 -POST {{userServerUrl}}/login +POST {{userApi}}/login Content-Type: application/json +tenant-id: {{userTenentId}} { "mobile": "15601691300", diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java similarity index 63% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java index 79e8a55a1..f52e96d1a 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java @@ -1,14 +1,13 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth; +package cn.iocoder.yudao.module.member.controller.app.auth; import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; -import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; -import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; -import com.alibaba.fastjson.JSON; +import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; +import cn.iocoder.yudao.module.member.service.auth.AuthService; +import cn.iocoder.yudao.module.member.service.sms.SysSmsCodeService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; @@ -18,7 +17,6 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -26,15 +24,16 @@ import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getCli import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; -@Api(tags = "认证") +@Api(tags = "APP 端 - 认证") @RestController @RequestMapping("/") @Validated @Slf4j -public class SysAuthController { +public class AppAuthController { @Resource - private SysAuthService authService; + private AuthService authService; + @Resource private SysSmsCodeService smsCodeService; @Resource @@ -42,35 +41,31 @@ public class SysAuthController { @PostMapping("/login") @ApiOperation("使用手机 + 密码登录") - public CommonResult login(@RequestBody @Valid SysAuthLoginReqVO reqVO) { + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult login(@RequestBody @Valid AppAuthLoginReqVO reqVO) { String token = authService.login(reqVO, getClientIP(), getUserAgent()); // 返回结果 - return success(SysAuthLoginRespVO.builder().token(token).build()); + return success(AppAuthLoginRespVO.builder().token(token).build()); } @PostMapping("/sms-login") @ApiOperation("使用手机 + 验证码登录") - public CommonResult smsLogin(@RequestBody @Valid SysAuthSmsLoginReqVO reqVO) { + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) { String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent()); // 返回结果 - return success(SysAuthLoginRespVO.builder().token(token).build()); + return success(AppAuthLoginRespVO.builder().token(token).build()); } @PostMapping("/send-sms-code") - @ApiOperation(value = "发送手机验证码",notes = "不检测该手机号是否已被注册") - public CommonResult sendSmsCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) { + @ApiOperation(value = "发送手机验证码") + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult sendSmsCode(@RequestBody @Valid AppAuthSendSmsReqVO reqVO) { smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP()); return success(true); } - @PostMapping("/send-sms-new-code") - @ApiOperation(value = "发送手机验证码",notes = "检测该手机号是否已被注册,用于修改手机时使用") - public CommonResult sendSmsNewCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) { - smsCodeService.sendSmsNewCode(reqVO); - return success(true); - } - - @GetMapping("/send-sms-code-login") + @GetMapping("/send-sms-code-login") // TODO 芋艿:post 比较合理 @ApiOperation(value = "向已登录用户发送验证码",notes = "修改手机时验证原手机号使用") public CommonResult sendSmsCodeLogin() { smsCodeService.sendSmsCodeLogin(getLoginUserId()); @@ -80,7 +75,8 @@ public class SysAuthController { @PostMapping("/reset-password") @ApiOperation(value = "重置密码", notes = "用户忘记密码时使用") @PreAuthenticated - public CommonResult resetPassword(@RequestBody @Valid MbrAuthResetPasswordReqVO reqVO) { + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult resetPassword(@RequestBody @Valid AppAuthResetPasswordReqVO reqVO) { authService.resetPassword(reqVO); return success(true); } @@ -88,20 +84,11 @@ public class SysAuthController { @PostMapping("/update-password") @ApiOperation(value = "修改用户密码",notes = "用户修改密码时使用") @PreAuthenticated - public CommonResult updatePassword(@RequestBody @Valid MbrAuthUpdatePasswordReqVO reqVO) { + public CommonResult updatePassword(@RequestBody @Valid AppAuthUpdatePasswordReqVO reqVO) { authService.updatePassword(getLoginUserId(), reqVO); return success(true); } - @PostMapping("/check-sms-code") - @ApiOperation(value = "校验验证码是否正确") - @PreAuthenticated - public CommonResult checkSmsCode(@RequestBody @Valid SysAuthSmsLoginReqVO reqVO) { - // TODO @宋天:check 的时候,不应该使用 useSmsCode 哈,这样验证码就直接被使用了。另外,check 开头的方法,更多是校验的逻辑,不会有 update 数据的动作。这点,在方法命名上,也是要注意的 - smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.CHECK_CODE_BY_SMS.getScene(),reqVO.getCode(),getClientIP()); - return success(true); - } - // ========== 社交登录相关 ========== @GetMapping("/social-auth-redirect") @@ -115,32 +102,33 @@ public class SysAuthController { return CommonResult.success(socialService.getAuthorizeUrl(type, redirectUri)); } - @PostMapping("/social-login") @ApiOperation("社交登录,使用 code 授权码") - public CommonResult socialLogin(@RequestBody @Valid MbrAuthSocialLoginReqVO reqVO) { + public CommonResult socialLogin(@RequestBody @Valid AppAuthSocialLoginReqVO reqVO) { String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent()); - return success(SysAuthLoginRespVO.builder().token(token).build()); + return success(AppAuthLoginRespVO.builder().token(token).build()); } - @PostMapping("/social-login2") @ApiOperation("社交登录,使用 手机号 + 手机验证码") - public CommonResult socialLogin2(@RequestBody @Valid MbrAuthSocialLogin2ReqVO reqVO) { + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult socialLogin2(@RequestBody @Valid AppAuthSocialLogin2ReqVO reqVO) { String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent()); - return success(SysAuthLoginRespVO.builder().token(token).build()); + return success(AppAuthLoginRespVO.builder().token(token).build()); } @PostMapping("/social-bind") @ApiOperation("社交绑定,使用 code 授权码") - public CommonResult socialBind(@RequestBody @Valid MbrAuthSocialBindReqVO reqVO) { + @PreAuthenticated + public CommonResult socialBind(@RequestBody @Valid AppAuthSocialBindReqVO reqVO) { authService.socialBind(getLoginUserId(), reqVO); return CommonResult.success(true); } @DeleteMapping("/social-unbind") @ApiOperation("取消社交绑定") - public CommonResult socialUnbind(@RequestBody MbrAuthSocialUnbindReqVO reqVO) { + @PreAuthenticated + public CommonResult socialUnbind(@RequestBody AppAuthSocialUnbindReqVO reqVO) { socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId(), UserTypeEnum.MEMBER); return CommonResult.success(true); } diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthCheckCodeReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthCheckCodeReqVO.java new file mode 100644 index 000000000..6d33b18ed --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthCheckCodeReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +// TODO 芋艿:code review 相关逻辑 +@ApiModel("APP 端 - 校验验证码 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppAuthCheckCodeReqVO { + + @ApiModelProperty(value = "手机号", example = "15601691234") + @NotBlank(message = "手机号不能为空") + @Mobile + private String mobile; + + @ApiModelProperty(value = "手机验证码", required = true, example = "1024") + @NotBlank(message = "手机验证码不能为空") + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") + private String code; + + @ApiModelProperty(value = "发送场景", example = "1", notes = "对应 MbrSmsSceneEnum 枚举") + @NotNull(message = "发送场景不能为空") + @InEnum(SysSmsSceneEnum.class) + private Integer scene; + +} diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthLoginReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java similarity index 84% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthLoginReqVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java index 1d51f8001..25875fd66 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthLoginReqVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +package cn.iocoder.yudao.module.member.controller.app.auth.vo; import cn.iocoder.yudao.framework.common.validation.Mobile; import io.swagger.annotations.ApiModel; @@ -11,12 +11,12 @@ import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotEmpty; -@ApiModel("手机 + 密码登录 Request VO") +@ApiModel("APP 端 - 手机 + 密码登录 Request VO") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class SysAuthLoginReqVO { +public class AppAuthLoginReqVO { @ApiModelProperty(value = "手机号", required = true, example = "15601691300") @NotEmpty(message = "手机号不能为空") diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthLoginRespVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java similarity index 70% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthLoginRespVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java index 95c69c754..935b3ec52 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthLoginRespVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +package cn.iocoder.yudao.module.member.controller.app.auth.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -7,12 +7,12 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -@ApiModel("手机密码登录 Response VO") +@ApiModel("APP 端 - 手机密码登录 Response VO") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class SysAuthLoginRespVO { +public class AppAuthLoginRespVO { @ApiModelProperty(value = "token", required = true, example = "yudaoyuanma") private String token; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthResetPasswordReqVO.java similarity index 66% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthResetPasswordReqVO.java index 3b3556fdb..9f48ee624 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthResetPasswordReqVO.java @@ -1,5 +1,6 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +package cn.iocoder.yudao.module.member.controller.app.auth.vo; +import cn.iocoder.yudao.framework.common.validation.Mobile; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -8,15 +9,17 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; -@ApiModel("重置密码 Request VO") +// TODO 芋艿:code review 相关逻辑 +@ApiModel("APP 端 - 重置密码 Request VO") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class MbrAuthResetPasswordReqVO { +public class AppAuthResetPasswordReqVO { @ApiModelProperty(value = "新密码", required = true, example = "buzhidao") @NotEmpty(message = "新密码不能为空") @@ -29,4 +32,9 @@ public class MbrAuthResetPasswordReqVO { @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") private String code; + @ApiModelProperty(value = "手机号",required = true,example = "15878962356") + @NotBlank(message = "手机号不能为空") + @Mobile + private String mobile; + } diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSendSmsReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSendSmsReqVO.java similarity index 74% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSendSmsReqVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSendSmsReqVO.java index d8c6c983f..9e53351fe 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSendSmsReqVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSendSmsReqVO.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +package cn.iocoder.yudao.module.member.controller.app.auth.vo; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.Mobile; -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; +import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -10,10 +10,10 @@ import lombok.experimental.Accessors; import javax.validation.constraints.NotNull; -@ApiModel("发送手机验证码 Response VO") +@ApiModel("APP 端 - 发送手机验证码 Response VO") @Data @Accessors(chain = true) -public class SysAuthSendSmsReqVO { +public class AppAuthSendSmsReqVO { @ApiModelProperty(value = "手机号", example = "15601691234") @Mobile diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSmsLoginReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java similarity index 85% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSmsLoginReqVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java index a7b36bb54..452e7e825 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSmsLoginReqVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +package cn.iocoder.yudao.module.member.controller.app.auth.vo; import cn.iocoder.yudao.framework.common.validation.Mobile; import io.swagger.annotations.ApiModel; @@ -12,12 +12,12 @@ import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; -@ApiModel("手机 + 验证码登录 Request VO") +@ApiModel("APP 端 - 手机 + 验证码登录 Request VO") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class SysAuthSmsLoginReqVO { +public class AppAuthSmsLoginReqVO { @ApiModelProperty(value = "手机号", required = true, example = "15601691300") @NotEmpty(message = "手机号不能为空") diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLoginReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialBindReqVO.java similarity index 86% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLoginReqVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialBindReqVO.java index cb430231a..d595e7428 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLoginReqVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialBindReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +package cn.iocoder.yudao.module.member.controller.app.auth.vo; import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; @@ -12,12 +12,12 @@ import lombok.NoArgsConstructor; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -@ApiModel("社交登录 Request VO,使用 code 授权码") +@ApiModel("APP 端 - 社交绑定 Request VO,使用 code 授权码") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class MbrAuthSocialLoginReqVO { +public class AppAuthSocialBindReqVO { @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") @InEnum(SysSocialTypeEnum.class) diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLogin2ReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLogin2ReqVO.java similarity index 90% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLogin2ReqVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLogin2ReqVO.java index 1da763ed3..cc832e371 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLogin2ReqVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLogin2ReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +package cn.iocoder.yudao.module.member.controller.app.auth.vo; import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; @@ -14,12 +14,12 @@ import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; -@ApiModel("社交登录 Request VO,使用 code 授权码 + 账号密码") +@ApiModel("APP 端 - 社交登录 Request VO,使用 code 授权码 + 账号密码") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class MbrAuthSocialLogin2ReqVO { +public class AppAuthSocialLogin2ReqVO { @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") @InEnum(SysSocialTypeEnum.class) diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialBindReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLoginReqVO.java similarity index 86% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialBindReqVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLoginReqVO.java index 767446b5f..3d1562522 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialBindReqVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLoginReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +package cn.iocoder.yudao.module.member.controller.app.auth.vo; import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; @@ -12,12 +12,12 @@ import lombok.NoArgsConstructor; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -@ApiModel("社交绑定 Request VO,使用 code 授权码") +@ApiModel("APP 端 - 社交登录 Request VO,使用 code 授权码") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class MbrAuthSocialBindReqVO { +public class AppAuthSocialLoginReqVO { @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") @InEnum(SysSocialTypeEnum.class) diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialUnbindReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialUnbindReqVO.java similarity index 84% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialUnbindReqVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialUnbindReqVO.java index 75e31701d..9225080f7 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialUnbindReqVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialUnbindReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +package cn.iocoder.yudao.module.member.controller.app.auth.vo; import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; @@ -12,12 +12,12 @@ import lombok.NoArgsConstructor; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -@ApiModel("取消社交绑定 Request VO,使用 code 授权码") +@ApiModel("APP 端 - 取消社交绑定 Request VO,使用 code 授权码") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class MbrAuthSocialUnbindReqVO { +public class AppAuthSocialUnbindReqVO { @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") @InEnum(SysSocialTypeEnum.class) diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthUpdatePasswordReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthUpdatePasswordReqVO.java similarity index 81% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthUpdatePasswordReqVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthUpdatePasswordReqVO.java index b5cc0c785..4bbecbc31 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthUpdatePasswordReqVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthUpdatePasswordReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +package cn.iocoder.yudao.module.member.controller.app.auth.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -11,12 +11,13 @@ import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; -@ApiModel("修改密码 Request VO") +// TODO 芋艿:code review 相关逻辑 +@ApiModel("APP 端 - 修改密码 Request VO") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class MbrAuthUpdatePasswordReqVO { +public class AppAuthUpdatePasswordReqVO { @ApiModelProperty(value = "用户旧密码", required = true, example = "123456") @NotBlank(message = "旧密码不能为空") diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/package-info.java new file mode 100644 index 000000000..7f013eaa9 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.member.controller.app; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.http b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.http similarity index 100% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.http rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.http diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.java new file mode 100644 index 000000000..cf5b5348c --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.member.controller.app.user; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO; +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO; +import cn.iocoder.yudao.module.member.convert.user.UserConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; +import cn.iocoder.yudao.module.member.service.user.UserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.io.IOException; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.module.member.enums.MemberErrorCodeConstants.FILE_IS_EMPTY; + +@Api(tags = "APP 端 - 用户个人中心") +@RestController +@RequestMapping("/member/user") +@Validated +@Slf4j +public class AppUserController { + + @Resource + private UserService userService; + + @PutMapping("/update-nickname") + @ApiOperation("修改用户昵称") + @PreAuthenticated + public CommonResult updateUserNickname(@RequestParam("nickname") String nickname) { + userService.updateUserNickname(getLoginUserId(), nickname); + return success(true); + } + + @PutMapping("/update-avatar") + @ApiOperation("修改用户头像") + @PreAuthenticated + public CommonResult updateUserAvatar(@RequestParam("avatarFile") MultipartFile file) throws IOException { + if (file.isEmpty()) { + throw exception(FILE_IS_EMPTY); + } + String avatar = userService.updateUserAvatar(getLoginUserId(), file.getInputStream()); + return success(avatar); + } + + @GetMapping("/get") + @ApiOperation("获得基本信息") + @PreAuthenticated + public CommonResult getUserInfo() { + UserDO user = userService.getUser(getLoginUserId()); + return success(UserConvert.INSTANCE.convert(user)); + } + + @PostMapping("/update-mobile") + @ApiOperation(value = "修改用户手机") + @PreAuthenticated + public CommonResult updateMobile(@RequestBody @Valid AppUserUpdateMobileReqVO reqVO) { + userService.updateUserMobile(getLoginUserId(), reqVO); + return success(true); + } + +} + diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserInfoRespVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserInfoRespVO.java similarity index 76% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserInfoRespVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserInfoRespVO.java index 697c4085d..f8d9d20d1 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserInfoRespVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserInfoRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.member.controller.user.vo; +package cn.iocoder.yudao.module.member.controller.app.user.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -6,11 +6,11 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -@ApiModel("用户个人信息 Response VO") +@ApiModel("APP 端 - 用户个人信息 Response VO") @Data @NoArgsConstructor @AllArgsConstructor -public class MbrUserInfoRespVO { +public class AppUserInfoRespVO { @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿") private String nickname; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserUpdateMobileReqVO.java similarity index 53% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserUpdateMobileReqVO.java index df1980b89..ded94ef1a 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserUpdateMobileReqVO.java @@ -1,5 +1,6 @@ -package cn.iocoder.yudao.userserver.modules.member.controller.user.vo; +package cn.iocoder.yudao.module.member.controller.app.user.vo; +import cn.iocoder.yudao.framework.common.validation.Mobile; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -12,12 +13,12 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; -@ApiModel("修改手机 Request VO") +@ApiModel("APP 端 - 修改手机 Request VO") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class MbrUserUpdateMobileReqVO { +public class AppUserUpdateMobileReqVO { @ApiModelProperty(value = "手机验证码", required = true, example = "1024") @NotEmpty(message = "手机验证码不能为空") @@ -27,9 +28,22 @@ public class MbrUserUpdateMobileReqVO { @ApiModelProperty(value = "手机号",required = true,example = "15823654487") @NotBlank(message = "手机号不能为空") - // TODO @宋天:手机校验,直接使用 @Mobile 哈 @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位") - @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式错误") + @Mobile private String mobile; + @ApiModelProperty(value = "原手机验证码", required = true, example = "1024") + @NotEmpty(message = "原手机验证码不能为空") + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") + private String oldCode; + + // TODO @芋艿:oldMobile 应该不用传递 + + @ApiModelProperty(value = "原手机号",required = true,example = "15823654487") + @NotBlank(message = "手机号不能为空") + @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位") + @Mobile + private String oldMobile; + } diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/package-info.java new file mode 100644 index 000000000..ea9687d81 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 RESTful API 给前端: + * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目 + * 2. app 包:提供给用户 APP yudao-ui-app 前端项目 + */ +package cn.iocoder.yudao.module.member.controller; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/auth/SysAuthConvert.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java similarity index 56% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/auth/SysAuthConvert.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java index f2af81257..419a70e0b 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/auth/SysAuthConvert.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java @@ -1,21 +1,21 @@ -package cn.iocoder.yudao.userserver.modules.system.convert.auth; +package cn.iocoder.yudao.module.member.convert.auth; -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @Mapper -public interface SysAuthConvert { +public interface AuthConvert { - SysAuthConvert INSTANCE = Mappers.getMapper(SysAuthConvert.class); + AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); @Mapping(source = "mobile", target = "username") - LoginUser convert0(MbrUserDO bean); + LoginUser convert0(UserDO bean); - default LoginUser convert(MbrUserDO bean) { + default LoginUser convert(UserDO bean) { // 目的,为了设置 UserTypeEnum.MEMBER.getValue() return convert0(bean).setUserType(UserTypeEnum.MEMBER.getValue()); } diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/convert/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/package-info.java similarity index 56% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/convert/package-info.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/package-info.java index 5877b74c1..6523a6656 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/convert/package-info.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/package-info.java @@ -3,4 +3,4 @@ * * 目前使用 MapStruct 框架 */ -package cn.iocoder.yudao.userserver.modules.member.convert; +package cn.iocoder.yudao.module.member.convert; diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java new file mode 100644 index 000000000..a490edfaf --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.member.convert.user; + +import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO; +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface UserConvert { + + UserConvert INSTANCE = Mappers.getMapper(UserConvert.class); + + AppUserInfoRespVO convert(UserDO bean); + + UserRespDTO convert2(UserDO bean); +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md similarity index 100% rename from yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/sms/SysSmsCodeDO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/sms/SysSmsCodeDO.java similarity index 90% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/sms/SysSmsCodeDO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/sms/SysSmsCodeDO.java index 8cce25f42..eb51770ea 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/sms/SysSmsCodeDO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/sms/SysSmsCodeDO.java @@ -1,9 +1,8 @@ -package cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms; +package cn.iocoder.yudao.module.member.dal.dataobject.sms; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; -import lombok.experimental.Accessors; import java.util.Date; diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/dal/dataobject/user/MbrUserDO.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/UserDO.java similarity index 90% rename from yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/dal/dataobject/user/MbrUserDO.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/UserDO.java index 02efd598b..f45168044 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/dal/dataobject/user/MbrUserDO.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/UserDO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user; +package cn.iocoder.yudao.module.member.dal.dataobject.user; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; @@ -10,7 +10,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import java.util.Date; /** - * 会员中心的用户 DO + * 会员用户 DO * * uk_mobile 索引:基于 {@link #mobile} 字段 * @@ -22,7 +22,7 @@ import java.util.Date; @Builder @NoArgsConstructor @AllArgsConstructor -public class MbrUserDO extends TenantBaseDO { +public class UserDO extends TenantBaseDO { /** * 用户ID diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/sms/SysSmsCodeMapper.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/sms/SysSmsCodeMapper.java similarity index 66% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/sms/SysSmsCodeMapper.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/sms/SysSmsCodeMapper.java index 6ef96ca1f..ccdbcf6ce 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/sms/SysSmsCodeMapper.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/sms/SysSmsCodeMapper.java @@ -1,10 +1,11 @@ -package cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms; +package cn.iocoder.yudao.module.member.dal.mysql.sms; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; -import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; +import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO; import org.apache.ibatis.annotations.Mapper; +// TODO @芋艿:拿到 system 模块下 @Mapper public interface SysSmsCodeMapper extends BaseMapperX { @@ -13,14 +14,15 @@ public interface SysSmsCodeMapper extends BaseMapperX { * * @param mobile 手机号 * @param scene 发送场景,选填 + * @param code 验证码 选填 * @return 手机验证码 */ - default SysSmsCodeDO selectLastByMobile(String mobile, Integer scene) { + default SysSmsCodeDO selectLastByMobile(String mobile,String code,Integer scene) { return selectOne(new QueryWrapperX() .eq("mobile", mobile) .eqIfPresent("scene", scene) + .eqIfPresent("code", code) .orderByDesc("id") .last("LIMIT 1")); } - } diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/UserMapper.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/UserMapper.java new file mode 100644 index 000000000..d962811e6 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/UserMapper.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.member.dal.mysql.user; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 会员 User Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface UserMapper extends BaseMapperX { + + default UserDO selectByMobile(String mobile) { + return selectOne(UserDO::getMobile, mobile); + } + +} diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/package-info.java new file mode 100644 index 000000000..5b05c46f1 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/package-info.java @@ -0,0 +1,9 @@ +/** + * DAL = Data Access Layer 数据访问层 + * 1. data object:数据对象 + * 2. redis:Redis 的 CRUD 操作 + * 3. mysql:MySQL 的 CRUD 操作 + * + * 其中,MySQL 的表以 mbr_ 作为前缀 + */ +package cn.iocoder.yudao.module.member.dal; diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/redis/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/redis/package-info.java new file mode 100644 index 000000000..8dfa9fb20 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/redis/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位,后续有类后,可以删除,避免 package 无法提交到 Git 上 + */ +package cn.iocoder.yudao.module.member.dal.redis; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/enums/MbrErrorCodeConstants.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/MemberErrorCodeConstants.java similarity index 80% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/enums/MbrErrorCodeConstants.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/MemberErrorCodeConstants.java index 3794eb09d..4ee545b1b 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/enums/MbrErrorCodeConstants.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/MemberErrorCodeConstants.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.member.enums; +package cn.iocoder.yudao.module.member.enums; import cn.iocoder.yudao.framework.common.exception.ErrorCode; @@ -7,11 +7,12 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; * * member 系统,使用 1-004-000-000 段 */ -public interface MbrErrorCodeConstants { +public interface MemberErrorCodeConstants { // ==========用户相关 1004001000============ ErrorCode USER_NOT_EXISTS = new ErrorCode(1004001000, "用户不存在"); // ==========文件相关 1004002000 =========== ErrorCode FILE_IS_EMPTY = new ErrorCode(1004002000, "文件为空"); + } diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/SysErrorCodeConstants.java similarity index 92% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/SysErrorCodeConstants.java index e7c104afb..b1b23a352 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/SysErrorCodeConstants.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.enums; +package cn.iocoder.yudao.module.member.enums; import cn.iocoder.yudao.framework.common.exception.ErrorCode; @@ -24,9 +24,9 @@ public interface SysErrorCodeConstants { ErrorCode USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1005001004, "超过每日短信发送数量"); ErrorCode USER_SMS_CODE_SEND_TOO_FAST = new ErrorCode(1005001005, "短信发送过于频率"); ErrorCode USER_SMS_CODE_IS_EXISTS = new ErrorCode(1005001006, "手机号已被使用"); + ErrorCode USER_SMS_CODE_IS_UNUSED = new ErrorCode(1005001006, "验证码未被使用"); // ========== 用户模块 1005002000 ========== ErrorCode USER_NOT_EXISTS = new ErrorCode(1005002001, "用户不存在"); - ErrorCode USER_CODE_FAILED = new ErrorCode(1005002002, "验证码不匹配"); ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1005002003, "密码校验失败"); } diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/sms/SysSmsSceneEnum.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/sms/SysSmsSceneEnum.java new file mode 100644 index 000000000..a47190117 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/sms/SysSmsSceneEnum.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.member.enums.sms; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 用户短信验证码发送场景的枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum SysSmsSceneEnum implements IntArrayValuable { + + LOGIN_BY_SMS(1,SysSmsTemplateCodeConstants.USER_SMS_LOGIN, "手机号登陆"), + CHANGE_MOBILE_BY_SMS(2,SysSmsTemplateCodeConstants.USER_SMS_UPDATE_MOBILE, "更换手机号"), + FORGET_MOBILE_BY_SMS(3,SysSmsTemplateCodeConstants.USER_SMS_RESET_PASSWORD, "忘记密码"), + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSmsSceneEnum::getScene).toArray(); + + /** + * 验证那场景编号 + */ + private final Integer scene; + + /** + * 模版编码 + */ + private final String code; + + /** + * 描述 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + + public static String getCodeByScene(Integer scene){ + for (SysSmsSceneEnum value : values()) { + if (value.getScene().equals(scene)){ + return value.getCode(); + } + } + return null; + } + +} diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/sms/SysSmsTemplateCodeConstants.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/sms/SysSmsTemplateCodeConstants.java new file mode 100644 index 000000000..3ad8439cd --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/sms/SysSmsTemplateCodeConstants.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.member.enums.sms; + +/** + * yudao-user-server 使用到的短信模板的 Code 编码的枚举 + * + * @author 芋道源码 + */ +public interface SysSmsTemplateCodeConstants { + + /** + * 前台用户短信登录 + */ + String USER_SMS_LOGIN = "user-sms-login"; + + /** + * 用户忘记密码 + */ + String USER_SMS_RESET_PASSWORD = "user-sms-reset-password"; + + /** + * 用户更新手机号 + */ + String USER_SMS_UPDATE_MOBILE = "user-sms-update-mobile"; + +} diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/package-info.java new file mode 100644 index 000000000..7342570b1 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/package-info.java @@ -0,0 +1,6 @@ +/** + * 属于 yudao-module-member-impl 的封装 + * + * @author 芋道源码 + */ +package cn.iocoder.yudao.module.member.framework; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/sms/SmsCodeConfiguration.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/sms/SmsCodeConfiguration.java similarity index 79% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/sms/SmsCodeConfiguration.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/sms/SmsCodeConfiguration.java index a4510ea47..fa741880b 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/sms/SmsCodeConfiguration.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/sms/SmsCodeConfiguration.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.framework.sms; +package cn.iocoder.yudao.module.member.framework.sms; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/sms/SmsCodeProperties.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/sms/SmsCodeProperties.java similarity index 94% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/sms/SmsCodeProperties.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/sms/SmsCodeProperties.java index b600c28dc..542fe2d9e 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/sms/SmsCodeProperties.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/sms/SmsCodeProperties.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.modules.system.framework.sms; +package cn.iocoder.yudao.module.member.framework.sms; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/package-info.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/package-info.java new file mode 100644 index 000000000..dabac6ac3 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.member; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthService.java similarity index 62% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthService.java index a81bfdeeb..98b87c505 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthService.java @@ -1,18 +1,18 @@ -package cn.iocoder.yudao.userserver.modules.system.service.auth; +package cn.iocoder.yudao.module.member.service.auth; import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; +import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; import javax.validation.Valid; /** - * 用户前台的认证 Service 接口 + * 会员的认证 Service 接口 * * 提供用户的账号密码登录、token 的校验等认证相关的功能 * * @author 芋道源码 */ -public interface SysAuthService extends SecurityAuthFrameworkService { +public interface AuthService extends SecurityAuthFrameworkService { /** * 手机 + 密码登录 @@ -22,7 +22,7 @@ public interface SysAuthService extends SecurityAuthFrameworkService { * @param userAgent 用户 UA * @return 身份令牌,使用 JWT 方式 */ - String login(@Valid SysAuthLoginReqVO reqVO, String userIp, String userAgent); + String login(@Valid AppAuthLoginReqVO reqVO, String userIp, String userAgent); /** * 手机 + 验证码登陆 @@ -32,7 +32,7 @@ public interface SysAuthService extends SecurityAuthFrameworkService { * @param userAgent 用户 UA * @return 身份令牌,使用 JWT 方式 */ - String smsLogin(@Valid SysAuthSmsLoginReqVO reqVO, String userIp, String userAgent); + String smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent); /** @@ -43,7 +43,7 @@ public interface SysAuthService extends SecurityAuthFrameworkService { * @param userAgent 用户 UA * @return 身份令牌,使用 JWT 方式 */ - String socialLogin(@Valid MbrAuthSocialLoginReqVO reqVO, String userIp, String userAgent); + String socialLogin(@Valid AppAuthSocialLoginReqVO reqVO, String userIp, String userAgent); /** * 社交登录,使用 手机号 + 手机验证码 @@ -53,7 +53,7 @@ public interface SysAuthService extends SecurityAuthFrameworkService { * @param userAgent 用户 UA * @return 身份令牌,使用 JWT 方式 */ - String socialLogin2(@Valid MbrAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent); + String socialLogin2(@Valid AppAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent); /** * 社交绑定,使用 code 授权码 @@ -61,25 +61,19 @@ public interface SysAuthService extends SecurityAuthFrameworkService { * @param userId 用户编号 * @param reqVO 绑定信息 */ - void socialBind(Long userId, @Valid MbrAuthSocialBindReqVO reqVO); + void socialBind(Long userId, @Valid AppAuthSocialBindReqVO reqVO); /** * 修改用户密码 * @param userId 用户id * @param userReqVO 用户请求实体类 */ - void updatePassword(Long userId, @Valid MbrAuthUpdatePasswordReqVO userReqVO); + void updatePassword(Long userId, AppAuthUpdatePasswordReqVO userReqVO); /** * 忘记密码 * @param userReqVO 用户请求实体类 */ - void resetPassword(MbrAuthResetPasswordReqVO userReqVO); + void resetPassword(AppAuthResetPasswordReqVO userReqVO); - /** - * 检测手机与验证码是否匹配 - * @param phone 手机号 - * @param code 验证码 - */ - void checkIfMobileMatchCodeAndDeleteCode(String phone,String code); } diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthServiceImpl.java similarity index 70% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthServiceImpl.java index a0b1c09cf..6deabba12 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthServiceImpl.java @@ -1,8 +1,7 @@ -package cn.iocoder.yudao.userserver.modules.system.service.auth.impl; +package cn.iocoder.yudao.module.member.service.auth; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO; import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginLogTypeEnum; import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginResultEnum; @@ -15,18 +14,18 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.security.core.LoginUser; -import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; -import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; -import cn.iocoder.yudao.userserver.modules.system.convert.auth.SysAuthConvert; -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; -import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; -import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; +import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; +import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; +import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; +import cn.iocoder.yudao.module.member.dal.mysql.user.UserMapper; +import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum; +import cn.iocoder.yudao.module.member.service.sms.SysSmsCodeService; +import cn.iocoder.yudao.module.member.service.user.UserService; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import me.zhyd.oauth.model.AuthUser; import org.springframework.context.annotation.Lazy; -import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; @@ -40,31 +39,28 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; -import javax.validation.Valid; import java.util.List; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; -import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*; +import static cn.iocoder.yudao.module.member.enums.SysErrorCodeConstants.*; /** - * Auth Service 实现类 + * 会员的认证 Service 接口 * * @author 芋道源码 */ @Service @Slf4j -public class SysAuthServiceImpl implements SysAuthService { - - private static final UserTypeEnum USER_TYPE_ENUM = UserTypeEnum.MEMBER; +public class AuthServiceImpl implements AuthService { @Resource @Lazy // 延迟加载,因为存在相互依赖的问题 private AuthenticationManager authenticationManager; @Resource - private MbrUserService userService; + private UserService userService; @Resource private SysSmsCodeService smsCodeService; @Resource @@ -74,28 +70,24 @@ public class SysAuthServiceImpl implements SysAuthService { @Resource private SysSocialCoreService socialService; - @Resource - private StringRedisTemplate stringRedisTemplate; @Resource private PasswordEncoder passwordEncoder; @Resource - private MbrUserMapper userMapper; - - private static final UserTypeEnum userTypeEnum = UserTypeEnum.MEMBER; + private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException { // 获取 username 对应的 SysUserDO - MbrUserDO user = userService.getUserByMobile(mobile); + UserDO user = userService.getUserByMobile(mobile); if (user == null) { throw new UsernameNotFoundException(mobile); } // 创建 LoginUser 对象 - return SysAuthConvert.INSTANCE.convert(user); + return AuthConvert.INSTANCE.convert(user); } @Override - public String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent) { + public String login(AppAuthLoginReqVO reqVO, String userIp, String userAgent) { // 使用手机 + 密码,进行登录。 LoginUser loginUser = this.login0(reqVO.getMobile(), reqVO.getPassword()); @@ -105,80 +97,77 @@ public class SysAuthServiceImpl implements SysAuthService { @Override @Transactional - public String smsLogin(SysAuthSmsLoginReqVO reqVO, String userIp, String userAgent) { + public String smsLogin(AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent) { // 校验验证码 smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.LOGIN_BY_SMS.getScene(), reqVO.getCode(), userIp); // 获得获得注册用户 - MbrUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp); + UserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp); Assert.notNull(user, "获取用户失败,结果为空"); // 执行登陆 this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SMS, SysLoginResultEnum.SUCCESS); - LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user); + LoginUser loginUser = AuthConvert.INSTANCE.convert(user); // 缓存登录用户到 Redis 中,返回 sessionId 编号 return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); } @Override - public String socialLogin(MbrAuthSocialLoginReqVO reqVO, String userIp, String userAgent) { + public String socialLogin(AppAuthSocialLoginReqVO reqVO, String userIp, String userAgent) { // 使用 code 授权码,进行登录 AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); org.springframework.util.Assert.notNull(authUser, "授权用户不为空"); // 如果未绑定 SysSocialUserDO 用户,则无法自动登录,进行报错 String unionId = socialService.getAuthUserUnionId(authUser); - List socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, USER_TYPE_ENUM); + List socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, getUserType()); if (CollUtil.isEmpty(socialUsers)) { throw exception(AUTH_THIRD_LOGIN_NOT_BIND); } // 自动登录 - MbrUserDO user = userService.getUser(socialUsers.get(0).getUserId()); + UserDO user = userService.getUser(socialUsers.get(0).getUserId()); if (user == null) { throw exception(USER_NOT_EXISTS); } this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SOCIAL, SysLoginResultEnum.SUCCESS); // 创建 LoginUser 对象 - LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user); + LoginUser loginUser = AuthConvert.INSTANCE.convert(user); // 绑定社交用户(更新) - socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, USER_TYPE_ENUM); + socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, getUserType()); // 缓存登录用户到 Redis 中,返回 sessionId 编号 return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); } @Override - public String socialLogin2(MbrAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) { + public String socialLogin2(AppAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) { AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); org.springframework.util.Assert.notNull(authUser, "授权用户不为空"); // 使用手机号、手机验证码登录 - SysAuthSmsLoginReqVO loginReqVO = SysAuthSmsLoginReqVO - .builder() - .mobile(reqVO.getMobile()) - .code(reqVO.getSmsCode()) - .build(); + AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder() + .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build(); String sessionId = this.smsLogin(loginReqVO, userIp, userAgent); LoginUser loginUser = userSessionCoreService.getLoginUser(sessionId); // 绑定社交用户(新增) - socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, USER_TYPE_ENUM); + socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, getUserType()); return sessionId; } @Override - public void socialBind(Long userId, MbrAuthSocialBindReqVO reqVO) { + public void socialBind(Long userId, AppAuthSocialBindReqVO reqVO) { // 使用 code 授权码,进行登录 AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); org.springframework.util.Assert.notNull(authUser, "授权用户不为空"); // 绑定社交用户(新增) - socialService.bindSocialUser(userId, reqVO.getType(), authUser, USER_TYPE_ENUM); + socialService.bindSocialUser(userId, reqVO.getType(), authUser, getUserType()); } private LoginUser login0(String username, String password) { @@ -188,7 +177,8 @@ public class SysAuthServiceImpl implements SysAuthService { try { // 调用 Spring Security 的 AuthenticationManager#authenticate(...) 方法,使用账号密码进行认证 // 在其内部,会调用到 loadUserByUsername 方法,获取 User 信息 - authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); + authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken( + username, password, getUserType())); } catch (BadCredentialsException badCredentialsException) { this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS); throw exception(AUTH_LOGIN_BAD_CREDENTIALS); @@ -208,7 +198,7 @@ public class SysAuthServiceImpl implements SysAuthService { private void createLoginLog(String mobile, SysLoginLogTypeEnum logTypeEnum, SysLoginResultEnum loginResult) { // 获得用户 - MbrUserDO user = userService.getUserByMobile(mobile); + UserDO user = userService.getUserByMobile(mobile); // 插入登录日志 SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO(); reqDTO.setLogType(logTypeEnum.getType()); @@ -246,10 +236,11 @@ public class SysAuthServiceImpl implements SysAuthService { return; } - // 重新加载 MbrUserDO 信息 - MbrUserDO user = userService.getUser(loginUser.getId()); + // 重新加载 UserDO 信息 + UserDO user = userService.getUser(loginUser.getId()); if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) { - throw exception(AUTH_TOKEN_EXPIRED); // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面 + // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面 + throw exception(AUTH_TOKEN_EXPIRED); } // 刷新 LoginUser 缓存 @@ -258,8 +249,8 @@ public class SysAuthServiceImpl implements SysAuthService { @Override public LoginUser mockLogin(Long userId) { - // 获取用户编号对应的 MbrUserDO - MbrUserDO user = userService.getUser(userId); + // 获取用户编号对应的 UserDO + UserDO user = userService.getUser(userId); if (user == null) { throw new UsernameNotFoundException(String.valueOf(userId)); } @@ -268,7 +259,7 @@ public class SysAuthServiceImpl implements SysAuthService { this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_MOCK, SysLoginResultEnum.SUCCESS); // 创建 LoginUser 对象 - return SysAuthConvert.INSTANCE.convert(user); + return AuthConvert.INSTANCE.convert(user); } @Override @@ -285,46 +276,35 @@ public class SysAuthServiceImpl implements SysAuthService { } @Override - public void updatePassword(Long userId, @Valid MbrAuthUpdatePasswordReqVO reqVO) { + public UserTypeEnum getUserType() { + return UserTypeEnum.MEMBER; + } + + @Override + public void updatePassword(Long userId, AppAuthUpdatePasswordReqVO reqVO) { // 检验旧密码 - MbrUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword()); + UserDO userDO = checkOldPassword(userId, reqVO.getOldPassword()); // 更新用户密码 - // TODO @宋天:不要更新整个对象哈 - userDO.setPassword(passwordEncoder.encode(reqVO.getPassword())); - userMapper.updateById(userDO); + UserDO mbrUserDO = UserDO.builder().id(userDO.getId()) + .password(passwordEncoder.encode(reqVO.getPassword())).build(); + userMapper.updateById(mbrUserDO); } @Override - public void resetPassword(MbrAuthResetPasswordReqVO reqVO) { - // 根据验证码取出手机号,并查询用户 - String mobile = stringRedisTemplate.opsForValue().get(reqVO.getCode()); - MbrUserDO userDO = userMapper.selectByMobile(mobile); - if (userDO == null){ - throw exception(USER_NOT_EXISTS); - } - // TODO @芋艿 这一步没必要检验验证码与手机是否匹配,因为是根据验证码去redis中查找手机号,然后根据手机号查询用户 - // 也就是说 即便黑客以其他方式将验证码发送到自己手机上,最终还是会根据手机号查询用户然后进行重置密码的操作,不存在安全问题 + public void resetPassword(AppAuthResetPasswordReqVO reqVO) { + // 检验用户是否存在 + UserDO userDO = checkUserIfExists(reqVO.getMobile()); - // TODO @宋天:这块微信在讨论下哈~~~ - - // 校验验证码 - smsCodeService.useSmsCode(userDO.getMobile(), SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP()); + // 使用验证码 + smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(), reqVO.getCode(), + getClientIP()); // 更新密码 - userDO.setPassword(passwordEncoder.encode(reqVO.getPassword())); - userMapper.updateById(userDO); - } - - @Override - public void checkIfMobileMatchCodeAndDeleteCode(String phone, String code) { - // 检验用户手机与验证码是否匹配 - String mobile = stringRedisTemplate.opsForValue().get(code); - if (!phone.equals(mobile)){ - throw exception(USER_CODE_FAILED); - } - // 销毁redis中此验证码 - stringRedisTemplate.delete(code); + UserDO mbrUserDO = UserDO.builder().build(); + mbrUserDO.setId(userDO.getId()); + mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword())); + userMapper.updateById(mbrUserDO); } /** @@ -332,11 +312,11 @@ public class SysAuthServiceImpl implements SysAuthService { * * @param id 用户 id * @param oldPassword 旧密码 - * @return MbrUserDO 用户实体 + * @return MemberUserDO 用户实体 */ @VisibleForTesting - public MbrUserDO checkOldPassword(Long id, String oldPassword) { - MbrUserDO user = userMapper.selectById(id); + public UserDO checkOldPassword(Long id, String oldPassword) { + UserDO user = userMapper.selectById(id); if (user == null) { throw exception(USER_NOT_EXISTS); } @@ -347,12 +327,20 @@ public class SysAuthServiceImpl implements SysAuthService { return user; } + public UserDO checkUserIfExists(String mobile) { + UserDO user = userMapper.selectByMobile(mobile); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + return user; + } + private void createLogoutLog(Long userId, String username) { SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO(); reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType()); reqDTO.setTraceId(TracerUtils.getTraceId()); reqDTO.setUserId(userId); - reqDTO.setUserType(USER_TYPE_ENUM.getValue()); + reqDTO.setUserType(getUserType().getValue()); reqDTO.setUsername(username); reqDTO.setUserAgent(ServletUtils.getUserAgent()); reqDTO.setUserIp(getClientIP()); diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/SysSmsCodeService.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/sms/SysSmsCodeService.java similarity index 62% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/SysSmsCodeService.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/sms/SysSmsCodeService.java index 6e9c3c7b3..e06e9a246 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/SysSmsCodeService.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/sms/SysSmsCodeService.java @@ -1,9 +1,9 @@ -package cn.iocoder.yudao.userserver.modules.system.service.sms; +package cn.iocoder.yudao.module.member.service.sms; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.validation.Mobile; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO; -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; +import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO; +import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum; /** * 短信验证码 Service 接口 @@ -15,33 +15,37 @@ public interface SysSmsCodeService { /** * 创建短信验证码,并进行发送 * - * @param mobile 手机号 - * @param scene 发送场景 {@link SysSmsSceneEnum} + * @param mobile 手机号 + * @param scene 发送场景 {@link SysSmsSceneEnum} * @param createIp 发送 IP */ void sendSmsCode(@Mobile String mobile, Integer scene, String createIp); - /** - * 发送短信验证码,并检测手机号是否已被注册 - * @param reqVO 请求实体 - */ - void sendSmsNewCode(SysAuthSendSmsReqVO reqVO); - /** * 验证短信验证码,并进行使用 * 如果正确,则将验证码标记成已使用 * 如果错误,则抛出 {@link ServiceException} 异常 * * @param mobile 手机号 - * @param scene 发送场景 - * @param code 验证码 + * @param scene 发送场景 + * @param code 验证码 * @param usedIp 使用 IP */ void useSmsCode(@Mobile String mobile, Integer scene, String code, String usedIp); /** * 根据用户id发送验证码 + * * @param userId 用户id */ void sendSmsCodeLogin(Long userId); + + /** + * 检查验证码是否有效 + * @param mobile 手机 + * @param code 验证码 + * @param scene 使用场景 + * @return 验证码记录 + */ + SysSmsCodeDO checkCodeIsExpired(String mobile, String code, Integer scene); } diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/sms/SysSmsCodeServiceImpl.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/sms/SysSmsCodeServiceImpl.java new file mode 100644 index 000000000..41049656d --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/sms/SysSmsCodeServiceImpl.java @@ -0,0 +1,137 @@ +package cn.iocoder.yudao.module.member.service.sms; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService; +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; +import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; +import cn.iocoder.yudao.module.member.dal.mysql.sms.SysSmsCodeMapper; +import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum; +import cn.iocoder.yudao.module.member.framework.sms.SmsCodeProperties; +import cn.iocoder.yudao.module.member.service.user.UserService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Date; + +import static cn.hutool.core.util.RandomUtil.randomInt; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.module.member.enums.SysErrorCodeConstants.*; + +/** + * 短信验证码 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class SysSmsCodeServiceImpl implements SysSmsCodeService { + + @Resource + private SmsCodeProperties smsCodeProperties; + + @Resource + private SysSmsCodeMapper smsCodeMapper; + + @Resource + private UserService userService; + + @Resource + private SysSmsCoreService smsCoreService; + + @Override + public void sendSmsCode(String mobile, Integer scene, String createIp) { + // 创建验证码 + String code = this.createSmsCode(mobile, scene, createIp); + + // 获取发送模板 + String codeTemplate = SysSmsSceneEnum.getCodeByScene(scene); + + // 如果是更换手机号发送验证码,则需要检测手机号是否被注册 + if (SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene().equals(scene)){ + this.checkMobileIsRegister(mobile,scene); + } + + // 发送验证码 + smsCoreService.sendSingleSmsToMember(mobile, null, codeTemplate, + MapUtil.of("code", code)); + } + + public void checkMobileIsRegister(String mobile, Integer scene) { + // 检测手机号是否已被使用 + UserDO user = userService.getUserByMobile(mobile); + if (user != null) { + throw ServiceExceptionUtil.exception(USER_SMS_CODE_IS_EXISTS); + } + + // 发送短信 + this.sendSmsCode(mobile,scene,getClientIP()); + } + + private String createSmsCode(String mobile, Integer scene, String ip) { + // 校验是否可以发送验证码,不用筛选场景 + SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,null); + if (lastSmsCode != null) { + if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。 + throw ServiceExceptionUtil.exception(USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); + } + if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() + < smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁 + throw ServiceExceptionUtil.exception(USER_SMS_CODE_SEND_TOO_FAST); + } + // TODO 芋艿:提升,每个 IP 每天可发送数量 + // TODO 芋艿:提升,每个 IP 每小时可发送数量 + } + + // 创建验证码记录 + String code = String.valueOf(randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1)); + SysSmsCodeDO newSmsCode = SysSmsCodeDO.builder().mobile(mobile).code(code) + .scene(scene).todayIndex(lastSmsCode != null ? lastSmsCode.getTodayIndex() + 1 : 1) + .createIp(ip).used(false).build(); + smsCodeMapper.insert(newSmsCode); + return code; + } + + @Override + public void useSmsCode(String mobile, Integer scene, String code, String usedIp) { + // 检测验证码是否有效 + SysSmsCodeDO lastSmsCode = this.checkCodeIsExpired(mobile, code, scene); + + // 判断验证码是否已被使用 + if (Boolean.TRUE.equals(lastSmsCode.getUsed())) { + throw ServiceExceptionUtil.exception(USER_SMS_CODE_USED); + } + + // 使用验证码 + smsCodeMapper.updateById(SysSmsCodeDO.builder().id(lastSmsCode.getId()) + .used(true).usedTime(new Date()).usedIp(usedIp).build()); + } + + @Override + public void sendSmsCodeLogin(Long userId) { + UserDO user = userService.getUser(userId); + if (user == null){ + throw ServiceExceptionUtil.exception(USER_NOT_EXISTS); + } + // 发送验证码 + this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP()); + } + + @Override + public SysSmsCodeDO checkCodeIsExpired(String mobile, String code, Integer scene) { + // 校验验证码 + SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile,code,scene); + + // 若验证码不存在,抛出异常 + if (lastSmsCode == null) { + throw ServiceExceptionUtil.exception(USER_SMS_CODE_NOT_FOUND); + } + if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() + >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期 + throw ServiceExceptionUtil.exception(USER_SMS_CODE_EXPIRED); + } + return lastSmsCode; + } + +} diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/UserService.java similarity index 54% rename from yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java rename to yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/UserService.java index e33978bfe..454e19dc6 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/UserService.java @@ -1,18 +1,18 @@ -package cn.iocoder.yudao.userserver.modules.member.service.user; +package cn.iocoder.yudao.module.member.service.user; -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; -import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO; import cn.iocoder.yudao.framework.common.validation.Mobile; -import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO; +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; import java.io.InputStream; /** - * 前台用户 Service 接口 + * 会员用户 Service 接口 * * @author 芋道源码 */ -public interface MbrUserService { +public interface UserService { /** * 通过手机查询用户 @@ -20,7 +20,7 @@ public interface MbrUserService { * @param mobile 手机 * @return 用户对象 */ - MbrUserDO getUserByMobile(String mobile); + UserDO getUserByMobile(String mobile); /** * 基于手机号创建用户。 @@ -30,7 +30,7 @@ public interface MbrUserService { * @param registerIp 注册 IP * @return 用户对象 */ - MbrUserDO createUserIfAbsent(@Mobile String mobile, String registerIp); + UserDO createUserIfAbsent(@Mobile String mobile, String registerIp); /** * 更新用户的最后登陆信息 @@ -46,14 +46,14 @@ public interface MbrUserService { * @param id 用户ID * @return 用户对象信息 */ - MbrUserDO getUser(Long id); + UserDO getUser(Long id); /** * 修改用户昵称 * @param userId 用户id * @param nickname 用户新昵称 */ - void updateNickname(Long userId, String nickname); + void updateUserNickname(Long userId, String nickname); /** * 修改用户头像 @@ -61,21 +61,13 @@ public interface MbrUserService { * @param inputStream 头像文件 * @return 头像url */ - String updateAvatar(Long userId, InputStream inputStream); - - /** - * 根据用户id,获取用户头像与昵称 - * - * @param userId 用户id - * @return 用户响应实体类 - */ - MbrUserInfoRespVO getUserInfo(Long userId); + String updateUserAvatar(Long userId, InputStream inputStream); /** * 修改手机 * @param userId 用户id * @param reqVO 请求实体 */ - void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO); + void updateUserMobile(Long userId, AppUserUpdateMobileReqVO reqVO); } diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/UserServiceImpl.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/UserServiceImpl.java new file mode 100644 index 000000000..a899b79e6 --- /dev/null +++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/UserServiceImpl.java @@ -0,0 +1,147 @@ +package cn.iocoder.yudao.module.member.service.user; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; +import cn.iocoder.yudao.module.member.dal.mysql.user.UserMapper; +import cn.iocoder.yudao.module.member.enums.SysErrorCodeConstants; +import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum; +import cn.iocoder.yudao.module.member.service.sms.SysSmsCodeService; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.io.InputStream; +import java.util.Date; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.module.member.enums.MemberErrorCodeConstants.USER_NOT_EXISTS; + +/** + * 会员 User Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Valid +@Slf4j +public class UserServiceImpl implements UserService { + + @Resource + private UserMapper memberUserMapper; + + @Resource + private InfFileCoreService fileCoreService; + @Resource + private SysSmsCodeService smsCodeService; + + @Resource + private PasswordEncoder passwordEncoder; + + @Override + public UserDO getUserByMobile(String mobile) { + return memberUserMapper.selectByMobile(mobile); + } + + @Override + public UserDO createUserIfAbsent(String mobile, String registerIp) { + // 用户已经存在 + UserDO user = memberUserMapper.selectByMobile(mobile); + if (user != null) { + return user; + } + // 用户不存在,则进行创建 + return this.createUser(mobile, registerIp); + } + + private UserDO createUser(String mobile, String registerIp) { + // 生成密码 + String password = IdUtil.fastSimpleUUID(); + // 插入用户 + UserDO user = new UserDO(); + user.setMobile(mobile); + user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 + user.setPassword(passwordEncoder.encode(password)); // 加密密码 + user.setRegisterIp(registerIp); + memberUserMapper.insert(user); + return user; + } + + @Override + public void updateUserLogin(Long id, String loginIp) { + memberUserMapper.updateById(new UserDO().setId(id) + .setLoginIp(loginIp).setLoginDate(new Date())); + } + + @Override + public UserDO getUser(Long id) { + return memberUserMapper.selectById(id); + } + + @Override + public void updateUserNickname(Long userId, String nickname) { + UserDO user = this.checkUserExists(userId); + // 仅当新昵称不等于旧昵称时进行修改 + if (nickname.equals(user.getNickname())){ + return; + } + UserDO userDO = new UserDO(); + userDO.setId(user.getId()); + userDO.setNickname(nickname); + memberUserMapper.updateById(userDO); + } + + @Override + public String updateUserAvatar(Long userId, InputStream avatarFile) { + this.checkUserExists(userId); + // 创建文件 + String avatar = fileCoreService.createFile(IdUtil.fastUUID(), IoUtil.readBytes(avatarFile)); + // 更新头像路径 + memberUserMapper.updateById(UserDO.builder().id(userId).avatar(avatar).build()); + return avatar; + } + + @Transactional(rollbackFor = Exception.class) + public void updateUserMobile(Long userId, AppUserUpdateMobileReqVO reqVO) { + // 检测用户是否存在 + checkUserExists(userId); + + // 校验旧手机和旧验证码 + SysSmsCodeDO sysSmsCodeDO = smsCodeService.checkCodeIsExpired(reqVO.getOldMobile(), reqVO.getOldCode(), + SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene()); + // 判断旧 code 是否未被使用,如果是,抛出异常 + if (Boolean.FALSE.equals(sysSmsCodeDO.getUsed())){ + throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_SMS_CODE_IS_UNUSED); + } + + // 使用新验证码 + smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), + reqVO.getCode(),getClientIP()); + + // 更新用户手机 + memberUserMapper.updateById(UserDO.builder().id(userId).mobile(reqVO.getMobile()).build()); + } + + @VisibleForTesting + public UserDO checkUserExists(Long id) { + if (id == null) { + return null; + } + UserDO user = memberUserMapper.selectById(id); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + return user; + } + +} diff --git a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/service/SysAuthServiceTest.java b/yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/auth/SysAuthServiceTest.java similarity index 65% rename from yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/service/SysAuthServiceTest.java rename to yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/auth/SysAuthServiceTest.java index 83cf35be6..aa38ab936 100644 --- a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/service/SysAuthServiceTest.java +++ b/yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/auth/SysAuthServiceTest.java @@ -1,20 +1,18 @@ -package cn.iocoder.yudao.userserver.modules.system.service; +package cn.iocoder.yudao.module.member.service.auth; -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; -import cn.iocoder.yudao.userserver.BaseDbAndRedisUnitTest; -import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; -import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.MbrAuthResetPasswordReqVO; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.MbrAuthUpdatePasswordReqVO; -import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; -import cn.iocoder.yudao.userserver.modules.system.service.auth.impl.SysAuthServiceImpl; -import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; +import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthResetPasswordReqVO; +import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthUpdatePasswordReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; +import cn.iocoder.yudao.module.member.dal.mysql.user.UserMapper; +import cn.iocoder.yudao.module.member.service.sms.SysSmsCodeService; +import cn.iocoder.yudao.module.member.service.user.UserService; +import cn.iocoder.yudao.module.member.test.BaseDbAndRedisUnitTest; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; @@ -23,7 +21,6 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.password.PasswordEncoder; import javax.annotation.Resource; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import static cn.hutool.core.util.RandomUtil.randomEle; @@ -35,17 +32,17 @@ import static org.mockito.Mockito.when; // TODO @芋艿:单测的 review,等逻辑都达成一致后 /** - * {@link SysAuthService} 的单元测试类 + * {@link AuthService} 的单元测试类 * * @author 宋天 */ -@Import({SysAuthServiceImpl.class, YudaoRedisAutoConfiguration.class}) +@Import({AuthServiceImpl.class, YudaoRedisAutoConfiguration.class}) public class SysAuthServiceTest extends BaseDbAndRedisUnitTest { @MockBean private AuthenticationManager authenticationManager; @MockBean - private MbrUserService userService; + private UserService userService; @MockBean private SysSmsCodeService smsCodeService; @MockBean @@ -59,21 +56,21 @@ public class SysAuthServiceTest extends BaseDbAndRedisUnitTest { @MockBean private PasswordEncoder passwordEncoder; @Resource - private MbrUserMapper mbrUserMapper; + private UserMapper mbrUserMapper; @Resource - private SysAuthServiceImpl authService; + private AuthServiceImpl authService; @Test public void testUpdatePassword_success(){ // 准备参数 - MbrUserDO userDO = randomMbrUserDO(); + UserDO userDO = randomUserDO(); mbrUserMapper.insert(userDO); // 新密码 String newPassword = randomString(); // 请求实体 - MbrAuthUpdatePasswordReqVO reqVO = MbrAuthUpdatePasswordReqVO.builder() + AppAuthUpdatePasswordReqVO reqVO = AppAuthUpdatePasswordReqVO.builder() .oldPassword(userDO.getPassword()) .password(newPassword) .build(); @@ -84,14 +81,14 @@ public class SysAuthServiceTest extends BaseDbAndRedisUnitTest { when(passwordEncoder.encode(newPassword)).thenReturn(newPassword); // 更新用户密码 - authService.updatePassword(userDO.getId(),reqVO); + authService.updatePassword(userDO.getId(), reqVO); assertEquals(mbrUserMapper.selectById(userDO.getId()).getPassword(),newPassword); } @Test public void testResetPassword_success(){ // 准备参数 - MbrUserDO userDO = randomMbrUserDO(); + UserDO userDO = randomUserDO(); mbrUserMapper.insert(userDO); // 随机密码 @@ -99,17 +96,15 @@ public class SysAuthServiceTest extends BaseDbAndRedisUnitTest { // 随机验证码 String code = randomNumbers(4); - MbrAuthResetPasswordReqVO reqVO = MbrAuthResetPasswordReqVO.builder() - .password(password) - .code(code) - .build(); - // 放入code+手机号 - stringRedisTemplate.opsForValue().set(code,userDO.getMobile(),10, TimeUnit.MINUTES); - // mock when(passwordEncoder.encode(password)).thenReturn(password); // 更新用户密码 + AppAuthResetPasswordReqVO reqVO = new AppAuthResetPasswordReqVO(); + reqVO.setMobile(userDO.getMobile()); + reqVO.setPassword(password); + reqVO.setCode(code); + authService.resetPassword(reqVO); assertEquals(mbrUserMapper.selectById(userDO.getId()).getPassword(),password); } @@ -118,12 +113,12 @@ public class SysAuthServiceTest extends BaseDbAndRedisUnitTest { // ========== 随机对象 ========== @SafeVarargs - private static MbrUserDO randomMbrUserDO(Consumer... consumers) { - Consumer consumer = (o) -> { + private static UserDO randomUserDO(Consumer... consumers) { + Consumer consumer = (o) -> { o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 o.setPassword(randomString()); }; - return randomPojo(MbrUserDO.class, ArrayUtils.append(consumer, consumers)); + return randomPojo(UserDO.class, ArrayUtils.append(consumer, consumers)); } diff --git a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/service/MbrUserServiceImplTest.java b/yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/user/MbrUserServiceImplTest.java similarity index 53% rename from yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/service/MbrUserServiceImplTest.java rename to yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/user/MbrUserServiceImplTest.java index 05571ba2e..96c562fa9 100644 --- a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/service/MbrUserServiceImplTest.java +++ b/yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/user/MbrUserServiceImplTest.java @@ -1,19 +1,18 @@ -package cn.iocoder.yudao.userserver.modules.member.service; +package cn.iocoder.yudao.module.member.service.user; +import cn.hutool.core.util.RandomUtil; import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService; -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; -import cn.iocoder.yudao.userserver.BaseDbAndRedisUnitTest; -import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO; -import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; -import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; -import cn.iocoder.yudao.userserver.modules.member.service.user.impl.MbrUserServiceImpl; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO; -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; -import cn.iocoder.yudao.userserver.modules.system.service.auth.impl.SysAuthServiceImpl; -import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO; +import cn.iocoder.yudao.module.member.dal.mysql.user.UserMapper; +import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum; +import cn.iocoder.yudao.module.member.service.auth.AuthServiceImpl; +import cn.iocoder.yudao.module.member.service.sms.SysSmsCodeService; +import cn.iocoder.yudao.module.member.test.BaseDbAndRedisUnitTest; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; @@ -32,24 +31,24 @@ import static org.mockito.Mockito.*; // TODO @芋艿:单测的 review,等逻辑都达成一致后 /** - * {@link MbrUserServiceImpl} 的单元测试类 + * {@link UserServiceImpl} 的单元测试类 * * @author 宋天 */ -@Import({MbrUserServiceImpl.class, YudaoRedisAutoConfiguration.class}) +@Import({UserServiceImpl.class, YudaoRedisAutoConfiguration.class}) public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest { @Resource - private MbrUserServiceImpl mbrUserService; + private UserServiceImpl mbrUserService; @Resource private StringRedisTemplate stringRedisTemplate; @Resource - private MbrUserMapper userMapper; + private UserMapper userMapper; @MockBean - private SysAuthServiceImpl authService; + private AuthServiceImpl authService; @MockBean private InfFileCoreService fileCoreService; @@ -63,35 +62,24 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest { @Test public void testUpdateNickName_success(){ // mock 数据 - MbrUserDO userDO = randomMbrUserDO(); + UserDO userDO = randomUserDO(); userMapper.insert(userDO); // 随机昵称 String newNickName = randomString(); // 调用接口修改昵称 - mbrUserService.updateNickname(userDO.getId(),newNickName); + mbrUserService.updateUserNickname(userDO.getId(),newNickName); // 查询新修改后的昵称 String nickname = mbrUserService.getUser(userDO.getId()).getNickname(); // 断言 assertEquals(newNickName,nickname); } - @Test - public void testGetUserInfo_success(){ - // mock 数据 - MbrUserDO userDO = randomMbrUserDO(); - userMapper.insert(userDO); - - // 查询用户昵称与头像 - MbrUserInfoRespVO userInfo = mbrUserService.getUserInfo(userDO.getId()); - System.out.println(userInfo); - } - @Test public void testUpdateAvatar_success(){ // mock 数据 - MbrUserDO dbUser = randomMbrUserDO(); + UserDO dbUser = randomUserDO(); userMapper.insert(dbUser); // 准备参数 @@ -102,7 +90,7 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest { String avatar = randomString(); when(fileCoreService.createFile(anyString(), eq(avatarFileBytes))).thenReturn(avatar); // 调用 - String str = mbrUserService.updateAvatar(userId, avatarFile); + String str = mbrUserService.updateUserAvatar(userId, avatarFile); // 断言 assertEquals(avatar, str); } @@ -111,28 +99,28 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest { public void updateMobile_success(){ // mock数据 String oldMobile = randomNumbers(11); - MbrUserDO userDO = randomMbrUserDO(); + UserDO userDO = randomUserDO(); userDO.setMobile(oldMobile); userMapper.insert(userDO); - // 验证旧手机 - sysSmsCodeService.sendSmsCodeLogin(userDO.getId()); - - // 验证旧手机验证码是否正确 - sysSmsCodeService.useSmsCode(oldMobile,SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(),"123","1.1.1.1"); - // 验证新手机 - SysAuthSendSmsReqVO smsReqVO = new SysAuthSendSmsReqVO(); - smsReqVO.setMobile(oldMobile); - smsReqVO.setScene(SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene()); - sysSmsCodeService.sendSmsNewCode(smsReqVO); + // 旧手机和旧验证码 + SysSmsCodeDO codeDO = new SysSmsCodeDO(); + String oldCode = RandomUtil.randomString(4); + codeDO.setMobile(userDO.getMobile()); + codeDO.setCode(oldCode); + codeDO.setScene(SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene()); + codeDO.setUsed(Boolean.FALSE); + when(sysSmsCodeService.checkCodeIsExpired(codeDO.getMobile(),codeDO.getCode(),codeDO.getScene())).thenReturn(codeDO); // 更新手机号 String newMobile = randomNumbers(11); - String code = randomNumbers(4); - MbrUserUpdateMobileReqVO reqVO = new MbrUserUpdateMobileReqVO(); + String newCode = randomNumbers(4); + AppUserUpdateMobileReqVO reqVO = new AppUserUpdateMobileReqVO(); reqVO.setMobile(newMobile); - reqVO.setCode(code); - mbrUserService.updateMobile(userDO.getId(),reqVO); + reqVO.setCode(newCode); + reqVO.setOldMobile(oldMobile); + reqVO.setOldCode(oldCode); + mbrUserService.updateUserMobile(userDO.getId(),reqVO); assertEquals(mbrUserService.getUser(userDO.getId()).getMobile(),newMobile); } @@ -140,11 +128,11 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest { // ========== 随机对象 ========== @SafeVarargs - private static MbrUserDO randomMbrUserDO(Consumer... consumers) { - Consumer consumer = (o) -> { + private static UserDO randomUserDO(Consumer... consumers) { + Consumer consumer = (o) -> { o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 }; - return randomPojo(MbrUserDO.class, ArrayUtils.append(consumer, consumers)); + return randomPojo(UserDO.class, ArrayUtils.append(consumer, consumers)); } } diff --git a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/BaseDbAndRedisUnitTest.java b/yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/BaseDbAndRedisUnitTest.java similarity index 95% rename from yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/BaseDbAndRedisUnitTest.java rename to yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/BaseDbAndRedisUnitTest.java index 2669ef49c..a9278498d 100644 --- a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/BaseDbAndRedisUnitTest.java +++ b/yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/BaseDbAndRedisUnitTest.java @@ -1,9 +1,8 @@ -package cn.iocoder.yudao.userserver; +package cn.iocoder.yudao.module.member.test; import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration; import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration; import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; -import cn.iocoder.yudao.userserver.config.RedisTestConfiguration; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; import org.redisson.spring.starter.RedissonAutoConfiguration; diff --git a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/BaseDbUnitTest.java b/yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/BaseDbUnitTest.java similarity index 97% rename from yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/BaseDbUnitTest.java rename to yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/BaseDbUnitTest.java index af8d71a0c..901d896bf 100644 --- a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/BaseDbUnitTest.java +++ b/yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/BaseDbUnitTest.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver; +package cn.iocoder.yudao.module.member.test; import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration; import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration; diff --git a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/config/RedisTestConfiguration.java b/yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/RedisTestConfiguration.java similarity index 96% rename from yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/config/RedisTestConfiguration.java rename to yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/RedisTestConfiguration.java index 7164efd87..99838337b 100644 --- a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/config/RedisTestConfiguration.java +++ b/yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/RedisTestConfiguration.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.userserver.config; +package cn.iocoder.yudao.module.member.test; import com.github.fppt.jedismock.RedisServer; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; diff --git a/yudao-user-server/src/test/resources/application-unit-test.yaml b/yudao-module-member/yudao-module-member-impl/src/test/resources/application-unit-test.yaml similarity index 90% rename from yudao-user-server/src/test/resources/application-unit-test.yaml rename to yudao-module-member/yudao-module-member-impl/src/test/resources/application-unit-test.yaml index d306a7af4..237068e6e 100644 --- a/yudao-user-server/src/test/resources/application-unit-test.yaml +++ b/yudao-module-member/yudao-module-member-impl/src/test/resources/application-unit-test.yaml @@ -42,3 +42,8 @@ mybatis: --- #################### 芋道相关配置 #################### # 芋道配置项,设置当前项目所有自定义的配置 +yudao: + info: + base-package: cn.iocoder.yudao.module.member.dal.mysql + core-service: + base-package: cn.iocoder.yudao.module.member.dal.mysql # TODO 芋艿:要清理掉 diff --git a/yudao-user-server/src/test/resources/file/erweima.jpg b/yudao-module-member/yudao-module-member-impl/src/test/resources/file/erweima.jpg similarity index 100% rename from yudao-user-server/src/test/resources/file/erweima.jpg rename to yudao-module-member/yudao-module-member-impl/src/test/resources/file/erweima.jpg diff --git a/yudao-user-server/src/test/resources/logback-spring.xml b/yudao-module-member/yudao-module-member-impl/src/test/resources/logback-spring.xml similarity index 100% rename from yudao-user-server/src/test/resources/logback-spring.xml rename to yudao-module-member/yudao-module-member-impl/src/test/resources/logback-spring.xml diff --git a/yudao-user-server/src/test/resources/sql/clean.sql b/yudao-module-member/yudao-module-member-impl/src/test/resources/sql/clean.sql similarity index 100% rename from yudao-user-server/src/test/resources/sql/clean.sql rename to yudao-module-member/yudao-module-member-impl/src/test/resources/sql/clean.sql diff --git a/yudao-user-server/src/test/resources/sql/create_tables.sql b/yudao-module-member/yudao-module-member-impl/src/test/resources/sql/create_tables.sql similarity index 100% rename from yudao-user-server/src/test/resources/sql/create_tables.sql rename to yudao-module-member/yudao-module-member-impl/src/test/resources/sql/create_tables.sql diff --git a/yudao-user-server/pom.xml b/yudao-user-server/pom.xml index 03dbfd699..be4005835 100644 --- a/yudao-user-server/pom.xml +++ b/yudao-user-server/pom.xml @@ -90,15 +90,6 @@ yudao-spring-boot-starter-test test - - - - - junit - junit - test - - diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/package-info.java deleted file mode 100644 index db25ed2a4..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 属于整个 yudao-user-server 的 framework 封装 - * - * @author 芋道源码 - */ -package cn.iocoder.yudao.userserver.framework; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/security/SecurityConfiguration.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/security/SecurityConfiguration.java deleted file mode 100644 index 66a0ae718..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/security/SecurityConfiguration.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.userserver.framework.security; - -import cn.iocoder.yudao.framework.web.config.WebProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; - -import javax.annotation.Resource; - -@Configuration -public class SecurityConfiguration { - - @Resource - private WebProperties webProperties; - - @Bean - public Customizer.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer() { - return registry -> { - registry.antMatchers(api("/**")).permitAll(); // 默认 API 都是用户可访问 - }; - } - - private String api(String url) { - return webProperties.getApiPrefix() + url; - } - -} diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/package-info.java deleted file mode 100644 index 148c73cf3..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.member.controller; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java deleted file mode 100644 index c0778184a..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.member.controller.user; - -import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO; -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; -import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; -import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; -import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import lombok.extern.slf4j.Slf4j; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import java.io.IOException; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; -import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.FILE_IS_EMPTY; - -@Api(tags = "用户个人中心") -@RestController -@RequestMapping("/member/user/profile") -@Validated -@Slf4j -public class SysUserProfileController { - - @Resource - private MbrUserService userService; - - @Resource - private SysSmsCodeService smsCodeService; - - @PutMapping("/update-nickname") - @ApiOperation("修改用户昵称") - @PreAuthenticated - public CommonResult updateNickname(@RequestParam("nickname") String nickname) { - userService.updateNickname(getLoginUserId(), nickname); - return success(true); - } - - @PutMapping("/update-avatar") - @ApiOperation("修改用户头像") - @PreAuthenticated - public CommonResult updateAvatar(@RequestParam("avatarFile") MultipartFile file) throws IOException { - if (file.isEmpty()) { - throw ServiceExceptionUtil.exception(FILE_IS_EMPTY); - } - String avatar = userService.updateAvatar(getLoginUserId(), file.getInputStream()); - return success(avatar); - } - - @GetMapping("/get") - @ApiOperation("获得基本信息") - @PreAuthenticated - public CommonResult getUserInfo() { - return success(userService.getUserInfo(getLoginUserId())); - } - - @PostMapping("/update-mobile") - @ApiOperation(value = "修改用户手机") - @PreAuthenticated - public CommonResult updateMobile(@RequestBody @Valid MbrUserUpdateMobileReqVO reqVO) { - // 校验验证码 - // TODO @宋天:统一到 userService.updateMobile 方法里 - smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP()); - - userService.updateMobile(getLoginUserId(), reqVO); - return success(true); - } - -} - diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/convert/user/UserProfileConvert.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/convert/user/UserProfileConvert.java deleted file mode 100644 index 6f9d16691..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/convert/user/UserProfileConvert.java +++ /dev/null @@ -1,15 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.member.convert.user; - -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; -import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -@Mapper -public interface UserProfileConvert { - - UserProfileConvert INSTANCE = Mappers.getMapper(UserProfileConvert.class); - - MbrUserInfoRespVO convert(MbrUserDO bean); - -} diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md deleted file mode 100644 index 8153487b7..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/dal/mysql/user/MbrUserMapper.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/dal/mysql/user/MbrUserMapper.java deleted file mode 100644 index 6abb5ddf3..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/dal/mysql/user/MbrUserMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.member.dal.mysql.user; - -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import org.apache.ibatis.annotations.Mapper; - -/** - * MbrUserDO Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface MbrUserMapper extends BaseMapperX { - - default MbrUserDO selectByMobile(String mobile) { - return selectOne(MbrUserDO::getMobile, mobile); - } - -} diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/package-info.java deleted file mode 100644 index 2334d44bf..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * weixin 包下,我们放微信相关业务. - * 例如说:微信公众号、等等 - * ps:微信支付,还是放在 pay 包下 - * - * 缩写:wx - */ -package cn.iocoder.yudao.userserver.modules.member; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/package-info.java deleted file mode 100644 index b12c6427a..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.member.service; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java deleted file mode 100644 index 442da4147..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java +++ /dev/null @@ -1,147 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.member.service.user.impl; - -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.util.IdUtil; -import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService; -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; -import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; -import cn.iocoder.yudao.userserver.modules.member.convert.user.UserProfileConvert; -import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; -import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; -import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.io.InputStream; -import java.util.Date; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.USER_NOT_EXISTS; - -/** - * User Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Valid -@Slf4j -public class MbrUserServiceImpl implements MbrUserService { - - @Resource - private MbrUserMapper userMapper; - - @Resource - private InfFileCoreService fileCoreService; - - @Resource - private PasswordEncoder passwordEncoder; - - @Resource - private SysAuthService sysAuthService; - - @Override - public MbrUserDO getUserByMobile(String mobile) { - return userMapper.selectByMobile(mobile); - } - - @Override - public MbrUserDO createUserIfAbsent(String mobile, String registerIp) { - // 用户已经存在 - MbrUserDO user = userMapper.selectByMobile(mobile); - if (user != null) { - return user; - } - // 用户不存在,则进行创建 - return this.createUser(mobile, registerIp); - } - - private MbrUserDO createUser(String mobile, String registerIp) { - // 生成密码 - String password = IdUtil.fastSimpleUUID(); - // 插入用户 - MbrUserDO user = new MbrUserDO(); - user.setMobile(mobile); - user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 - user.setPassword(passwordEncoder.encode(password)); // 加密密码 - user.setRegisterIp(registerIp); - userMapper.insert(user); - return user; - } - - @Override - public void updateUserLogin(Long id, String loginIp) { - userMapper.updateById(new MbrUserDO().setId(id).setLoginIp(loginIp).setLoginDate(new Date())); - } - - @Override - public MbrUserDO getUser(Long id) { - return userMapper.selectById(id); - } - - @Override - public void updateNickname(Long userId, String nickname) { - MbrUserDO user = this.checkUserExists(userId); - // 仅当新昵称不等于旧昵称时进行修改 - if (nickname.equals(user.getNickname())){ - return; - } - MbrUserDO userDO = new MbrUserDO(); - userDO.setId(user.getId()); - userDO.setNickname(nickname); - userMapper.updateById(userDO); - } - - @Override - public String updateAvatar(Long userId, InputStream avatarFile) { - this.checkUserExists(userId); - // 创建文件 - String avatar = fileCoreService.createFile(IdUtil.fastUUID(), IoUtil.readBytes(avatarFile)); - // 更新头像路径 - MbrUserDO userDO = MbrUserDO.builder() - .id(userId) - .avatar(avatar) - .build(); - userMapper.updateById(userDO); - return avatar; - } - - @Override - public MbrUserInfoRespVO getUserInfo(Long userId) { - MbrUserDO user = this.checkUserExists(userId); - // 拼接返回结果 - return UserProfileConvert.INSTANCE.convert(user); - } - - @Override - public void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO) { - // 检测用户是否存在 - MbrUserDO userDO = checkUserExists(userId); - // 检测手机与验证码是否匹配 - // TODO @宋天:修改手机的时候。应该要校验,老手机 + 老手机 code;新手机 + 新手机 code - sysAuthService.checkIfMobileMatchCodeAndDeleteCode(userDO.getMobile(),reqVO.getCode()); - // 更新用户手机 - // TODO @宋天:更新的时候,单独创建对象。直接全量更新,会可能导致属性覆盖。可以看看打印出来的 SQL 哈 - userDO.setMobile(reqVO.getMobile()); - userMapper.updateById(userDO); - } - - @VisibleForTesting - public MbrUserDO checkUserExists(Long id) { - if (id == null) { - return null; - } - MbrUserDO user = userMapper.selectById(id); - if (user == null) { - throw exception(USER_NOT_EXISTS); - }else{ - return user; - } - } -} diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/package-info.java deleted file mode 100644 index aa4f746ad..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位 - */ -package cn.iocoder.yudao.userserver.modules.system.controller; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/package-info.java deleted file mode 100644 index d7da621f0..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 提供 POJO 类的实体转换 - * - * 目前使用 MapStruct 框架 - */ -package cn.iocoder.yudao.userserver.modules.system.convert; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md deleted file mode 100644 index 8153487b7..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/package-info.java deleted file mode 100644 index 0c99dcc95..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.system.dal.dataobject; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/package-info.java deleted file mode 100644 index a1bdeadcd..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.system.dal.mysql; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/redis/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/redis/package-info.java deleted file mode 100644 index bb5083518..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/redis/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.system.dal.redis; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/sms/SysSmsSceneEnum.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/sms/SysSmsSceneEnum.java deleted file mode 100644 index c2156d218..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/sms/SysSmsSceneEnum.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.system.enums.sms; - -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 用户短信验证码发送场景的枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum SysSmsSceneEnum implements IntArrayValuable { - - LOGIN_BY_SMS(1, "手机号登陆"), - CHANGE_MOBILE_BY_SMS(2, "更换手机号"), - FORGET_MOBILE_BY_SMS(3, "忘记密码"), - CHECK_CODE_BY_SMS(4, "审核验证码"), - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSmsSceneEnum::getScene).toArray(); - - private final Integer scene; - private final String name; - - @Override - public int[] array() { - return ARRAYS; - } - -} diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/sms/SysSmsTemplateCodeConstants.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/sms/SysSmsTemplateCodeConstants.java deleted file mode 100644 index 5529a0741..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/sms/SysSmsTemplateCodeConstants.java +++ /dev/null @@ -1,15 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.system.enums.sms; - -/** - * yudao-user-server 使用到的短信模板的 Code 编码的枚举 - * - * @author 芋道源码 - */ -public interface SysSmsTemplateCodeConstants { - - /** - * 前台用户短信登录 - */ - String USER_SMS_LOGIN = "user-sms-login"; - -} diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/package-info.java deleted file mode 100644 index cb0def51f..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 属于 system 模块的 framework 封装 - * - * @author 芋道源码 - */ -package cn.iocoder.yudao.userserver.modules.system.framework; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/package-info.java deleted file mode 100644 index 89fa9c162..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * system 包下,我们放通用业务,支撑上层的核心业务。 - * 例如说:用户、部门、权限、数据字典等等 - * - * 缩写:sys - */ -package cn.iocoder.yudao.userserver.modules.system; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/package-info.java deleted file mode 100644 index d1896cb92..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.system.service; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/impl/SysSmsCodeServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/impl/SysSmsCodeServiceImpl.java deleted file mode 100644 index a5796c6fc..000000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/impl/SysSmsCodeServiceImpl.java +++ /dev/null @@ -1,141 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.system.service.sms.impl; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; -import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService; -import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO; -import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; -import cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms.SysSmsCodeMapper; -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsTemplateCodeConstants; -import cn.iocoder.yudao.userserver.modules.system.framework.sms.SmsCodeProperties; -import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.Date; -import java.util.concurrent.TimeUnit; - -import static cn.hutool.core.util.RandomUtil.randomInt; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; -import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*; - -/** - * 短信验证码 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class SysSmsCodeServiceImpl implements SysSmsCodeService { - - /** - * 验证码 + 手机 在redis中存储的有效时间,单位:分钟 - */ - private static final Long CODE_TIME = 10L; - - @Resource - private SmsCodeProperties smsCodeProperties; - - @Resource - private SysSmsCodeMapper smsCodeMapper; - - @Resource - private MbrUserService mbrUserService; - - @Resource - private SysSmsCoreService smsCoreService; - - @Resource - private StringRedisTemplate stringRedisTemplate; - - @Override - public void sendSmsCode(String mobile, Integer scene, String createIp) { - // 创建验证码 - String code = this.createSmsCode(mobile, scene, createIp); - // 发送验证码 - // TODO @宋天:这里可以拓展下 SysSmsSceneEnum,支持设置对应的短信模板编号(不同场景的短信文案是不同的)、是否要校验手机号已经注册。这样 Controller 就可以收口成一个接口了。相当于说,不同场景,不同策略 - smsCoreService.sendSingleSmsToMember(mobile, null, SysSmsTemplateCodeConstants.USER_SMS_LOGIN, - MapUtil.of("code", code)); - - // 存储手机号与验证码到redis,用于标记 - // TODO @宋天:SysSmsCodeDO 表应该足够,无需增加额外的 redis 存储哇 - // TODO @宋天:Redis 相关的操作,不要散落到业务层,而是写一个它对应的 RedisDAO。这样,实现业务与技术的解耦 - // TODO @宋天:直接使用 code 作为 key,会存在 2 个问题:1)code 可能会冲突,多个手机号之间;2)缺少前缀。例如说 sms_code_${code} - stringRedisTemplate.opsForValue().set(code,mobile,CODE_TIME, TimeUnit.MINUTES); - } - - @Override - public void sendSmsNewCode(SysAuthSendSmsReqVO reqVO) { - // 检测手机号是否已被使用 - MbrUserDO userByMobile = mbrUserService.getUserByMobile(reqVO.getMobile()); - if (userByMobile != null){ - throw exception(USER_SMS_CODE_IS_EXISTS); - } - - // 发送短信 - this.sendSmsCode(reqVO.getMobile(),reqVO.getScene(),getClientIP()); - } - - private String createSmsCode(String mobile, Integer scene, String ip) { - // 校验是否可以发送验证码,不用筛选场景 - SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null); - if (lastSmsCode != null) { - if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。 - throw exception(USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); - } - if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() - < smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁 - throw exception(USER_SMS_CODE_SEND_TOO_FAST); - } - // TODO 芋艿:提升,每个 IP 每天可发送数量 - // TODO 芋艿:提升,每个 IP 每小时可发送数量 - } - - // 创建验证码记录 - String code = String.valueOf(randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1)); - SysSmsCodeDO newSmsCode = SysSmsCodeDO.builder().mobile(mobile).code(code) - .scene(scene).todayIndex(lastSmsCode != null ? lastSmsCode.getTodayIndex() + 1 : 1) - .createIp(ip).used(false).build(); - smsCodeMapper.insert(newSmsCode); - return code; - } - - @Override - public void useSmsCode(String mobile, Integer scene, String code, String usedIp) { - // 校验验证码 - SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, scene); - if (lastSmsCode == null) { // 若验证码不存在,抛出异常 - throw exception(USER_SMS_CODE_NOT_FOUND); - } - if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() - >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期 - throw exception(USER_SMS_CODE_EXPIRED); - } - if (lastSmsCode.getUsed()) { // 验证码已使用 - throw exception(USER_SMS_CODE_USED); - } - if (!lastSmsCode.getCode().equals(code)) { - throw exception(USER_SMS_CODE_NOT_CORRECT); - } - - // 使用验证码 - smsCodeMapper.updateById(SysSmsCodeDO.builder().id(lastSmsCode.getId()) - .used(true).usedTime(new Date()).usedIp(usedIp).build()); - } - - @Override - public void sendSmsCodeLogin(Long userId) { - MbrUserDO user = mbrUserService.getUser(userId); - if (user == null){ - throw exception(USER_NOT_EXISTS); - } - // 发送验证码 - this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP()); - } - -} diff --git a/yudao-user-server/src/main/resources/application-dev.yaml b/yudao-user-server/src/main/resources/application-dev.yaml index 7e6aa355e..f44bc4935 100644 --- a/yudao-user-server/src/main/resources/application-dev.yaml +++ b/yudao-user-server/src/main/resources/application-dev.yaml @@ -124,6 +124,19 @@ logging: file: name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + +--- #################### 微信公众号相关配置 #################### +wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 + mp: + # 公众号配置(必填) + app-id: wx041349c6f39b268b + secret: 5abee519483bc9f8cb37ce280e814bd0 + # 存储配置,解决 AccessToken 的跨节点的共享 + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + --- #################### 芋道相关配置 #################### # 芋道配置项,设置当前项目所有自定义的配置 diff --git a/yudao-user-server/src/main/resources/application.yaml b/yudao-user-server/src/main/resources/application.yaml index 209543380..1d789232d 100644 --- a/yudao-user-server/src/main/resources/application.yaml +++ b/yudao-user-server/src/main/resources/application.yaml @@ -66,11 +66,6 @@ yudao: constants-class-list: - cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants - cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants - sms-code: # 短信验证码相关的配置项 - expire-times: 10m - send-frequency: 1m - send-maximum-quantity-per-day: 10 - begin-code: 9999 # 这里配置 9999 的原因是,测试方便。 - end-code: 9999 # 这里配置 9999 的原因是,测试方便。 + debug: false diff --git a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/controller/SysUserProfileControllerTest.java b/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/controller/SysUserProfileControllerTest.java deleted file mode 100644 index 63c0be2d2..000000000 --- a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/controller/SysUserProfileControllerTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.member.controller; - -import cn.iocoder.yudao.userserver.modules.member.controller.user.SysUserProfileController; -import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; -import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.result.MockMvcResultHandlers; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * {@link SysUserProfileController} 的单元测试类 - * - * @author 宋天 - */ -// TODO @宋天:controller 的单测可以不写哈,因为收益太低了。未来我们做 qa 自动化测试 -public class SysUserProfileControllerTest { - - private MockMvc mockMvc; - - @InjectMocks - private SysUserProfileController sysUserProfileController; - - @Mock - private MbrUserService userService; - - @Mock - private SysSmsCodeService smsCodeService; - - @Before // TODO @宋天:使用 junit5 哈 - public void setup() { - // 初始化 - MockitoAnnotations.openMocks(this); - - // 构建mvc环境 - mockMvc = MockMvcBuilders.standaloneSetup(sysUserProfileController).build(); - } - - @Test - @Ignore - public void testUpdateMobile_success() throws Exception { - //模拟接口调用 - this.mockMvc.perform(post("/system/user/profile/update-mobile") - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content("{\"mobile\":\"15819844280\",\"code\":\"123456\"}}")) - .andExpect(status().isOk()) - .andDo(MockMvcResultHandlers.print()); -// TODO @宋天:方法的结尾,不用空行哈 - } - - -} diff --git a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/controller/SysAuthControllerTest.java b/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/controller/SysAuthControllerTest.java deleted file mode 100644 index a398da840..000000000 --- a/yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/controller/SysAuthControllerTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.system.controller; - -import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.SysAuthController; -import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; -import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.result.MockMvcResultHandlers; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * {@link SysAuthController} 的单元测试类 - * - * @author 宋天 - */ -public class SysAuthControllerTest { - - private MockMvc mockMvc; - - @InjectMocks - private SysAuthController sysAuthController; - - @Mock - private SysAuthService authService; - @Mock - private SysSmsCodeService smsCodeService; - @Mock - private SysSocialCoreService socialService; - - @Before - public void setup() { - // 初始化 - MockitoAnnotations.openMocks(this); - - // 构建mvc环境 - mockMvc = MockMvcBuilders.standaloneSetup(sysAuthController).build(); - } - - @Test - public void testResetPassword_success() throws Exception { - // 模拟接口调用 - this.mockMvc.perform(post("/reset-password") - .contentType(MediaType.APPLICATION_JSON) - .content("{\"password\":\"1123\",\"code\":\"123456\"}}")) - .andExpect(status().isOk()) - .andDo(MockMvcResultHandlers.print()); - } - - @Test - public void testUpdatePassword_success() throws Exception { - // 模拟接口调用 - this.mockMvc.perform(post("/update-password") - .contentType(MediaType.APPLICATION_JSON) - .content("{\"password\":\"1123\",\"code\":\"123456\",\"oldPassword\":\"1123\"}}")) - .andExpect(status().isOk()) - .andDo(MockMvcResultHandlers.print()); - } - -} diff --git a/更新日志.md b/更新日志.md index 5ce39fb84..9ac18dd8e 100644 --- a/更新日志.md +++ b/更新日志.md @@ -4,29 +4,30 @@ * 钉钉、飞书等通知 * Vue3 支持 -## [v1.4.0] 计划 - -* 工作流 - * 修改表单为外置表单 - * 修改请假流程 - * 暂时以用户的岗位作为activiti 的用户组 - * 请假需要请假人部门下具有项目经理岗位, 部门经理, 和人事 岗位的用户 - * 新增 芋道源码部门下 用户 normal(岗位 普通用户) projectmgr(岗位 项目经理) depmgr(岗位 部门经理) hradmin (岗位 人事) - * 请假流程如下 - 1. 请假人 normal (密码 123456) 登录在我的请假表单,点击新增,填写请假表单 - 2. 如果请假天数<=3, 项目经理 进行审批. 项目经理 projectmgr(密码:123456) 登录 待办请假,中进行审批,可以查看历史跟踪,和流程图 - 3. 如果请假天数>3 需部门经理 进行审批,部门经理depmgr(密码:123456) 登录 待办请假,中进行审批,可以查看历史跟踪,和流程图 - 4. 人事登陆(用户名:hradmin 密码:123456) 登录 待办请假, 中进行审批,可以查看历史跟踪,和流程图 - 5. 流程结束 - * 我的请假中,可以查询本人的请假申请, 和进度 - ### 📝 TODO * 支付 * 用户前台的社交登陆 * 用户前台的修改手机、修改密码、忘记密码 -## [v1.3.0] 进行中 +## [v1.4.0] 计划,预计 2022.02.28 发布 + +### ⚠️ Warning + +### 📈 Statistic + +### ⭐ New Features + +*【优化】操作日志新增用户类型,实现 APP 端的 API 的操作日志的记录 + +### 🐞 Bug Fixes + +*【修复】用户无权限访问 指定 API 时,未返回 FORBIDDEN 结果码 + +### 🔨 Dependency Upgrades + + +## [v1.3.0] 2022.01.24 ### ⚠️ Warning