diff --git a/pom.xml b/pom.xml
index 1962a92fd..84e886116 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,6 +11,7 @@
yudao-dependencies
yudao-framework
yudao-admin-server
+ yudao-user-server
${artifactId}
@@ -18,7 +19,7 @@
https://github.com/YunaiV/ruoyi-vue-pro
- 1.0.0
+ 1.1.0-snapshot
1.8
${java.version}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java
new file mode 100644
index 000000000..8bb4bc9a6
--- /dev/null
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.adminserver.framework.security;
+
+import cn.iocoder.yudao.framework.web.config.WebProperties;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
+
+import javax.annotation.Resource;
+
+@Configuration
+public class SecurityConfiguration {
+
+ @Resource
+ private WebProperties webProperties;
+
+ @Value("${spring.boot.admin.context-path:''}")
+ private String adminSeverContextPath;
+
+ @Bean
+ public Customizer.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer() {
+ return registry -> {
+ // 通用的接口,可匿名访问 TODO 芋艿:需要抽象出去
+ registry.antMatchers(api("/system/captcha/**")).anonymous();
+ // Spring Boot Admin Server 的安全配置 TODO 芋艿:需要抽象出去
+ registry.antMatchers(adminSeverContextPath).anonymous()
+ .antMatchers(adminSeverContextPath + "/**").anonymous();
+ // 短信回调 API
+ registry.antMatchers(api("/system/sms/callback/**")).anonymous();
+ };
+ }
+
+ private String api(String url) {
+ return webProperties.getApiPrefix() + url;
+ }
+
+}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java
index 8fae484ac..aba6382d2 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java
@@ -32,10 +32,9 @@ public class InfApiAccessLogServiceImpl implements InfApiAccessLogService {
@Override
@Async
public Future createApiAccessLogAsync(ApiAccessLogCreateDTO createDTO) {
- // 插入
InfApiAccessLogDO apiAccessLog = InfApiAccessLogConvert.INSTANCE.convert(createDTO);
int insert = apiAccessLogMapper.insert(apiAccessLog);
- return new AsyncResult<>(insert == 1);
+ return new AsyncResult<>(insert > 1);
}
@Override
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/user/SysUserController.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/user/SysUserController.java
index df54e7980..6de3f6b0c 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/user/SysUserController.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/user/SysUserController.java
@@ -167,7 +167,7 @@ public class SysUserController {
@PreAuthorize("@ss.hasPermission('system:user:import')")
public CommonResult importExcel(@RequestParam("file") MultipartFile file,
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
- List list = ExcelUtils.raed(file, SysUserImportExcelVO.class);
+ List list = ExcelUtils.read(file, SysUserImportExcelVO.class);
return success(userService.importUsers(list, updateSupport));
}
diff --git a/yudao-admin-ui/src/views/system/role/index.vue b/yudao-admin-ui/src/views/system/role/index.vue
index 0ced5d8df..7ab00f82b 100644
--- a/yudao-admin-ui/src/views/system/role/index.vue
+++ b/yudao-admin-ui/src/views/system/role/index.vue
@@ -66,7 +66,7 @@
-
diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml
index 42adcdf8a..0758e5e81 100644
--- a/yudao-dependencies/pom.xml
+++ b/yudao-dependencies/pom.xml
@@ -14,7 +14,7 @@
https://github.com/YunaiV/ruoyi-vue-pro
- 1.0.0
+ 1.1.0-snapshot
2.4.5
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
index 0e9bfb4de..67d558f6b 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
@@ -39,7 +39,7 @@ public class ExcelUtils {
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
}
- public static List raed(MultipartFile file, Class head) throws IOException {
+ public static List read(MultipartFile file, Class head) throws IOException {
return EasyExcel.read(file.getInputStream(), head, null)
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
.doReadAllSync();
diff --git a/yudao-framework/yudao-spring-boot-starter-security/pom.xml b/yudao-framework/yudao-spring-boot-starter-security/pom.xml
index 4a2b1878a..670a09774 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/pom.xml
+++ b/yudao-framework/yudao-spring-boot-starter-security/pom.xml
@@ -19,7 +19,12 @@
cn.iocoder.boot
yudao-common
- ${revision}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
index b9e0f34fc..94b3ecd14 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.security.config;
+import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect;
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;
@@ -32,6 +33,14 @@ public class YudaoSecurityAutoConfiguration {
@Resource
private SecurityProperties securityProperties;
+ /**
+ * 处理用户未登陆拦截的切面的 Bean
+ */
+ @Bean
+ public PreAuthenticatedAspect preAuthenticatedAspect() {
+ return new PreAuthenticatedAspect();
+ }
+
/**
* 认证失败处理类 Bean
*/
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
index 3d678eefd..893d2b0f5 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
@@ -14,10 +14,12 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@@ -41,9 +43,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
@Resource
private WebProperties webProperties;
- @Value("${spring.boot.admin.context-path:''}")
- private String adminSeverContextPath;
-
/**
* 自定义用户【认证】逻辑
*/
@@ -74,6 +73,13 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
*/
@Resource
private JwtAuthenticationTokenFilter authenticationTokenFilter;
+ /**
+ * 自定义的权限映射 Bean
+ *
+ * @see #configure(HttpSecurity)
+ */
+ @Resource
+ private Customizer.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer;
/**
* 由于 Spring Security 创建 AuthenticationManager 对象时,没声明 @Bean 注解,导致无法被注入
@@ -121,15 +127,16 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
.csrf().disable()
// 基于 token 机制,所以不需要 Session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
+ .headers().frameOptions().disable().and()
// 一堆自定义的 Spring Security 处理器
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler).and()
- // 设置每个请求的权限
- .authorizeRequests()
+ .logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); // 登出
+
+ // 设置每个请求的权限 ①:全局共享规则
+ httpSecurity.authorizeRequests()
// 登陆的接口,可匿名访问
.antMatchers(api("/login")).anonymous()
- // 通用的接口,可匿名访问 TODO 芋艿:需要抽象出去
- .antMatchers(api("/system/captcha/**")).anonymous()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
// 文件的获取接口,可匿名访问
@@ -139,21 +146,15 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
.antMatchers("/swagger-resources/**").anonymous()
.antMatchers("/webjars/**").anonymous()
.antMatchers("/*/api-docs").anonymous()
- // Spring Boot Admin Server 的安全配置 TODO 芋艿:需要抽象出去
- .antMatchers(adminSeverContextPath).anonymous()
- .antMatchers(adminSeverContextPath + "/**").anonymous()
// Spring Boot Actuator 的安全配置
.antMatchers("/actuator").anonymous()
.antMatchers("/actuator/**").anonymous()
- // Druid 监控 TODO 芋艿:需要抽象出去
+ // Druid 监控 TODO 芋艿:等对接了 druid admin 后,在调整下。
.antMatchers("/druid/**").anonymous()
- // 短信回调 API TODO 芋艿:需要抽象出去
- .antMatchers(api("/system/sms/callback/**")).anonymous()
- // 除上面外的所有请求全部需要鉴权认证
- .anyRequest().authenticated()
- .and()
- .headers().frameOptions().disable();
- httpSecurity.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler);
+ // 设置每个请求的权限 ②:每个项目的自定义规则
+ .and().authorizeRequests(authorizeRequestsCustomizer)
+ // 设置每个请求的权限 ③:兜底规则,必须认证
+ .authorizeRequests().anyRequest().authenticated();
// 添加 JWT Filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/annotations/PreAuthenticated.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/annotations/PreAuthenticated.java
new file mode 100644
index 000000000..901936dd7
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/annotations/PreAuthenticated.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.framework.security.core.annotations;
+
+import java.lang.annotation.*;
+
+/**
+ * 声明用户需要登陆
+ *
+ * 为什么不使用 {@link org.springframework.security.access.prepost.PreAuthorize} 注解,原因是不通过时,抛出的是认证不通过,而不是未登陆
+ *
+ * @author 芋道源码
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface PreAuthenticated {
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/aop/PreAuthenticatedAspect.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/aop/PreAuthenticatedAspect.java
new file mode 100644
index 000000000..808afc393
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/aop/PreAuthenticatedAspect.java
@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.framework.security.core.aop;
+
+import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+
+import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+
+@Aspect
+@Slf4j
+public class PreAuthenticatedAspect {
+
+ @Around("@annotation(preAuthenticated)")
+ public Object around(ProceedingJoinPoint joinPoint, PreAuthenticated preAuthenticated) throws Throwable {
+ if (SecurityFrameworkUtils.getLoginUser() == null) {
+ throw exception(UNAUTHORIZED);
+ }
+ return joinPoint.proceed();
+ }
+
+}
diff --git a/yudao-user-server/pom.xml b/yudao-user-server/pom.xml
new file mode 100644
index 000000000..4bd1c5857
--- /dev/null
+++ b/yudao-user-server/pom.xml
@@ -0,0 +1,111 @@
+
+
+
+ cn.iocoder.boot
+ yudao
+ ${revision}
+
+ 4.0.0
+
+ yudao-user-server
+ jar
+
+ yudao-admin-server
+ 用户前台 Server,提供其 API 接口
+ https://github.com/YunaiV/ruoyi-vue-pro
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-biz-dict
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-biz-sms
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-web
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-security
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-mybatis
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-redis
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-config
+
+
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-mq
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-protection
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-monitor
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+
+ ${artifactId}
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/UserServerApplication.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/UserServerApplication.java
new file mode 100644
index 000000000..edfbc63dc
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/UserServerApplication.java
@@ -0,0 +1,13 @@
+package cn.iocoder.yudao.userserver;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class UserServerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(UserServerApplication.class, args);
+ }
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/async/config/AsyncConfiguration.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/async/config/AsyncConfiguration.java
new file mode 100644
index 000000000..ed271220c
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/async/config/AsyncConfiguration.java
@@ -0,0 +1,9 @@
+package cn.iocoder.yudao.userserver.framework.async.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+@Configuration
+@EnableAsync
+public class AsyncConfiguration {
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/async/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/async/package-info.java
new file mode 100644
index 000000000..a06351522
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/async/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 异步执行,基于 Spring @Async 实现
+ */
+package cn.iocoder.yudao.userserver.framework.async;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/async/《芋道 Spring Boot 异步任务入门》.md b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/async/《芋道 Spring Boot 异步任务入门》.md
new file mode 100644
index 000000000..5822b838c
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/async/《芋道 Spring Boot 异步任务入门》.md
@@ -0,0 +1 @@
+
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/package-info.java
new file mode 100644
index 000000000..db25ed2a4
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 属于整个 yudao-user-server 的 framework 封装
+ *
+ * @author 芋道源码
+ */
+package cn.iocoder.yudao.userserver.framework;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/security/SecurityConfiguration.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/security/SecurityConfiguration.java
new file mode 100644
index 000000000..e43f5e3e6
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/security/SecurityConfiguration.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.userserver.framework.security;
+
+import cn.iocoder.yudao.framework.web.config.WebProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
+
+import javax.annotation.Resource;
+
+@Configuration
+public class SecurityConfiguration {
+
+ @Resource
+ private WebProperties webProperties;
+
+ @Bean
+ public Customizer.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer() {
+ return registry -> {
+ registry.antMatchers(api("/**")).anonymous(); // 默认 API 都是用户可访问
+ };
+ }
+
+ private String api(String url) {
+ return webProperties.getApiPrefix() + url;
+ }
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/controller/HelloController.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/controller/HelloController.java
new file mode 100644
index 000000000..6a3270fc2
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/controller/HelloController.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.userserver.modules.infra.controller;
+
+import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author weir
+ */
+@Slf4j
+@RestController
+public class HelloController {
+
+ @RequestMapping("/user/hello")
+ public String hello(String hello) {
+ return "echo + " + hello;
+ }
+
+ @RequestMapping("/user/info")
+ @PreAuthenticated
+ public String xx() {
+ return "none";
+ }
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/logger/InfApiAccessLogConvert.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/logger/InfApiAccessLogConvert.java
new file mode 100644
index 000000000..3ddbbf65b
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/logger/InfApiAccessLogConvert.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.userserver.modules.infra.convert.logger;
+
+import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiAccessLogCreateDTO;
+import cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.logger.InfApiAccessLogDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * API 访问日志 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfApiAccessLogConvert {
+
+ InfApiAccessLogConvert INSTANCE = Mappers.getMapper(InfApiAccessLogConvert.class);
+
+ InfApiAccessLogDO convert(ApiAccessLogCreateDTO bean);
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/logger/InfApiErrorLogConvert.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/logger/InfApiErrorLogConvert.java
new file mode 100644
index 000000000..51c6c3eef
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/logger/InfApiErrorLogConvert.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.userserver.modules.infra.convert.logger;
+
+import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiErrorLogCreateDTO;
+import cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.logger.InfApiErrorLogDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * API 错误日志 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfApiErrorLogConvert {
+
+ InfApiErrorLogConvert INSTANCE = Mappers.getMapper(InfApiErrorLogConvert.class);
+
+ InfApiErrorLogDO convert(ApiErrorLogCreateDTO bean);
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/package-info.java
new file mode 100644
index 000000000..d1aa5643a
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 提供 POJO 类的实体转换
+ *
+ * 目前使用 MapStruct 框架
+ */
+package cn.iocoder.yudao.userserver.modules.infra.convert;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md
new file mode 100644
index 000000000..8153487b7
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md
@@ -0,0 +1 @@
+
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/config/InfConfigDO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/config/InfConfigDO.java
new file mode 100644
index 000000000..a9ab432d6
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/config/InfConfigDO.java
@@ -0,0 +1,64 @@
+package cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.config;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * 参数配置表
+ *
+ * @author ruoyi
+ */
+@TableName("inf_config")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class InfConfigDO extends BaseDO {
+
+ /**
+ * 参数主键
+ */
+ @TableId
+ private Long id;
+ /**
+ * 参数分组
+ */
+ @TableField("`group`")
+ private String group;
+ /**
+ * 参数名称
+ */
+ private String name;
+ /**
+ * 参数键名
+ */
+ @TableField("`key`")
+ private String key;
+ /**
+ * 参数键值
+ */
+ private String value;
+ /**
+ * 参数类型
+ *
+ * 枚举 {@link InfConfigTypeEnum}
+ */
+ @TableField("`type`")
+ private Integer type;
+ /**
+ * 是否敏感
+ *
+ * 对于敏感配置,需要管理权限才能查看
+ */
+ @TableField("`sensitive`")
+ private Boolean sensitive;
+ /**
+ * 备注
+ */
+ private String remark;
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/file/InfFileDO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/file/InfFileDO.java
new file mode 100644
index 000000000..604a888c2
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/file/InfFileDO.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.file;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.io.InputStream;
+
+/**
+ * 文件表
+ *
+ * @author 芋道源码
+ */
+@Data
+@TableName("inf_file")
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfFileDO extends BaseDO {
+
+ /**
+ * 文件路径
+ */
+ @TableId(type = IdType.INPUT)
+ private String id;
+ /**
+ * 文件类型
+ *
+ * 通过 {@link cn.hutool.core.io.FileTypeUtil#getType(InputStream)} 获取
+ */
+ @TableField(value = "`type`")
+ private String type;
+ /**
+ * 文件内容
+ */
+ private byte[] content;
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/logger/InfApiAccessLogDO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/logger/InfApiAccessLogDO.java
new file mode 100644
index 000000000..2548ec469
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/logger/InfApiAccessLogDO.java
@@ -0,0 +1,107 @@
+package cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.logger;
+
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.util.Date;
+
+/**
+ * API 访问日志
+ *
+ * @author 芋道源码
+ */
+@TableName("inf_api_access_log")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfApiAccessLogDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Integer id;
+ /**
+ * 链路追踪编号
+ *
+ * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
+ */
+ private String traceId;
+ /**
+ * 用户编号
+ */
+ private Long userId;
+ /**
+ * 用户类型
+ *
+ * 枚举 {@link UserTypeEnum}
+ */
+ private Integer userType;
+ /**
+ * 应用名
+ *
+ * 目前读取 `spring.application.name` 配置项
+ */
+ private String applicationName;
+
+ // ========== 请求相关字段 ==========
+
+ /**
+ * 请求方法名
+ */
+ private String requestMethod;
+ /**
+ * 访问地址
+ */
+ private String requestUrl;
+ /**
+ * 请求参数
+ *
+ * query: Query String
+ * body: Quest Body
+ */
+ private String requestParams;
+ /**
+ * 用户 IP
+ */
+ private String userIp;
+ /**
+ * 浏览器 UA
+ */
+ private String userAgent;
+
+ // ========== 执行相关字段 ==========
+
+ /**
+ * 开始请求时间
+ */
+ private Date beginTime;
+ /**
+ * 结束请求时间
+ */
+ private Date endTime;
+ /**
+ * 执行时长,单位:毫秒
+ */
+ private Integer duration;
+ /**
+ * 结果码
+ *
+ * 目前使用的 {@link CommonResult#getCode()} 属性
+ */
+ private Integer resultCode;
+ /**
+ * 结果提示
+ *
+ * 目前使用的 {@link CommonResult#getMsg()} 属性
+ */
+ private String resultMsg;
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/logger/InfApiErrorLogDO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/logger/InfApiErrorLogDO.java
new file mode 100644
index 000000000..6ed2b3150
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/dataobject/logger/InfApiErrorLogDO.java
@@ -0,0 +1,152 @@
+package cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.logger;
+
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.userserver.modules.infra.enums.logger.InfApiErrorLogProcessStatusEnum;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.util.Date;
+
+/**
+ * API 异常数据
+ *
+ * @author 芋道源码
+ */
+@TableName("inf_api_error_log")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfApiErrorLogDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ private Long id;
+ /**
+ * 用户编号
+ */
+ private Long userId;
+ /**
+ * 链路追踪编号
+ *
+ * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
+ */
+ private String traceId;
+ /**
+ * 用户类型
+ *
+ * 枚举 {@link UserTypeEnum}
+ */
+ private Integer userType;
+ /**
+ * 应用名
+ *
+ * 目前读取 spring.application.name
+ */
+ private String applicationName;
+
+ // ========== 请求相关字段 ==========
+
+ /**
+ * 请求方法名
+ */
+ private String requestMethod;
+ /**
+ * 访问地址
+ */
+ private String requestUrl;
+ /**
+ * 请求参数
+ *
+ * query: Query String
+ * body: Quest Body
+ */
+ private String requestParams;
+ /**
+ * 用户 IP
+ */
+ private String userIp;
+ /**
+ * 浏览器 UA
+ */
+ private String userAgent;
+
+ // ========== 异常相关字段 ==========
+
+ /**
+ * 异常发生时间
+ */
+ private Date exceptionTime;
+ /**
+ * 异常名
+ *
+ * {@link Throwable#getClass()} 的类全名
+ */
+ private String exceptionName;
+ /**
+ * 异常导致的消息
+ *
+ * {@link cn.hutool.core.exceptions.ExceptionUtil#getMessage(Throwable)}
+ */
+ private String exceptionMessage;
+ /**
+ * 异常导致的根消息
+ *
+ * {@link cn.hutool.core.exceptions.ExceptionUtil#getRootCauseMessage(Throwable)}
+ */
+ private String exceptionRootCauseMessage;
+ /**
+ * 异常的栈轨迹
+ *
+ * {@link org.apache.commons.lang3.exception.ExceptionUtils#getStackTrace(Throwable)}
+ */
+ private String exceptionStackTrace;
+ /**
+ * 异常发生的类全名
+ *
+ * {@link StackTraceElement#getClassName()}
+ */
+ private String exceptionClassName;
+ /**
+ * 异常发生的类文件
+ *
+ * {@link StackTraceElement#getFileName()}
+ */
+ private String exceptionFileName;
+ /**
+ * 异常发生的方法名
+ *
+ * {@link StackTraceElement#getMethodName()}
+ */
+ private String exceptionMethodName;
+ /**
+ * 异常发生的方法所在行
+ *
+ * {@link StackTraceElement#getLineNumber()}
+ */
+ private Integer exceptionLineNumber;
+
+ // ========== 处理相关字段 ==========
+
+ /**
+ * 处理状态
+ *
+ * 枚举 {@link InfApiErrorLogProcessStatusEnum}
+ */
+ private Integer processStatus;
+ /**
+ * 处理时间
+ */
+ private Date processTime;
+ /**
+ * 处理用户编号
+ *
+ * 关联 cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserDO.SysUserDO#getId()
+ */
+ private Long processUserId;
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/config/InfConfigDAOImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/config/InfConfigDAOImpl.java
new file mode 100644
index 000000000..5453c05a2
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/config/InfConfigDAOImpl.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.userserver.modules.infra.dal.mysql.config;
+
+import cn.iocoder.yudao.framework.apollo.internals.ConfigFrameworkDAO;
+import cn.iocoder.yudao.framework.apollo.internals.dto.ConfigRespDTO;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
+
+import javax.sql.DataSource;
+import java.sql.ResultSet;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * ConfigFrameworkDAO 实现类
+ *
+ * @author 芋道源码
+ */
+public class InfConfigDAOImpl implements ConfigFrameworkDAO {
+
+ private final JdbcTemplate jdbcTemplate;
+
+ public InfConfigDAOImpl(String jdbcUrl, String username, String password) {
+ DataSource dataSource = new DriverManagerDataSource(jdbcUrl, username, password);
+ this.jdbcTemplate = new JdbcTemplate(dataSource);
+ }
+
+ @Override
+ public boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
+ return jdbcTemplate.query("SELECT id FROM inf_config WHERE update_time > ? LIMIT 1",
+ ResultSet::next, maxUpdateTime);
+ }
+
+ @Override
+ public List selectList() {
+ return jdbcTemplate.query("SELECT `key`, `value`, update_time, deleted FROM inf_config", new BeanPropertyRowMapper<>(ConfigRespDTO.class));
+ }
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/file/InfFileMapper.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/file/InfFileMapper.java
new file mode 100644
index 000000000..1db7d311b
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/file/InfFileMapper.java
@@ -0,0 +1,10 @@
+package cn.iocoder.yudao.userserver.modules.infra.dal.mysql.file;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.file.InfFileDO;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface InfFileMapper extends BaseMapperX {
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/logger/InfApiAccessLogMapper.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/logger/InfApiAccessLogMapper.java
new file mode 100644
index 000000000..cf7f6e92c
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/logger/InfApiAccessLogMapper.java
@@ -0,0 +1,16 @@
+package cn.iocoder.yudao.userserver.modules.infra.dal.mysql.logger;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.logger.InfApiAccessLogDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * API 访问日志 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfApiAccessLogMapper extends BaseMapperX {
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/logger/InfApiErrorLogMapper.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/logger/InfApiErrorLogMapper.java
new file mode 100644
index 000000000..9e9124267
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/dal/mysql/logger/InfApiErrorLogMapper.java
@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.userserver.modules.infra.dal.mysql.logger;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.logger.InfApiErrorLogDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * API 错误日志 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfApiErrorLogMapper extends BaseMapperX {
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/enums/logger/InfApiErrorLogProcessStatusEnum.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/enums/logger/InfApiErrorLogProcessStatusEnum.java
new file mode 100644
index 000000000..618d5d9be
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/enums/logger/InfApiErrorLogProcessStatusEnum.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.userserver.modules.infra.enums.logger;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * API 异常数据的处理状态
+ *
+ * @author 芋道源码
+ */
+@AllArgsConstructor
+@Getter
+public enum InfApiErrorLogProcessStatusEnum {
+
+ INIT(0, "未处理"),
+ DONE(1, "已处理"),
+ IGNORE(2, "已忽略");
+
+ /**
+ * 状态
+ */
+ private final Integer status;
+ /**
+ * 资源类型名
+ */
+ private final String name;
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/enums/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/enums/package-info.java
new file mode 100644
index 000000000..3a03078cb
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/enums/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 占位类,可以无视
+ */
+package cn.iocoder.yudao.userserver.modules.infra.enums;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/package-info.java
new file mode 100644
index 000000000..fdf89894e
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * infra 包下,我们放基础设施的运维与管理,支撑上层的通用与核心业务。
+ * 例如说:定时任务的管理、服务器的信息等等
+ *
+ * 缩写:inf
+ */
+package cn.iocoder.yudao.userserver.modules.infra;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/auth/SysAuthService.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/auth/SysAuthService.java
new file mode 100644
index 000000000..e9b530904
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/auth/SysAuthService.java
@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.userserver.modules.infra.service.auth;
+
+import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
+
+/**
+ * 认证 Service 接口
+ *
+ * 提供用户的账号密码登陆、token 的校验等认证相关的功能
+ *
+ * @author 芋道源码
+ */
+public interface SysAuthService extends SecurityAuthFrameworkService {
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/auth/impl/SysAuthServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/auth/impl/SysAuthServiceImpl.java
new file mode 100644
index 000000000..6ab606c59
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/auth/impl/SysAuthServiceImpl.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.userserver.modules.infra.service.auth.impl;
+
+import cn.iocoder.yudao.framework.security.core.LoginUser;
+import cn.iocoder.yudao.userserver.modules.infra.service.auth.SysAuthService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+/**
+ * Auth Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Slf4j
+public class SysAuthServiceImpl implements SysAuthService {
+
+ @Override
+ public LoginUser verifyTokenAndRefresh(String token) {
+ return null;
+ }
+
+ @Override
+ public LoginUser mockLogin(Long userId) {
+ return null;
+ }
+
+ @Override
+ public void logout(String token) {
+
+ }
+
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ return null;
+ }
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/InfApiAccessLogService.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/InfApiAccessLogService.java
new file mode 100644
index 000000000..bcc4f3a97
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/InfApiAccessLogService.java
@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.userserver.modules.infra.service.logger;
+
+import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;
+
+/**
+ * API 访问日志 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfApiAccessLogService extends ApiAccessLogFrameworkService {
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/InfApiErrorLogService.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/InfApiErrorLogService.java
new file mode 100644
index 000000000..2c221604e
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/InfApiErrorLogService.java
@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.userserver.modules.infra.service.logger;
+
+import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
+
+/**
+ * API 错误日志 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfApiErrorLogService extends ApiErrorLogFrameworkService {
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java
new file mode 100644
index 000000000..1b0f2611c
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.userserver.modules.infra.service.logger.impl;
+
+import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiAccessLogCreateDTO;
+import cn.iocoder.yudao.userserver.modules.infra.convert.logger.InfApiAccessLogConvert;
+import cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.logger.InfApiAccessLogDO;
+import cn.iocoder.yudao.userserver.modules.infra.dal.mysql.logger.InfApiAccessLogMapper;
+import cn.iocoder.yudao.userserver.modules.infra.service.logger.InfApiAccessLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.AsyncResult;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.concurrent.Future;
+
+/**
+ * API 访问日志 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+@Slf4j
+public class InfApiAccessLogServiceImpl implements InfApiAccessLogService {
+
+ @Resource
+ private InfApiAccessLogMapper apiAccessLogMapper;
+
+ @Override
+ public Future createApiAccessLogAsync(ApiAccessLogCreateDTO createDTO) {
+ InfApiAccessLogDO apiAccessLog = InfApiAccessLogConvert.INSTANCE.convert(createDTO);
+ int insert = apiAccessLogMapper.insert(apiAccessLog);
+ return new AsyncResult<>(insert > 1);
+ }
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/impl/InfApiErrorLogServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/impl/InfApiErrorLogServiceImpl.java
new file mode 100644
index 000000000..05bddb7d9
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/service/logger/impl/InfApiErrorLogServiceImpl.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.userserver.modules.infra.service.logger.impl;
+
+import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiErrorLogCreateDTO;
+import cn.iocoder.yudao.userserver.modules.infra.convert.logger.InfApiErrorLogConvert;
+import cn.iocoder.yudao.userserver.modules.infra.dal.dataobject.logger.InfApiErrorLogDO;
+import cn.iocoder.yudao.userserver.modules.infra.dal.mysql.logger.InfApiErrorLogMapper;
+import cn.iocoder.yudao.userserver.modules.infra.enums.logger.InfApiErrorLogProcessStatusEnum;
+import cn.iocoder.yudao.userserver.modules.infra.service.logger.InfApiErrorLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.AsyncResult;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.concurrent.Future;
+
+/**
+ * API 错误日志 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+@Slf4j
+public class InfApiErrorLogServiceImpl implements InfApiErrorLogService {
+
+ @Resource
+ private InfApiErrorLogMapper apiErrorLogMapper;
+
+ @Override
+ public Future createApiErrorLogAsync(ApiErrorLogCreateDTO createDTO) {
+ InfApiErrorLogDO apiErrorLog = InfApiErrorLogConvert.INSTANCE.convert(createDTO);
+ apiErrorLog.setProcessStatus(InfApiErrorLogProcessStatusEnum.INIT.getStatus());
+ int insert = apiErrorLogMapper.insert(apiErrorLog);
+ return new AsyncResult<>(insert == 1);
+ }
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/package-info.java
new file mode 100644
index 000000000..430f6c945
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.yudao.userserver.modules;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/dict/SysDictDataConvert.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/dict/SysDictDataConvert.java
new file mode 100644
index 000000000..02155f698
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/dict/SysDictDataConvert.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.userserver.modules.system.convert.dict;
+
+import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
+import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.Collection;
+import java.util.List;
+
+@Mapper
+public interface SysDictDataConvert {
+
+ SysDictDataConvert INSTANCE = Mappers.getMapper(SysDictDataConvert.class);
+
+ DictDataRespDTO convert02(SysDictDataDO bean);
+
+ List convertList03(Collection list);
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/package-info.java
new file mode 100644
index 000000000..65ad8dc88
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.yudao.userserver.modules.system.convert;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/dict/SysDictDataDO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/dict/SysDictDataDO.java
new file mode 100644
index 000000000..eacec758f
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/dict/SysDictDataDO.java
@@ -0,0 +1,54 @@
+package cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 字典数据表
+ *
+ * @author ruoyi
+ */
+@TableName("sys_dict_data")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SysDictDataDO extends BaseDO {
+
+ /**
+ * 字典数据编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 字典排序
+ */
+ private Integer sort;
+ /**
+ * 字典标签
+ */
+ private String label;
+ /**
+ * 字典值
+ */
+ private String value;
+ /**
+ * 字典类型
+ *
+ * 冗余 {@link SysDictDataDO#getDictType()}
+ */
+ private String dictType;
+ /**
+ * 状态
+ *
+ * 枚举 {@link CommonStatusEnum}
+ */
+ private Integer status;
+ /**
+ * 备注
+ */
+ private String remark;
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/package-info.java
new file mode 100644
index 000000000..0c99dcc95
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.yudao.userserver.modules.system.dal.dataobject;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/dict/SysDictDataMapper.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/dict/SysDictDataMapper.java
new file mode 100644
index 000000000..928a53f7d
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/dict/SysDictDataMapper.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.userserver.modules.system.dal.mysql.dict;
+
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Date;
+
+@Mapper
+public interface SysDictDataMapper extends BaseMapperX {
+
+ default SysDictDataDO selectByDictTypeAndValue(String dictType, String value) {
+ return selectOne(new QueryWrapper().eq("dict_type", dictType)
+ .eq("value", value));
+ }
+
+ default int selectCountByDictType(String dictType) {
+ return selectCount("dict_type", dictType);
+ }
+
+ default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
+ return selectOne(new QueryWrapper().select("id")
+ .gt("update_time", maxUpdateTime).last("LIMIT 1")) != null;
+ }
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/package-info.java
new file mode 100644
index 000000000..a1bdeadcd
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.yudao.userserver.modules.system.dal.mysql;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/SysDictDataService.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/SysDictDataService.java
new file mode 100644
index 000000000..2efffe19e
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/SysDictDataService.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.userserver.modules.system.dict;
+
+import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService;
+
+/**
+ * 字典数据 Service 接口
+ *
+ * @author ruoyi
+ */
+public interface SysDictDataService extends DictDataFrameworkService {
+
+ /**
+ * 初始化字典数据的本地缓存
+ */
+ void initLocalCache();
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/impl/SysDictDataServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/impl/SysDictDataServiceImpl.java
new file mode 100644
index 000000000..6e062eca7
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/impl/SysDictDataServiceImpl.java
@@ -0,0 +1,122 @@
+package cn.iocoder.yudao.userserver.modules.system.dict.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.userserver.modules.system.convert.dict.SysDictDataConvert;
+import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
+import cn.iocoder.yudao.userserver.modules.system.dal.mysql.dict.SysDictDataMapper;
+import cn.iocoder.yudao.userserver.modules.system.dict.SysDictDataService;
+import com.google.common.collect.ImmutableTable;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 字典数据 Service 实现类
+ *
+ * @author ruoyi
+ */
+@Service
+@Slf4j
+public class SysDictDataServiceImpl implements SysDictDataService {
+
+ /**
+ * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
+ * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
+ */
+ private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
+
+ /**
+ * 字典数据缓存,第二个 key 使用 label
+ *
+ * key1:字典类型 dictType
+ * key2:字典标签 label
+ */
+ private ImmutableTable labelDictDataCache;
+ /**
+ * 字典数据缓存,第二个 key 使用 value
+ *
+ * key1:字典类型 dictType
+ * key2:字典值 value
+ */
+ private ImmutableTable valueDictDataCache;
+ /**
+ * 缓存字典数据的最大更新时间,用于后续的增量轮询,判断是否有更新
+ */
+ private volatile Date maxUpdateTime;
+
+ @Resource
+ private SysDictDataMapper dictDataMapper;
+
+ @Override
+ @PostConstruct
+ public synchronized void initLocalCache() {
+ // 获取字典数据列表,如果有更新
+ List dataList = this.loadDictDataIfUpdate(maxUpdateTime);
+ if (CollUtil.isEmpty(dataList)) {
+ return;
+ }
+
+ // 构建缓存
+ ImmutableTable.Builder labelDictDataBuilder = ImmutableTable.builder();
+ ImmutableTable.Builder valueDictDataBuilder = ImmutableTable.builder();
+ dataList.forEach(dictData -> {
+ labelDictDataBuilder.put(dictData.getDictType(), dictData.getLabel(), dictData);
+ valueDictDataBuilder.put(dictData.getDictType(), dictData.getValue(), dictData);
+ });
+ labelDictDataCache = labelDictDataBuilder.build();
+ valueDictDataCache = valueDictDataBuilder.build();
+ assert dataList.size() > 0; // 断言,避免告警
+ maxUpdateTime = dataList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
+ log.info("[initLocalCache][缓存字典数据,数量为:{}]", dataList.size());
+ }
+
+ @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
+ public void schedulePeriodicRefresh() {
+ initLocalCache();
+ }
+
+ /**
+ * 如果字典数据发生变化,从数据库中获取最新的全量字典数据。
+ * 如果未发生变化,则返回空
+ *
+ * @param maxUpdateTime 当前字典数据的最大更新时间
+ * @return 字典数据列表
+ */
+ private List loadDictDataIfUpdate(Date maxUpdateTime) {
+ // 第一步,判断是否要更新。
+ if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
+ log.info("[loadDictDataIfUpdate][首次加载全量字典数据]");
+ } else { // 判断数据库中是否有更新的字典数据
+ if (!dictDataMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
+ return null;
+ }
+ log.info("[loadDictDataIfUpdate][增量加载全量字典数据]");
+ }
+ // 第二步,如果有更新,则从数据库加载所有字典数据
+ return dictDataMapper.selectList();
+ }
+
+ @Override
+ public DictDataRespDTO getDictDataFromCache(String type, String value) {
+ return SysDictDataConvert.INSTANCE.convert02(valueDictDataCache.get(type, value));
+ }
+
+ @Override
+ public DictDataRespDTO parseDictDataFromCache(String type, String label) {
+ return SysDictDataConvert.INSTANCE.convert02(labelDictDataCache.get(type, label));
+ }
+
+ @Override
+ public List listDictDatasFromCache(String type) {
+ return SysDictDataConvert.INSTANCE.convertList03(labelDictDataCache.row(type).values());
+ }
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/package-info.java
new file mode 100644
index 000000000..89fa9c162
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * system 包下,我们放通用业务,支撑上层的核心业务。
+ * 例如说:用户、部门、权限、数据字典等等
+ *
+ * 缩写:sys
+ */
+package cn.iocoder.yudao.userserver.modules.system;
diff --git a/yudao-user-server/src/main/resources/application-dev.yaml b/yudao-user-server/src/main/resources/application-dev.yaml
new file mode 100644
index 000000000..c21eeec80
--- /dev/null
+++ b/yudao-user-server/src/main/resources/application-dev.yaml
@@ -0,0 +1,140 @@
+server:
+ port: 28080
+
+--- #################### 数据库相关配置 ####################
+
+spring:
+ # 数据源配置项
+ autoconfigure:
+ exclude:
+ - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
+ datasource:
+ druid: # Druid 【监控】相关的全局配置
+ web-stat-filter:
+ enabled: true
+ stat-view-servlet:
+ enabled: true
+ allow: # 设置白名单,不填则允许所有访问
+ url-pattern: /druid/*
+ login-username: # 控制台管理用户名和密码
+ login-password:
+ filter:
+ stat:
+ enabled: true
+ log-slow-sql: true # 慢 SQL 记录
+ slow-sql-millis: 100
+ merge-sql: true
+ wall:
+ config:
+ multi-statement-allow: true
+ dynamic: # 多数据源配置
+ druid: # Druid 【连接池】相关的全局配置
+ initial-size: 5 # 初始连接数
+ min-idle: 10 # 最小连接池数量
+ max-active: 20 # 最大连接池数量
+ max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+ time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+ min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+ max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+ validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+ test-while-idle: true
+ test-on-borrow: false
+ test-on-return: false
+ primary: master
+ datasource:
+ master:
+ name: ruoyi-vue-pro
+ url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
+ driver-class-name: com.mysql.jdbc.Driver
+ username: root
+ password: 3WLiVUBEwTbvAfsh
+ slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
+ name: ruoyi-vue-pro
+ url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
+ driver-class-name: com.mysql.jdbc.Driver
+ username: root
+ password: 3WLiVUBEwTbvAfsh
+
+ # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+ redis:
+ host: 400-infra.server.iocoder.cn # 地址
+ port: 6379 # 端口
+ database: 1 # 数据库索引
+
+--- #################### 定时任务相关配置 ####################
+
+--- #################### 配置中心相关配置 ####################
+
+# Apollo 配置中心
+apollo:
+ bootstrap:
+ enabled: true # 设置 Apollo 在启动阶段生效
+ eagerLoad:
+ enabled: true # 设置 Apollo 在日志初始化前生效,可以实现日志的动态级别配置
+ jdbc: # 自定义的 JDBC 配置项,用于数据库的地址
+ dao: cn.iocoder.yudao.userserver.modules.infra.dal.mysql.config.InfConfigDAOImpl
+ url: ${spring.datasource.dynamic.datasource.master.url}
+ username: ${spring.datasource.dynamic.datasource.master.username}
+ password: ${spring.datasource.dynamic.datasource.master.password}
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项
+lock4j:
+ acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
+ expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
+
+# Resilience4j 配置项
+resilience4j:
+ ratelimiter:
+ instances:
+ backendA:
+ limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50
+ limit-refresh-period: 60s # 每个周期的时长,单位:微秒。默认为 500
+ timeout-duration: 1s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s
+ register-health-indicator: true # 是否注册到健康监测
+
+--- #################### 监控相关配置 ####################
+
+# Actuator 监控端点的配置项
+management:
+ endpoints:
+ web:
+ base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
+ exposure:
+ include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+
+# Spring Boot Admin 配置项
+spring:
+ boot:
+ admin:
+ # Spring Boot Admin Client 客户端的相关配置
+ client:
+ url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址
+ instance:
+ prefer-ip: true # 注册实例时,优先使用 IP
+
+# 日志文件配置
+logging:
+ file:
+ name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
+
+--- #################### 芋道相关配置 ####################
+
+# 芋道配置项,设置当前项目所有自定义的配置
+yudao:
+ security:
+ token-header: Authorization
+ token-secret: abcdefghijklmnopqrstuvwxyz
+ token-timeout: 1d
+ session-timeout: 30m
+ mock-enable: true
+ mock-secret: test
+ file:
+ base-path: http://api-dashboard.yudao.iocoder.cn${yudao.web.api-prefix}/infra/file/get/
+ xss:
+ enable: false
+ exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系
+ - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
+ - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
+ demo: true # 开启演示模式
diff --git a/yudao-user-server/src/main/resources/application-local.yaml b/yudao-user-server/src/main/resources/application-local.yaml
new file mode 100644
index 000000000..62a53cf07
--- /dev/null
+++ b/yudao-user-server/src/main/resources/application-local.yaml
@@ -0,0 +1,142 @@
+server:
+ port: 28080
+
+--- #################### 数据库相关配置 ####################
+
+spring:
+ # 数据源配置项
+ autoconfigure:
+ exclude:
+ - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
+ datasource:
+ druid: # Druid 【监控】相关的全局配置
+ web-stat-filter:
+ enabled: true
+ stat-view-servlet:
+ enabled: true
+ allow: # 设置白名单,不填则允许所有访问
+ url-pattern: /druid/*
+ login-username: # 控制台管理用户名和密码
+ login-password:
+ filter:
+ stat:
+ enabled: true
+ log-slow-sql: true # 慢 SQL 记录
+ slow-sql-millis: 100
+ merge-sql: true
+ wall:
+ config:
+ multi-statement-allow: true
+ dynamic: # 多数据源配置
+ druid: # Druid 【连接池】相关的全局配置
+ initial-size: 5 # 初始连接数
+ min-idle: 10 # 最小连接池数量
+ max-active: 20 # 最大连接池数量
+ max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+ time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+ min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+ max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+ validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+ test-while-idle: true
+ test-on-borrow: false
+ test-on-return: false
+ primary: master
+ datasource:
+ master:
+ name: ruoyi-vue-pro
+ url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
+ driver-class-name: com.mysql.jdbc.Driver
+ username: root
+ password: 123456
+ slave: # 模拟从库,可根据自己需要修改
+ name: ruoyi-vue-pro
+ url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
+ driver-class-name: com.mysql.jdbc.Driver
+ username: root
+ password: 123456
+
+ # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+ redis:
+ host: 127.0.0.1 # 地址
+ port: 6379 # 端口
+ database: 0 # 数据库索引
+
+--- #################### 定时任务相关配置 ####################
+
+--- #################### 配置中心相关配置 ####################
+
+# Apollo 配置中心
+apollo:
+ bootstrap:
+ enabled: true # 设置 Apollo 在启动阶段生效
+ eagerLoad:
+ enabled: true # 设置 Apollo 在日志初始化前生效,可以实现日志的动态级别配置
+ jdbc: # 自定义的 JDBC 配置项,用于数据库的地址
+ dao: cn.iocoder.yudao.userserver.modules.infra.dal.mysql.config.InfConfigDAOImpl
+ url: ${spring.datasource.dynamic.datasource.master.url}
+ username: ${spring.datasource.dynamic.datasource.master.username}
+ password: ${spring.datasource.dynamic.datasource.master.password}
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项
+lock4j:
+ acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
+ expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
+
+# Resilience4j 配置项
+resilience4j:
+ ratelimiter:
+ instances:
+ backendA:
+ limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50
+ limit-refresh-period: 60s # 每个周期的时长,单位:微秒。默认为 500
+ timeout-duration: 1s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s
+ register-health-indicator: true # 是否注册到健康监测
+
+--- #################### 监控相关配置 ####################
+
+# Actuator 监控端点的配置项
+management:
+ endpoints:
+ web:
+ base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
+ exposure:
+ include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+
+# Spring Boot Admin 配置项
+spring:
+ boot:
+ admin:
+ # Spring Boot Admin Client 客户端的相关配置
+ client:
+ url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址
+ instance:
+ prefer-ip: true # 注册实例时,优先使用 IP
+ # Spring Boot Admin Server 服务端的相关配置
+ context-path: /admin # 配置 Spring
+
+# 日志文件配置
+logging:
+ file:
+ name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
+
+--- #################### 芋道相关配置 ####################
+
+# 芋道配置项,设置当前项目所有自定义的配置
+yudao:
+ security:
+ token-header: Authorization
+ token-secret: abcdefghijklmnopqrstuvwxyz
+ token-timeout: 1d
+ session-timeout: 30m
+ mock-enable: true
+ mock-secret: test
+ file:
+ base-path: http://127.0.0.1:${server.port}${yudao.web.api-prefix}/infra/file/get/
+ xss:
+ enable: false
+ exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系
+ - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
+ - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
+ demo: false # 关闭演示模式
diff --git a/yudao-user-server/src/main/resources/application.yaml b/yudao-user-server/src/main/resources/application.yaml
new file mode 100644
index 000000000..573a9a5e1
--- /dev/null
+++ b/yudao-user-server/src/main/resources/application.yaml
@@ -0,0 +1,60 @@
+spring:
+ application:
+ name: yudao-user-server
+
+ profiles:
+ active: local
+
+ # Servlet 配置
+ servlet:
+ # 文件上传相关配置项
+ multipart:
+ max-file-size: 16MB # 单个文件大小
+ max-request-size: 32MB # 设置总上传的文件大小
+
+ # Jackson 配置项
+ jackson:
+ serialization:
+ write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳
+ write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
+ write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
+ fail-on-empty-beans: false # 允许序列化无属性的 Bean
+
+# MyBatis Plus 的配置项
+mybatis-plus:
+ configuration:
+ map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
+ log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志
+ global-config:
+ db-config:
+ id-type: AUTO # 自增 ID
+ logic-delete-value: 1 # 逻辑已删除值(默认为 1)
+ logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
+ mapper-locations: classpath*:mapper/*.xml
+ type-aliases-package: ${yudao.info.base-package}.modules.*.dal.dataobject
+
+--- #################### 芋道相关配置 ####################
+
+yudao:
+ info:
+ version: 1.0.0
+ base-package: cn.iocoder.yudao.userserver
+ web:
+ api-prefix: /api
+ controller-package: ${yudao.info.base-package}
+ swagger:
+ title: 管理后台
+ description: 提供管理员管理的所有功能
+ version: ${yudao.info.version}
+ base-package: ${yudao.info.base-package}.modules
+ captcha:
+ timeout: 5m
+ width: 160
+ height: 60
+ codegen:
+ base-package: ${yudao.info.base-package}
+ db-schemas: ${spring.datasource.dynamic.datasource.master.name}
+ error-code: # 错误码相关配置项
+ constants-class-list:
+
+debug: false
diff --git a/yudao-user-server/src/main/resources/banner.txt b/yudao-user-server/src/main/resources/banner.txt
new file mode 100644
index 000000000..39a441d7d
--- /dev/null
+++ b/yudao-user-server/src/main/resources/banner.txt
@@ -0,0 +1,17 @@
+芋道源码 http://www.iocoder.cn
+Application Version: ${yudao.info.version}
+Spring Boot Version: ${spring-boot.version}
+
+.__ __. ______ .______ __ __ _______
+| \ | | / __ \ | _ \ | | | | / _____|
+| \| | | | | | | |_) | | | | | | | __
+| . ` | | | | | | _ < | | | | | | |_ |
+| |\ | | `--' | | |_) | | `--' | | |__| |
+|__| \__| \______/ |______/ \______/ \______|
+
+███╗ ██╗ ██████╗ ██████╗ ██╗ ██╗ ██████╗
+████╗ ██║██╔═══██╗ ██╔══██╗██║ ██║██╔════╝
+██╔██╗ ██║██║ ██║ ██████╔╝██║ ██║██║ ███╗
+██║╚██╗██║██║ ██║ ██╔══██╗██║ ██║██║ ██║
+██║ ╚████║╚██████╔╝ ██████╔╝╚██████╔╝╚██████╔╝
+╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝
diff --git a/yudao-user-server/src/main/resources/logback-spring.xml b/yudao-user-server/src/main/resources/logback-spring.xml
new file mode 100644
index 000000000..5bc181fd2
--- /dev/null
+++ b/yudao-user-server/src/main/resources/logback-spring.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ ${PATTERN_DEFAULT}
+
+
+
+
+
+
+
+
+
+ ${PATTERN_DEFAULT}
+
+
+
+ ${LOG_FILE}
+
+
+ ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}
+
+ ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}
+
+ ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}
+
+ ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}
+
+ ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}
+
+
+
+
+
+ 0
+
+ 256
+
+
+
+
+
+
+
+ ${PATTERN_DEFAULT}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+