소스 검색

Merge branch 'feature/multi-module' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/flowable

 Conflicts:
	pom.xml
	yudao-module-bpm/yudao-module-bpm-activiti/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/config/BpmActivitiConfiguration.java
YunaiV 3 년 전
부모
커밋
6aca4ae9fd
100개의 변경된 파일1522개의 추가작업 그리고 570개의 파일을 삭제
  1. 5 1
      http-client.env.json
  2. 4 1
      pom.xml
  3. 12 1
      yudao-admin-server/pom.xml
  4. 2 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/AdminServerApplication.java
  5. 14 5
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java
  6. 13 9
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
  7. 2 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java
  8. 25 0
      yudao-admin-server/src/main/resources/application-local.yaml
  9. 15 3
      yudao-admin-server/src/main/resources/application.yaml
  10. 0 1
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/infra/service/file/InfFileCoreService.java
  11. 0 6
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/convert/package-info.java
  12. 0 10
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/dal/mysql/user/MbrUserCoreMapper.java
  13. 0 7
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/package-info.java
  14. 0 19
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/service/user/MbrUserCoreService.java
  15. 0 28
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/service/user/impl/MbrUserCoreServiceImpl.java
  16. 5 8
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/sms/impl/SysSmsCoreServiceImpl.java
  17. 0 7
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/tool/package-info.java
  18. 1 1
      yudao-dependencies/pom.xml
  19. 6 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java
  20. 1 0
      yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java
  21. 3 0
      yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java
  22. 2 1
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java
  23. 18 5
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
  24. 21 21
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
  25. 149 0
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUserDetailsAuthenticationProvider.java
  26. 43 0
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUsernamePasswordAuthenticationToken.java
  27. 10 11
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JWTAuthenticationTokenFilter.java
  28. 0 53
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/AbstractSignUpUrlAuthenticationSuccessHandler.java
  29. 4 6
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/LogoutSuccessHandlerImpl.java
  30. 9 1
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java
  31. 7 3
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java
  32. 34 21
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/WebProperties.java
  33. 13 4
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java
  34. 5 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
  35. 16 3
      yudao-module-bpm/yudao-module-bpm-activiti/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/config/BpmActivitiConfiguration.java
  36. 24 0
      yudao-module-member/pom.xml
  37. 26 0
      yudao-module-member/yudao-module-member-api/pom.xml
  38. 4 0
      yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java
  39. 20 0
      yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/UserApi.java
  40. 32 0
      yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserRespDTO.java
  41. 101 0
      yudao-module-member/yudao-module-member-impl/pom.xml
  42. 1 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java
  43. 30 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/api/user/UserApiImpl.java
  44. 1 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/package-info.java
  45. 1 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/package-info.java
  46. 1 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/package-info.java
  47. 2 1
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
  48. 31 43
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
  49. 42 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthCheckCodeReqVO.java
  50. 3 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java
  51. 3 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java
  52. 11 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthResetPasswordReqVO.java
  53. 4 4
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSendSmsReqVO.java
  54. 3 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java
  55. 3 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialBindReqVO.java
  56. 3 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLogin2ReqVO.java
  57. 3 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLoginReqVO.java
  58. 3 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialUnbindReqVO.java
  59. 4 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthUpdatePasswordReqVO.java
  60. 1 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/package-info.java
  61. 0 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.http
  62. 72 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.java
  63. 3 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserInfoRespVO.java
  64. 19 5
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppUserUpdateMobileReqVO.java
  65. 6 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/package-info.java
  66. 6 6
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java
  67. 1 1
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/package-info.java
  68. 17 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java
  69. 0 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md
  70. 1 2
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/sms/SysSmsCodeDO.java
  71. 3 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/UserDO.java
  72. 6 4
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/sms/SysSmsCodeMapper.java
  73. 19 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/UserMapper.java
  74. 9 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/package-info.java
  75. 4 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/redis/package-info.java
  76. 3 2
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/MemberErrorCodeConstants.java
  77. 2 2
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/SysErrorCodeConstants.java
  78. 54 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/sms/SysSmsSceneEnum.java
  79. 25 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/sms/SysSmsTemplateCodeConstants.java
  80. 6 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/package-info.java
  81. 1 1
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/sms/SmsCodeConfiguration.java
  82. 1 1
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/framework/sms/SmsCodeProperties.java
  83. 1 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/package-info.java
  84. 11 17
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthService.java
  85. 73 85
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthServiceImpl.java
  86. 17 13
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/sms/SysSmsCodeService.java
  87. 137 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/sms/SysSmsCodeServiceImpl.java
  88. 12 20
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/UserService.java
  89. 147 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/UserServiceImpl.java
  90. 25 30
      yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/auth/SysAuthServiceTest.java
  91. 37 49
      yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/user/MbrUserServiceImplTest.java
  92. 1 2
      yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/BaseDbAndRedisUnitTest.java
  93. 1 1
      yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/BaseDbUnitTest.java
  94. 1 1
      yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/test/RedisTestConfiguration.java
  95. 5 0
      yudao-module-member/yudao-module-member-impl/src/test/resources/application-unit-test.yaml
  96. 0 0
      yudao-module-member/yudao-module-member-impl/src/test/resources/file/erweima.jpg
  97. 0 0
      yudao-module-member/yudao-module-member-impl/src/test/resources/logback-spring.xml
  98. 0 0
      yudao-module-member/yudao-module-member-impl/src/test/resources/sql/clean.sql
  99. 0 0
      yudao-module-member/yudao-module-member-impl/src/test/resources/sql/create_tables.sql
  100. 0 9
      yudao-user-server/pom.xml

+ 5 - 1
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"
   }
 }

+ 4 - 1
pom.xml

@@ -13,6 +13,7 @@
         <module>yudao-admin-server</module>
         <module>yudao-user-server</module>
         <module>yudao-core-service</module>
+        <module>yudao-module-member</module>
         <module>yudao-module-bpm</module>
     </modules>
 
@@ -21,13 +22,14 @@
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
     <properties>
-        <revision>1.3.0-snapshot</revision>
+        <revision>1.4.0-snapshot</revision>
         <!-- Maven 相关 -->
         <java.version>1.8</java.version>
         <maven.compiler.source>${java.version}</maven.compiler.source>
         <maven.compiler.target>${java.version}</maven.compiler.target>
         <maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
         <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
+        <!-- 看看咋放到 bom 里 -->
         <lombok.version>1.18.20</lombok.version>
         <mapstruct.version>1.4.1.Final</mapstruct.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -55,6 +57,7 @@
                     <artifactId>maven-surefire-plugin</artifactId>
                     <version>${maven-surefire-plugin.version}</version>
                 </plugin>
+                <!-- TODO 看看咋放到 bom 里 -->
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-compiler-plugin</artifactId>

+ 12 - 1
yudao-admin-server/pom.xml

@@ -13,10 +13,21 @@
     <packaging>jar</packaging>
 
     <name>yudao-admin-server</name>
-    <description>管理后台 Server,提供其 API 接口</description>
+    <description>
+        后端 Server 的主项目,通过引入需要 yudao-module-xxx 的依赖,
+        从而实现提供 RESTful API 给 yudao-ui-admin、yudao-ui-user 等前端项目。
+        本质上来说,它就是个空壳(容器)!
+    </description>
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
     <dependencies>
+        <!-- TODO 芋艿:多模块 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-member-impl</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
         <!-- 业务组件 -->
         <dependency>
             <groupId>cn.iocoder.boot</groupId>

+ 2 - 1
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) {

+ 14 - 5
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java

@@ -23,19 +23,28 @@ public class SecurityConfiguration {
     public Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.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;
     }
 
 }

+ 13 - 9
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<SysSocialUserDO> socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, USER_TYPE_ENUM);
+        List<SysSocialUserDO> 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());

+ 2 - 1
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

+ 25 - 0
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 请求微信公众号平台
+
 --- #################### 芋道相关配置 ####################
 
 # 芋道配置项,设置当前项目所有自定义的配置

+ 15 - 3
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

+ 0 - 1
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 {
 
-
     /**
      * 保存文件,并返回文件的访问路径
      *

+ 0 - 6
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/convert/package-info.java

@@ -1,6 +0,0 @@
-/**
- * 提供 POJO 类的实体转换
- *
- * 目前使用 MapStruct 框架
- */
-package cn.iocoder.yudao.coreservice.modules.member.convert;

+ 0 - 10
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/dal/mysql/user/MbrUserCoreMapper.java

@@ -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<MbrUserDO> {
-
-}

+ 0 - 7
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/package-info.java

@@ -1,7 +0,0 @@
-/**
- * member 包下,我们放会员业务.
- * 例如说:会员中心等等
- *
- * 缩写:mbr
- */
-package cn.iocoder.yudao.coreservice.modules.member;

+ 0 - 19
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/service/user/MbrUserCoreService.java

@@ -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);
-
-}

+ 0 - 28
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/service/user/impl/MbrUserCoreServiceImpl.java

@@ -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);
-    }
-
-}

+ 5 - 8
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<String, Object> 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);

+ 0 - 7
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/tool/package-info.java

@@ -1,7 +0,0 @@
-/**
- * tool 包下,我们放研发工具,提升研发效率与质量。
- * 例如说:代码生成器、接口文档等等
- *
- * 缩写:tool
- */
-package cn.iocoder.yudao.coreservice.modules.tool;

+ 1 - 1
yudao-dependencies/pom.xml

@@ -14,7 +14,7 @@
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
     <properties>
-        <revision>1.3.0-snapshot</revision>
+        <revision>1.4.0-snapshot</revision>
         <!-- 统一依赖管理 -->
         <spring.boot.version>2.4.12</spring.boot.version>
         <!-- Web 相关 -->

+ 6 - 0
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());
+    }
+
 }

+ 1 - 0
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,

+ 3 - 0
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 = "操作模块不能为空")

+ 2 - 1
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 {
 

+ 18 - 5
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<SecurityAuthFrameworkService> securityFrameworkServices,
+            WebProperties webProperties, PasswordEncoder passwordEncoder) {
+        return new MultiUserDetailsAuthenticationProvider(securityFrameworkServices, webProperties, passwordEncoder);
     }
 
     /**

+ 21 - 21
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<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer;
+    private Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.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;
     }
 
 }

+ 149 - 0
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<UserTypeEnum, SecurityAuthFrameworkService> services = new HashMap<>();
+
+    private final WebProperties properties;
+
+    private final PasswordEncoder passwordEncoder;
+
+    public MultiUserDetailsAuthenticationProvider(List<SecurityAuthFrameworkService> 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()));
+    }
+
+}

+ 43 - 0
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<? extends GrantedAuthority> 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<? extends GrantedAuthority> authorities, UserTypeEnum userType) {
+        super(principal, credentials, authorities);
+        this.userType = userType;
+    }
+
+}

+ 10 - 11
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);
     }
 
 }

+ 0 - 53
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/AbstractSignUpUrlAuthenticationSuccessHandler.java

@@ -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;
-    }
-}

+ 4 - 6
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.common.util.servlet.ServletUtils;
 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
-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.common.util.servlet.ServletUtils;
 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));

+ 9 - 1
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();
+
 }

+ 7 - 3
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<String, Object> requestParams = MapUtil.<String, Object>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));

+ 34 - 21
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;
-
-    /**
-     * Controller 所在包
-     *
-     * 主要目的是,给该 Controller 设置指定的 {@link #apiPrefix}
-     *
-     * 因为我们有多个 modules 包里会包含 Controller,所以只需要写到 cn.iocoder.yudao 这样的层级
-     */
-    @NotNull(message = "Controller 所在包不能为空")
-    private String controllerPackage;
+    @NotNull(message = "APP API 不能为空")
+    private Api appApi;
+    @NotNull(message = "Admin API 不能为空")
+    private Api adminApi;
+
+    @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;
+
+    }
 
 }

+ 13 - 4
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

+ 5 - 0
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);

+ 16 - 3
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());
         };
     }
 

+ 24 - 0
yudao-module-member/pom.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <modules>
+        <module>yudao-module-member-api</module>
+        <module>yudao-module-member-impl</module>
+    </modules>
+    <artifactId>yudao-module-member</artifactId>
+    <packaging>pom</packaging>
+
+    <name>${artifactId}</name>
+    <description>
+        member 模块,我们放会员业务。
+        例如说:会员中心等等
+    </description>
+
+</project>

+ 26 - 0
yudao-module-member/yudao-module-member-api/pom.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao-module-member</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-module-member-api</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${artifactId}</name>
+    <description>
+        member 模块 API,暴露给其它模块调用
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-common</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 4 - 0
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;

+ 20 - 0
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);
+
+}

+ 32 - 0
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;
+
+}

+ 101 - 0
yudao-module-member/yudao-module-member-impl/pom.xml

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao-module-member</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-module-member-impl</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${artifactId}</name>
+    <description>
+        member 模块,我们放会员业务。
+        例如说:会员中心等等
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-member-api</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
+        <!-- 业务组件 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-core-service</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-biz-sms</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-biz-weixin</artifactId>
+        </dependency>
+
+        <!-- Web 相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <!-- DB 相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-redis</artifactId>
+        </dependency>
+
+        <!-- 消息队列相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-mq</artifactId>
+        </dependency>
+
+        <!-- Test 测试相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- 工具类相关 -->
+
+    </dependencies>
+
+    <build>
+        <!-- 设置构建的 jar 包名 -->
+        <finalName>${artifactId}</finalName>
+        <plugins>
+            <!-- 打包 -->
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <fork>true</fork>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 1 - 0
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;

+ 30 - 0
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);
+    }
+
+}

+ 1 - 0
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;

+ 1 - 0
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;

+ 1 - 0
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;

+ 2 - 1
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.http → 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",

+ 31 - 43
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java → 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<SysAuthLoginRespVO> login(@RequestBody @Valid SysAuthLoginReqVO reqVO) {
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
+    public CommonResult<AppAuthLoginRespVO> 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<SysAuthLoginRespVO> smsLogin(@RequestBody @Valid SysAuthSmsLoginReqVO reqVO) {
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
+    public CommonResult<AppAuthLoginRespVO> 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<Boolean> sendSmsCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) {
+    @ApiOperation(value = "发送手机验证码")
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
+    public CommonResult<Boolean> 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<Boolean> 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<Boolean> sendSmsCodeLogin() {
         smsCodeService.sendSmsCodeLogin(getLoginUserId());
@@ -80,7 +75,8 @@ public class SysAuthController {
     @PostMapping("/reset-password")
     @ApiOperation(value = "重置密码", notes = "用户忘记密码时使用")
     @PreAuthenticated
-    public CommonResult<Boolean> resetPassword(@RequestBody @Valid MbrAuthResetPasswordReqVO reqVO) {
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
+    public CommonResult<Boolean> 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<Boolean> updatePassword(@RequestBody @Valid MbrAuthUpdatePasswordReqVO reqVO) {
+    public CommonResult<Boolean> updatePassword(@RequestBody @Valid AppAuthUpdatePasswordReqVO reqVO) {
         authService.updatePassword(getLoginUserId(), reqVO);
         return success(true);
     }
 
-    @PostMapping("/check-sms-code")
-    @ApiOperation(value = "校验验证码是否正确")
-    @PreAuthenticated
-    public CommonResult<Boolean> 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<SysAuthLoginRespVO> socialLogin(@RequestBody @Valid MbrAuthSocialLoginReqVO reqVO) {
+    public CommonResult<AppAuthLoginRespVO> 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<SysAuthLoginRespVO> socialLogin2(@RequestBody @Valid MbrAuthSocialLogin2ReqVO reqVO) {
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
+    public CommonResult<AppAuthLoginRespVO> 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<Boolean> socialBind(@RequestBody @Valid MbrAuthSocialBindReqVO reqVO) {
+    @PreAuthenticated
+    public CommonResult<Boolean> socialBind(@RequestBody @Valid AppAuthSocialBindReqVO reqVO) {
         authService.socialBind(getLoginUserId(), reqVO);
         return CommonResult.success(true);
     }
 
     @DeleteMapping("/social-unbind")
     @ApiOperation("取消社交绑定")
-    public CommonResult<Boolean> socialUnbind(@RequestBody MbrAuthSocialUnbindReqVO reqVO) {
+    @PreAuthenticated
+    public CommonResult<Boolean> socialUnbind(@RequestBody AppAuthSocialUnbindReqVO reqVO) {
         socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId(), UserTypeEnum.MEMBER);
         return CommonResult.success(true);
     }

+ 42 - 0
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;
+
+}

+ 3 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthLoginReqVO.java → 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 = "手机号不能为空")

+ 3 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthLoginRespVO.java → 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;

+ 11 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java → 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;
+
 }

+ 4 - 4
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSendSmsReqVO.java → 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

+ 3 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSmsLoginReqVO.java → 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 = "手机号不能为空")

+ 3 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLoginReqVO.java → 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)

+ 3 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLogin2ReqVO.java → 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)

+ 3 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialBindReqVO.java → 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)

+ 3 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialUnbindReqVO.java → 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)

+ 4 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthUpdatePasswordReqVO.java → 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 = "旧密码不能为空")

+ 1 - 0
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;

+ 0 - 0
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.http → yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.http


+ 72 - 0
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<Boolean> updateUserNickname(@RequestParam("nickname") String nickname) {
+        userService.updateUserNickname(getLoginUserId(), nickname);
+        return success(true);
+    }
+
+    @PutMapping("/update-avatar")
+    @ApiOperation("修改用户头像")
+    @PreAuthenticated
+    public CommonResult<String> 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<AppUserInfoRespVO> getUserInfo() {
+        UserDO user = userService.getUser(getLoginUserId());
+        return success(UserConvert.INSTANCE.convert(user));
+    }
+
+    @PostMapping("/update-mobile")
+    @ApiOperation(value = "修改用户手机")
+    @PreAuthenticated
+    public CommonResult<Boolean> updateMobile(@RequestBody @Valid AppUserUpdateMobileReqVO reqVO) {
+        userService.updateUserMobile(getLoginUserId(), reqVO);
+        return success(true);
+    }
+
+}
+

+ 3 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserInfoRespVO.java → 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;

+ 19 - 5
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java → 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;
+
 }

+ 6 - 0
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;

+ 6 - 6
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/auth/SysAuthConvert.java → 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());
     }

+ 1 - 1
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/convert/package-info.java → 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;

+ 17 - 0
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);
+}

+ 0 - 0
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md → yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md


+ 1 - 2
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/sms/SysSmsCodeDO.java → 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;
 

+ 3 - 3
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/member/dal/dataobject/user/MbrUserDO.java → 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

+ 6 - 4
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/sms/SysSmsCodeMapper.java → 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<SysSmsCodeDO> {
 
@@ -13,14 +14,15 @@ public interface SysSmsCodeMapper extends BaseMapperX<SysSmsCodeDO> {
      *
      * @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<SysSmsCodeDO>()
                 .eq("mobile", mobile)
                 .eqIfPresent("scene", scene)
+                .eqIfPresent("code", code)
                 .orderByDesc("id")
                 .last("LIMIT 1"));
     }
-
 }

+ 19 - 0
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<UserDO> {
+
+    default UserDO selectByMobile(String mobile) {
+        return selectOne(UserDO::getMobile, mobile);
+    }
+
+}

+ 9 - 0
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;

+ 4 - 0
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;

+ 3 - 2
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/enums/MbrErrorCodeConstants.java → 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, "文件为空");
+
 }

+ 2 - 2
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java → 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, "密码校验失败");
 }

+ 54 - 0
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;
+    }
+
+}

+ 25 - 0
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";
+
+}

+ 6 - 0
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;

+ 1 - 1
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/sms/SmsCodeConfiguration.java → 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;

+ 1 - 1
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/framework/sms/SmsCodeProperties.java → 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;

+ 1 - 0
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;

+ 11 - 17
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java → 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);
 }

+ 73 - 85
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java → 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<SysSocialUserDO> socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, USER_TYPE_ENUM);
+        List<SysSocialUserDO> 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());

+ 17 - 13
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/SysSmsCodeService.java → 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);
 }

+ 137 - 0
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;
+    }
+
+}

+ 12 - 20
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java → 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);
 
 }

+ 147 - 0
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;
+    }
+
+}

+ 25 - 30
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/service/SysAuthServiceTest.java → 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<MbrUserDO>... consumers) {
-        Consumer<MbrUserDO> consumer = (o) -> {
+    private static UserDO randomUserDO(Consumer<UserDO>... consumers) {
+        Consumer<UserDO> 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));
     }
 
 

+ 37 - 49
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/service/MbrUserServiceImplTest.java → 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<MbrUserDO>... consumers) {
-        Consumer<MbrUserDO> consumer = (o) -> {
+    private static UserDO randomUserDO(Consumer<UserDO>... consumers) {
+        Consumer<UserDO> 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));
     }
 
 }

+ 1 - 2
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/BaseDbAndRedisUnitTest.java → 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;

+ 1 - 1
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/BaseDbUnitTest.java → 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;

+ 1 - 1
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/config/RedisTestConfiguration.java → 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;

+ 5 - 0
yudao-user-server/src/test/resources/application-unit-test.yaml → 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 芋艿:要清理掉

+ 0 - 0
yudao-user-server/src/test/resources/file/erweima.jpg → yudao-module-member/yudao-module-member-impl/src/test/resources/file/erweima.jpg


+ 0 - 0
yudao-user-server/src/test/resources/logback-spring.xml → yudao-module-member/yudao-module-member-impl/src/test/resources/logback-spring.xml


+ 0 - 0
yudao-user-server/src/test/resources/sql/clean.sql → yudao-module-member/yudao-module-member-impl/src/test/resources/sql/clean.sql


+ 0 - 0
yudao-user-server/src/test/resources/sql/create_tables.sql → yudao-module-member/yudao-module-member-impl/src/test/resources/sql/create_tables.sql


+ 0 - 9
yudao-user-server/pom.xml

@@ -90,15 +90,6 @@
             <artifactId>yudao-spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
-
-        <!-- TODO @宋天:junit 已经在 yudao-spring-boot-starter-test 啦,不用在引入哈 -->
-        <!--单元测试相关-->
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-
         <!-- 工具类相关 -->
 
     </dependencies>

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.