Merge remote-tracking branch 'origin/main'
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -39,4 +39,9 @@ build/
|
||||
|
||||
|
||||
### mybatis plus generator
|
||||
/generator/
|
||||
/generator/
|
||||
|
||||
### Uploads ###
|
||||
/uploads/
|
||||
### Logs ###
|
||||
/logs/
|
||||
@@ -6,7 +6,7 @@ import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@FeignClient(name = "user-service", path = "/api/v1/user")
|
||||
@FeignClient(name = "aioj-user-service", path = "/api/v1/user")
|
||||
public interface UserClient {
|
||||
|
||||
@GetMapping("/inner/get-by-username")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
spring:
|
||||
application:
|
||||
name: auth-service
|
||||
name: aioj-auth-service
|
||||
data:
|
||||
redis:
|
||||
host: 10.0.0.10
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
spring:
|
||||
application:
|
||||
name: auth-service
|
||||
name: aioj-auth-service
|
||||
profiles:
|
||||
active: @env@
|
||||
devtools:
|
||||
@@ -36,4 +36,7 @@ jwt:
|
||||
enabled: true
|
||||
secret: "12345678901234567890123456789012" # 至少32字节!!
|
||||
access-expire: 900000 # 24小时
|
||||
refresh-expire: 604800000 # 7天
|
||||
refresh-expire: 604800000 # 7天
|
||||
logging:
|
||||
file:
|
||||
path: ./logs/${spring.application.name}
|
||||
|
||||
110
aioj-backend-auth/src/main/resources/logback-spring.xml
Normal file
110
aioj-backend-auth/src/main/resources/logback-spring.xml
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--debug="false" 表示关闭 Logback 框架自身的内部状态信息打印。-->
|
||||
<configuration scan="true" scanPeriod="10 seconds" debug="false">
|
||||
<!-- 引入 spring boot 默认日志颜色和基础配置-->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
<!-- 定义变量 APP_NAME,从 Spring 环境变量中获取 spring.application.name 的值-->
|
||||
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
|
||||
|
||||
<!-- 定义时间格式,yyyy-MM 表示年-月,yyyy-MM-dd 表示年-月-日-->
|
||||
<timestamp key="time-month" datePattern="yyyy-MM"/>
|
||||
<timestamp key="time-month-day" datePattern="yyyy-MM-dd"/>
|
||||
<!-- 定义变量 LOG_FILE_PATH,默认值为 ./logs/${APP_NAME},可以通过环境变量 LOG_PATH 覆盖 日志存储路径-->
|
||||
<property name="LOG_FILE_PATH" value="${LOG_PATH:-./logs/${APP_NAME}}"/>
|
||||
<!-- 定义日志格式
|
||||
格式说明:
|
||||
%d{yyyy-MM-dd HH:mm:ss.SSS}:日志记录时间,格式为年-月-日 时:分:秒.毫秒
|
||||
[%thread]:日志记录线程名称
|
||||
%-5level:日志级别,左对齐,占用 5 个字符宽度
|
||||
%logger{50}:日志记录器名称,最多显示 50 个字符
|
||||
-[%X{traceId:-}]:从 MDC 中获取 traceId 变量值,如果不存在则显示为空
|
||||
%msg%n:日志消息内容,换行符
|
||||
-->
|
||||
<property name="FILE_LOG_PATTERN"
|
||||
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -[%X{traceId:-}] %msg%n"/>
|
||||
|
||||
<!-- appender 控制台输出-->
|
||||
<!--
|
||||
<appender>: 这是 Logback 配置的根标签之一,用于定义一个日志输出目的地。
|
||||
name="CONSOLE": 为这个 Appender 赋予一个唯一的名称,方便在 <root> 或 <logger> 标签中引用它。
|
||||
class="ch.qos.logback.core.ConsoleAppender": 指定使用的实现类。
|
||||
ConsoleAppender 是 Logback 库中专门用于将日志事件写入 System.out (标准输出) 或 System.err (标准错误) 的类。
|
||||
这是我们在本地开发和测试时最常用的 Appender。
|
||||
CONSOLE_LOG_PATTERN 是上面include的默认日志格式,这里直接引用即可
|
||||
-->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 全量info日志-->
|
||||
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
<maxHistory>31</maxHistory>
|
||||
<totalSizeCap>100GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<!-- 只记录 INFO 级别以及以上的日志的日志 -->
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
</appender>
|
||||
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
<maxHistory>31</maxHistory>
|
||||
<totalSizeCap>100GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 只记录 ERROR 级别以及以上的日志的日志 -->
|
||||
<level>ERROR</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 异步写入日志-->
|
||||
<appender name="ASYNC_INFO"
|
||||
class="ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<appender-ref ref="INFO_FILE"/>
|
||||
</appender>
|
||||
<appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<appender-ref ref="ERROR_FILE"/>
|
||||
</appender>
|
||||
|
||||
|
||||
<!-- =========== 环境配置 打印到控制台 ===========-->
|
||||
<springProfile name="dev">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="ASYNC_ERROR"/>
|
||||
<appender-ref ref="ASYNC_INFO"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="prod">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="ASYNC_ERROR"/>
|
||||
<appender-ref ref="ASYNC_INFO"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
</configuration>
|
||||
@@ -1,6 +1,6 @@
|
||||
spring:
|
||||
application:
|
||||
name: file-service
|
||||
name: aioj-file-service
|
||||
profiles:
|
||||
active: @env@
|
||||
servlet:
|
||||
|
||||
111
aioj-backend-file-service/src/main/resources/logback-spring.xml
Normal file
111
aioj-backend-file-service/src/main/resources/logback-spring.xml
Normal file
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--debug="false" 表示关闭 Logback 框架自身的内部状态信息打印。-->
|
||||
<configuration scan="true" scanPeriod="10 seconds" debug="false">
|
||||
<!-- 引入 spring boot 默认日志颜色和基础配置-->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
<!-- 定义变量 APP_NAME,从 Spring 环境变量中获取 spring.application.name 的值-->
|
||||
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
|
||||
|
||||
<!-- 定义时间格式,yyyy-MM 表示年-月,yyyy-MM-dd 表示年-月-日-->
|
||||
<timestamp key="time-month" datePattern="yyyy-MM"/>
|
||||
<timestamp key="time-month-day" datePattern="yyyy-MM-dd"/>
|
||||
<!-- 定义变量 LOG_FILE_PATH,默认值为 ./logs/${APP_NAME},可以通过环境变量 LOG_PATH 覆盖 日志存储路径-->
|
||||
<property name="LOG_FILE_PATH" value="${LOG_PATH:-./logs/${APP_NAME}}"/>
|
||||
<!-- 定义日志格式
|
||||
格式说明:
|
||||
%d{yyyy-MM-dd HH:mm:ss.SSS}:日志记录时间,格式为年-月-日 时:分:秒.毫秒
|
||||
[%thread]:日志记录线程名称
|
||||
%-5level:日志级别,左对齐,占用 5 个字符宽度
|
||||
%logger{50}:日志记录器名称,最多显示 50 个字符
|
||||
-[%X{traceId:-}]:从 MDC 中获取 traceId 变量值,如果不存在则显示为空
|
||||
%msg%n:日志消息内容,换行符
|
||||
-->
|
||||
<property name="FILE_LOG_PATTERN"
|
||||
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -[%X{traceId:-}] %msg%n"/>
|
||||
|
||||
<!-- appender 控制台输出-->
|
||||
<!--
|
||||
<appender>: 这是 Logback 配置的根标签之一,用于定义一个日志输出目的地。
|
||||
name="CONSOLE": 为这个 Appender 赋予一个唯一的名称,方便在 <root> 或 <logger> 标签中引用它。
|
||||
class="ch.qos.logback.core.ConsoleAppender": 指定使用的实现类。
|
||||
ConsoleAppender 是 Logback 库中专门用于将日志事件写入 System.out (标准输出) 或 System.err (标准错误) 的类。
|
||||
这是我们在本地开发和测试时最常用的 Appender。
|
||||
CONSOLE_LOG_PATTERN 是上面include的默认日志格式,这里直接引用即可
|
||||
-->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 全量info日志-->
|
||||
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
<maxHistory>31</maxHistory>
|
||||
<totalSizeCap>100GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<!-- 只记录 INFO 级别以及以上的日志的日志 -->
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
</appender>
|
||||
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
<maxHistory>31</maxHistory>
|
||||
<totalSizeCap>100GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 只记录 ERROR 级别以及以上的日志的日志 -->
|
||||
<level>ERROR</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 异步写入日志-->
|
||||
<appender name="ASYNC_INFO"
|
||||
class="ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<appender-ref ref="INFO_FILE"/>
|
||||
</appender>
|
||||
<appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<appender-ref ref="ERROR_FILE"/>
|
||||
</appender>
|
||||
|
||||
|
||||
<!-- =========== 环境配置 打印到控制台 ===========-->
|
||||
<springProfile name="dev">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="ASYNC_ERROR"/>
|
||||
<appender-ref ref="ASYNC_INFO"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="prod">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="ASYNC_ERROR"/>
|
||||
<appender-ref ref="ASYNC_INFO"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
</configuration>
|
||||
@@ -105,7 +105,7 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
|
||||
private Mono<Boolean> validateToken(String token) {
|
||||
return webClientBuilder.build()
|
||||
.post()
|
||||
.uri("lb://auth-service/api/v1/auth/validate")
|
||||
.uri("lb://aioj-auth-service/api/v1/auth/validate")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.retrieve()
|
||||
|
||||
@@ -2,8 +2,9 @@ server:
|
||||
port: 18085
|
||||
error:
|
||||
include-stacktrace: never
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: aioj-backend-gateway
|
||||
profiles:
|
||||
active: @env@
|
||||
cloud:
|
||||
@@ -13,28 +14,28 @@ spring:
|
||||
routes:
|
||||
# auth服务 Swagger 文档路由
|
||||
- id: auth-service-doc
|
||||
uri: lb://auth-service
|
||||
uri: lb://aioj-auth-service
|
||||
predicates:
|
||||
- Path=/auth-service/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
# user服务 Swagger 文档路由
|
||||
- id: user-service-doc
|
||||
uri: lb://user-service
|
||||
uri: lb://aioj-user-service
|
||||
predicates:
|
||||
- Path=/user-service/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
# auth服务 Swagger 文档路由
|
||||
- id: file-service-doc
|
||||
uri: lb://file-service
|
||||
uri: lb://aioj-file-service
|
||||
predicates:
|
||||
- Path=/file-service/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
# auth业务接口
|
||||
# auth业务接口路由
|
||||
- id: auth-service
|
||||
uri: lb://auth-service
|
||||
uri: lb://aioj-auth-service
|
||||
predicates:
|
||||
- Path=/api/v1/auth/**
|
||||
filters:
|
||||
@@ -46,7 +47,7 @@ spring:
|
||||
firstBackoff: 50ms
|
||||
maxBackoff: 500ms
|
||||
- id: user-service
|
||||
uri: lb://user-service
|
||||
uri: lb://aioj-user-service
|
||||
predicates:
|
||||
- Path=/api/v1/user/**
|
||||
filters:
|
||||
@@ -58,7 +59,7 @@ spring:
|
||||
firstBackoff: 50ms
|
||||
maxBackoff: 500ms
|
||||
- id: file-service
|
||||
uri: lb://file-service
|
||||
uri: lb://aioj-file-service
|
||||
predicates:
|
||||
- Path=/api/v1/file/**
|
||||
filters:
|
||||
@@ -71,13 +72,14 @@ spring:
|
||||
maxBackoff: 500ms
|
||||
# 文件访问路由(公开,直接转发不去前缀)
|
||||
- id: file-access
|
||||
uri: lb://file-service
|
||||
uri: lb://aioj-file-service
|
||||
predicates:
|
||||
- Path=/api/file/**
|
||||
# 设置应用启动后的就绪探针
|
||||
lifecycle:
|
||||
timeout-per-shutdown-phase: 30s
|
||||
|
||||
|
||||
aioj-backend-gateway:
|
||||
# 白名单配置
|
||||
white-list:
|
||||
@@ -93,3 +95,6 @@ aioj:
|
||||
log:
|
||||
enabled: true
|
||||
max-length: 20000
|
||||
logging:
|
||||
file:
|
||||
path: ./logs/${spring.application.name}
|
||||
|
||||
120
aioj-backend-gateway/src/main/resources/logback-spring.xml
Normal file
120
aioj-backend-gateway/src/main/resources/logback-spring.xml
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--debug="false" 表示关闭 Logback 框架自身的内部状态信息打印。-->
|
||||
<configuration scan="true" scanPeriod="10 seconds" debug="false">
|
||||
<!-- 引入 spring boot 默认日志颜色和基础配置-->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
<!-- 定义变量 APP_NAME,从 Spring 环境变量中获取 spring.application.name 的值-->
|
||||
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
|
||||
|
||||
<!-- 定义时间格式,yyyy-MM 表示年-月,yyyy-MM-dd 表示年-月-日-->
|
||||
<timestamp key="time-month" datePattern="yyyy-MM"/>
|
||||
<timestamp key="time-month-day" datePattern="yyyy-MM-dd"/>
|
||||
<!-- 定义变量 LOG_FILE_PATH,默认值为 ./logs/${APP_NAME},可以通过环境变量 LOG_PATH 覆盖 日志存储路径-->
|
||||
<property name="LOG_FILE_PATH" value="${LOG_PATH:-./logs/${APP_NAME}}"/>
|
||||
<!-- 定义日志格式
|
||||
格式说明:
|
||||
%d{yyyy-MM-dd HH:mm:ss.SSS}:日志记录时间,格式为年-月-日 时:分:秒.毫秒
|
||||
[%thread]:日志记录线程名称
|
||||
%-5level:日志级别,左对齐,占用 5 个字符宽度
|
||||
%logger{50}:日志记录器名称,最多显示 50 个字符
|
||||
-[%X{traceId:-}]:从 MDC 中获取 traceId 变量值,如果不存在则显示为空
|
||||
%msg%n:日志消息内容,换行符
|
||||
-->
|
||||
<property name="FILE_LOG_PATTERN"
|
||||
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -[%X{traceId:-}] %msg%n"/>
|
||||
|
||||
<!-- appender 控制台输出-->
|
||||
<!--
|
||||
<appender>: 这是 Logback 配置的根标签之一,用于定义一个日志输出目的地。
|
||||
name="CONSOLE": 为这个 Appender 赋予一个唯一的名称,方便在 <root> 或 <logger> 标签中引用它。
|
||||
class="ch.qos.logback.core.ConsoleAppender": 指定使用的实现类。
|
||||
ConsoleAppender 是 Logback 库中专门用于将日志事件写入 System.out (标准输出) 或 System.err (标准错误) 的类。
|
||||
这是我们在本地开发和测试时最常用的 Appender。
|
||||
CONSOLE_LOG_PATTERN 是上面include的默认日志格式,这里直接引用即可
|
||||
-->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 全量info日志-->
|
||||
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
<maxHistory>31</maxHistory>
|
||||
<totalSizeCap>100GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<!-- 只记录 INFO 级别以及以上的日志的日志 -->
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
</appender>
|
||||
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
<maxHistory>31</maxHistory>
|
||||
<totalSizeCap>100GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 只记录 ERROR 级别以及以上的日志的日志 -->
|
||||
<level>ERROR</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 异步写入日志-->
|
||||
<appender name="ASYNC_INFO"
|
||||
class="ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<appender-ref ref="INFO_FILE"/>
|
||||
</appender>
|
||||
<appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<appender-ref ref="ERROR_FILE"/>
|
||||
</appender>
|
||||
|
||||
|
||||
<!-- =========== 环境配置 打印到控制台 ===========-->
|
||||
<springProfile name="dev">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="ASYNC_ERROR"/>
|
||||
<appender-ref ref="ASYNC_INFO"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="prod">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="ASYNC_ERROR"/>
|
||||
<appender-ref ref="ASYNC_INFO"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
<appender name="USER_LOG" class="ch.qos.logback.core.FileAppender">
|
||||
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/auth.log</file>
|
||||
<encoder>
|
||||
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
<logger name="cn.meowrain.aioj.backend.gateway.filter.AuthGlobalFilter">
|
||||
<appender-ref ref="USER_LOG"/>
|
||||
</logger>
|
||||
</configuration>
|
||||
@@ -0,0 +1,40 @@
|
||||
package cn.meowrain.aioj.backend.userservice.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* 异步配置类
|
||||
*
|
||||
* @author meowrain
|
||||
* @since 2026-01-18
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
public class AsyncConfig {
|
||||
|
||||
@Bean("emailExecutor")
|
||||
public Executor emailExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
// 核心线程数
|
||||
executor.setCorePoolSize(2);
|
||||
// 最大线程数
|
||||
executor.setMaxPoolSize(5);
|
||||
// 队列容量
|
||||
executor.setQueueCapacity(100);
|
||||
// 线程名前缀
|
||||
executor.setThreadNamePrefix("email-async-");
|
||||
// 拒绝策略:由调用线程执行
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
executor.initialize();
|
||||
log.info("邮件异步线程池初始化完成: coreSize=2, maxSize=5, queueCapacity=100");
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
package cn.meowrain.aioj.backend.userservice.controller;
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.core.utils.ContextHolderUtils;
|
||||
import cn.meowrain.aioj.backend.framework.core.web.Result;
|
||||
import cn.meowrain.aioj.backend.framework.core.web.Results;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.AvatarUpdateRequestDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.BindEmailRequest;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.EmailSendCodeRequestDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.*;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.resp.UserAuthRespDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.resp.UserProfileRespDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -15,7 +12,6 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@RestController()
|
||||
@@ -60,7 +56,7 @@ public class UserController {
|
||||
@Operation(summary = "发送验证码", description = "根据用户注册的邮箱发送验证码")
|
||||
@GetMapping("/email/send-code")
|
||||
public Result<Void> getVerifyCode(@Parameter(description = "邮箱信息", required = true)
|
||||
@Valid @RequestParam EmailSendCodeRequestDTO request) {
|
||||
@Valid @ModelAttribute EmailSendCodeRequestDTO request) {
|
||||
userService.sendEmailCode(request.getEmail());
|
||||
return Results.success(null);
|
||||
}
|
||||
@@ -73,8 +69,8 @@ public class UserController {
|
||||
*/
|
||||
@Operation(summary = "绑定邮箱", description = "根据用户注册的邮箱绑定邮箱")
|
||||
@PostMapping("/email/bind")
|
||||
public Result<Void> bindEmail(@RequestBody BindEmailRequest request) {
|
||||
userService.bindEmail(request.getEmail(), request.getCode());
|
||||
public Result<Void> bindEmail(@RequestBody @Valid BindEmailRequest request) {
|
||||
userService.bindEmail(request.getEmail(), request.getVerifyCode());
|
||||
return Results.success(null);
|
||||
}
|
||||
|
||||
@@ -86,20 +82,22 @@ public class UserController {
|
||||
@Operation(summary = "解绑邮箱", description = "根据用户注册的邮箱解绑邮箱")
|
||||
@PostMapping("/email/unbind")
|
||||
public Result<Void> unbindEmail() {
|
||||
userService.unbindEmail(ContextHolderUtils.getCurrentUserId());
|
||||
userService.unbindEmail();
|
||||
return Results.success(null);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "个人资料管理", description = "获取完整个人资料")
|
||||
@GetMapping("/profile")
|
||||
public Result<Void> getUserProfile() {
|
||||
return Results.success();
|
||||
public Result<UserProfileRespDTO> getUserProfile() {
|
||||
UserProfileRespDTO userProfileRespDTO = userService.getUserProfile();
|
||||
return Results.success(userProfileRespDTO);
|
||||
}
|
||||
|
||||
@Operation(summary = "个人资料管理-更新个人资料", description = "更新个人资料")
|
||||
@PutMapping("/profile")
|
||||
public Result<Void> updateUserProfile() {
|
||||
public Result<Void> updateUserProfile(@RequestBody UserProfileUpdateRequestDTO dto) {
|
||||
userService.updateUserProfile(dto);
|
||||
return Results.success();
|
||||
}
|
||||
|
||||
@@ -110,8 +108,10 @@ public class UserController {
|
||||
return Results.success();
|
||||
}
|
||||
|
||||
@PutMapping("/password")
|
||||
@Operation(summary = "个人资料管理-修改密码",description = "修改密码")
|
||||
public Result<Void> changePassword() {
|
||||
public Result<Void> changePassword(@RequestBody ChangePasswordRequestDTO dto) {
|
||||
userService.changePassword(dto);
|
||||
return Results.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,5 @@ public class BindEmailRequest {
|
||||
@Schema(description = "邮箱",example = "123@qq.com")
|
||||
private String email;
|
||||
@Schema(description = "验证码",example = "123456")
|
||||
private String code;
|
||||
private String verifyCode;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.meowrain.aioj.backend.userservice.dto.req;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "修改密码请求参数")
|
||||
public class ChangePasswordRequestDTO {
|
||||
@Schema(description = "旧密码")
|
||||
private String oldPassword;
|
||||
@Schema(description = "新密码")
|
||||
private String newPassword;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.meowrain.aioj.backend.userservice.dto.req;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "用户个人资料更新请求DTO")
|
||||
public class UserProfileUpdateRequestDTO {
|
||||
@Schema(description = "用户昵称")
|
||||
private String userName;
|
||||
@Schema(description = "用户简介")
|
||||
private String userProfile;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package cn.meowrain.aioj.backend.userservice.dto.resp;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "用户个人资料响应DTO")
|
||||
public class UserProfileRespDTO {
|
||||
|
||||
}
|
||||
@@ -10,4 +10,11 @@ public interface EmailService {
|
||||
* @param email 收件人邮箱
|
||||
*/
|
||||
void sendVerifyCode(String email);
|
||||
|
||||
/**
|
||||
* 获取邮箱验证码
|
||||
* @param email 收件人邮箱
|
||||
* @return 验证码
|
||||
*/
|
||||
String getVerifyCode(String email);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@ package cn.meowrain.aioj.backend.userservice.service;
|
||||
|
||||
import cn.meowrain.aioj.backend.userservice.dao.entity.User;
|
||||
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.ChangePasswordRequestDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.UserProfileUpdateRequestDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.resp.UserAuthRespDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.resp.UserProfileRespDTO;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
public interface UserService extends IService<User> {
|
||||
@@ -42,7 +45,7 @@ public interface UserService extends IService<User> {
|
||||
/**
|
||||
* 解绑邮箱
|
||||
*/
|
||||
void unbindEmail(Long userId);
|
||||
void unbindEmail();
|
||||
|
||||
/**
|
||||
* 设置用户头像
|
||||
@@ -50,4 +53,19 @@ public interface UserService extends IService<User> {
|
||||
* @return
|
||||
*/
|
||||
void setProfileAvatar(Long fileId);
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
void changePassword(ChangePasswordRequestDTO dto);
|
||||
|
||||
/**
|
||||
* 更新用户个人资料
|
||||
*/
|
||||
void updateUserProfile(UserProfileUpdateRequestDTO dto);
|
||||
|
||||
/**
|
||||
* 获取用户个人资料
|
||||
*/
|
||||
UserProfileRespDTO getUserProfile();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.meowrain.aioj.backend.userservice.service.impl;
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.core.exception.ClientException;
|
||||
import cn.meowrain.aioj.backend.framework.core.exception.ServiceException;
|
||||
import cn.meowrain.aioj.backend.userservice.common.constants.RedisKeyConstants;
|
||||
import cn.meowrain.aioj.backend.userservice.service.EmailService;
|
||||
@@ -11,6 +12,7 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -95,6 +97,7 @@ public class EmailServiceImpl implements EmailService {
|
||||
}
|
||||
|
||||
|
||||
@Async("emailExecutor")
|
||||
@Override
|
||||
public void sendVerifyCode(String email) {
|
||||
// 生成验证码
|
||||
@@ -124,4 +127,22 @@ public class EmailServiceImpl implements EmailService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取邮箱验证码
|
||||
* @param email 收件人邮箱
|
||||
* @return 验证码
|
||||
*/
|
||||
@Override
|
||||
public String getVerifyCode(String email) {
|
||||
String redisKey = String.format(RedisKeyConstants.EMAIL_CODE_PREFIX,email);
|
||||
// 从redis里面获取验证码,用Object接收,因为redis里面可能存储的是null 比如过期这种情况
|
||||
Object verifyCodeInSystem = redisTemplate.opsForValue().get(redisKey);
|
||||
if(verifyCodeInSystem == null) {
|
||||
throw new ClientException("验证码不存在或已过期");
|
||||
}
|
||||
// 转换为字符串
|
||||
return verifyCodeInSystem.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,8 +10,11 @@ import cn.meowrain.aioj.backend.userservice.dao.entity.User;
|
||||
import cn.meowrain.aioj.backend.userservice.dao.mapper.UserMapper;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.chains.context.UserRegisterRequestParamVerifyContext;
|
||||
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.ChangePasswordRequestDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.UserProfileUpdateRequestDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.resp.UserAuthRespDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.resp.UserProfileRespDTO;
|
||||
import cn.meowrain.aioj.backend.userservice.service.EmailService;
|
||||
import cn.meowrain.aioj.backend.userservice.service.UserService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
@@ -29,90 +32,187 @@ import java.util.Date;
|
||||
@Slf4j
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
|
||||
|
||||
private final UserRegisterRequestParamVerifyContext userRegisterRequestParamVerifyContext;
|
||||
private final UserRegisterRequestParamVerifyContext userRegisterRequestParamVerifyContext;
|
||||
|
||||
private final EmailService emailService;
|
||||
private final EmailService emailService;
|
||||
|
||||
@Override
|
||||
public Long userRegister(UserRegisterRequestDTO request) {
|
||||
UserAuthRespDTO authInfoByUserAccount = findAuthInfoByUserAccount(request.getUserAccount());
|
||||
if (authInfoByUserAccount != null) {
|
||||
throw new ClientException("重复创建用户");
|
||||
}
|
||||
@Override
|
||||
public Long userRegister(UserRegisterRequestDTO request) {
|
||||
UserAuthRespDTO authInfoByUserAccount = findAuthInfoByUserAccount(request.getUserAccount());
|
||||
if (authInfoByUserAccount != null) {
|
||||
throw new ClientException("重复创建用户");
|
||||
}
|
||||
|
||||
log.info("进行用户注册");
|
||||
userRegisterRequestParamVerifyContext.handler(ChainMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName(),
|
||||
request);
|
||||
// 使用 BCrypt 加密密码
|
||||
Date now = new Date();
|
||||
String salt = BCrypt.gensalt();
|
||||
String encryptPassword = BCrypt.hashpw(request.getUserPassword(), salt);
|
||||
User user = new User().setUserAccount(request.getUserAccount())
|
||||
.setUserPassword(encryptPassword)
|
||||
.setUserRole("user")
|
||||
.setCreateTime(now)
|
||||
.setUpdateTime(now);
|
||||
try {
|
||||
// 需要修改表,使得用户名是唯一的
|
||||
this.save(user);
|
||||
}
|
||||
catch (DuplicateKeyException e) {
|
||||
log.error("重复创建用户");
|
||||
throw new ServiceException("用户名已存在", ErrorCode.SYSTEM_ERROR);
|
||||
}
|
||||
log.info("进行用户注册");
|
||||
userRegisterRequestParamVerifyContext.handler(ChainMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName(),
|
||||
request);
|
||||
// 使用 BCrypt 加密密码
|
||||
Date now = new Date();
|
||||
String salt = BCrypt.gensalt();
|
||||
String encryptPassword = BCrypt.hashpw(request.getUserPassword(), salt);
|
||||
User user = new User().setUserAccount(request.getUserAccount())
|
||||
.setUserPassword(encryptPassword)
|
||||
.setUserRole("user")
|
||||
.setCreateTime(now)
|
||||
.setUpdateTime(now);
|
||||
try {
|
||||
// 需要修改表,使得用户名是唯一的
|
||||
this.save(user);
|
||||
} catch (DuplicateKeyException e) {
|
||||
log.error("重复创建用户");
|
||||
throw new ServiceException("用户名已存在", ErrorCode.SYSTEM_ERROR);
|
||||
}
|
||||
|
||||
return user.getId();
|
||||
}
|
||||
return user.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAuthRespDTO findAuthInfoByUserAccount(String userAccount) {
|
||||
User one = this.lambdaQuery().eq(User::getUserAccount, userAccount).one();
|
||||
UserAuthRespDTO userAuthDTO = new UserAuthRespDTO();
|
||||
if (one != null) {
|
||||
BeanUtils.copyProperties(one, userAuthDTO);
|
||||
return userAuthDTO;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public UserAuthRespDTO findAuthInfoByUserAccount(String userAccount) {
|
||||
User one = this.lambdaQuery().eq(User::getUserAccount, userAccount).one();
|
||||
UserAuthRespDTO userAuthDTO = new UserAuthRespDTO();
|
||||
if (one != null) {
|
||||
BeanUtils.copyProperties(one, userAuthDTO);
|
||||
return userAuthDTO;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAuthRespDTO findAuthInfoByUserId(Long userId) {
|
||||
User one = this.lambdaQuery().eq(User::getId, userId).one();
|
||||
UserAuthRespDTO userAuthDTO = new UserAuthRespDTO();
|
||||
if (one != null) {
|
||||
BeanUtils.copyProperties(one, userAuthDTO);
|
||||
return userAuthDTO;
|
||||
}
|
||||
return null;
|
||||
@Override
|
||||
public UserAuthRespDTO findAuthInfoByUserId(Long userId) {
|
||||
User one = this.lambdaQuery().eq(User::getId, userId).one();
|
||||
UserAuthRespDTO userAuthDTO = new UserAuthRespDTO();
|
||||
if (one != null) {
|
||||
BeanUtils.copyProperties(one, userAuthDTO);
|
||||
return userAuthDTO;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEmailCode(String email) {
|
||||
emailService.sendVerifyCode(email);
|
||||
}
|
||||
@Override
|
||||
public void sendEmailCode(String email) {
|
||||
emailService.sendVerifyCode(email);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindEmail(String email, String code) {
|
||||
Long currentUserId = ContextHolderUtils.getCurrentUserId();
|
||||
@Override
|
||||
public void bindEmail(String email, String code) {
|
||||
if (email == null || code == null) {
|
||||
throw new ClientException("邮箱或验证码不能为空");
|
||||
}
|
||||
Long currentUserId = ContextHolderUtils.getCurrentUserId();
|
||||
// 检查用户是否存在
|
||||
User user = this.lambdaQuery().eq(User::getId, currentUserId).one();
|
||||
if (user == null) {
|
||||
throw new ClientException("用户不存在");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// 验证邮箱验证码
|
||||
// 从redis里面获取验证码
|
||||
String verifyCodeInSystem = emailService.getVerifyCode(email);
|
||||
// 验证验证码是否匹配
|
||||
if (!verifyCodeInSystem.equals(code)) {
|
||||
throw new ClientException("验证码错误,请重新输入");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindEmail(Long userId) {
|
||||
User one = this.lambdaQuery().eq(User::getId, userId).one();
|
||||
// 绑定邮箱
|
||||
user.setUserEmail(email);
|
||||
user.setUpdateTime(new Date());
|
||||
this.updateById(user);
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public void unbindEmail() {
|
||||
Long currentUserId = ContextHolderUtils.getCurrentUserId();
|
||||
User user = this.lambdaQuery().eq(User::getId, currentUserId).one();
|
||||
if (user == null) {
|
||||
throw new ClientException("用户不存在");
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void setProfileAvatar(Long fileId) {
|
||||
Long currentUserId = ContextHolderUtils.getCurrentUserId();
|
||||
User user = this.lambdaQuery().eq(User::getId, currentUserId).one();
|
||||
user.setUserAvatar(fileId);
|
||||
user.setUpdateTime(new Date());
|
||||
this.updateById(user);
|
||||
}
|
||||
if (user.getUserEmail() == null) {
|
||||
throw new ClientException("邮箱未绑定");
|
||||
}
|
||||
// 这种方法避免了查询整个对象,并且能精确控制 NULL 值的更新。
|
||||
boolean success = this.lambdaUpdate()
|
||||
// 设置 userEmail 为 NULL
|
||||
.set(User::getUserEmail, null)
|
||||
// 设置 updateTime 为当前时间
|
||||
.set(User::getUpdateTime, new Date())
|
||||
// 筛选条件:用户ID
|
||||
.eq(User::getId, currentUserId)
|
||||
// 执行更新
|
||||
.update();
|
||||
|
||||
if (!success) {
|
||||
throw new ClientException("解绑失败,请重试");
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void setProfileAvatar(Long fileId) {
|
||||
Long currentUserId = ContextHolderUtils.getCurrentUserId();
|
||||
User user = this.lambdaQuery().eq(User::getId, currentUserId).one();
|
||||
user.setUserAvatar(fileId);
|
||||
user.setUpdateTime(new Date());
|
||||
this.updateById(user);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void changePassword(ChangePasswordRequestDTO dto) {
|
||||
Long currentUserId = ContextHolderUtils.getCurrentUserId();
|
||||
User user = this.lambdaQuery().eq(User::getId, currentUserId).one();
|
||||
|
||||
// 检查用户是否存在
|
||||
if (user == null) {
|
||||
throw new ClientException("用户不存在");
|
||||
}
|
||||
|
||||
// 检查旧密码是否正确
|
||||
String oldPassword = user.getUserPassword();
|
||||
if (!BCrypt.checkpw(dto.getOldPassword(), oldPassword)) {
|
||||
throw new ClientException("旧密码错误");
|
||||
}
|
||||
|
||||
// 检查新密码是否与旧密码相同
|
||||
if (BCrypt.checkpw(dto.getNewPassword(), oldPassword)) {
|
||||
throw new ClientException("新密码不能与旧密码相同");
|
||||
}
|
||||
|
||||
|
||||
// 加密新密码
|
||||
Date now = new Date();
|
||||
String salt = BCrypt.gensalt();
|
||||
String encryptPassword = BCrypt.hashpw(dto.getNewPassword(), salt);
|
||||
// 更新用户密码
|
||||
user.setUserPassword(encryptPassword);
|
||||
user.setUpdateTime(now);
|
||||
this.updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserProfile(UserProfileUpdateRequestDTO dto) {
|
||||
Long currentUserId = ContextHolderUtils.getCurrentUserId();
|
||||
User user = this.lambdaQuery().eq(User::getId, currentUserId).one();
|
||||
if (user == null) {
|
||||
throw new ClientException("用户不存在");
|
||||
}
|
||||
// 更新用户个人资料
|
||||
user.setUserName(dto.getUserName());
|
||||
user.setUserProfile(dto.getUserProfile());
|
||||
user.setUpdateTime(new Date());
|
||||
this.updateById(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户个人资料
|
||||
* @return 用户个人资料
|
||||
*/
|
||||
@Override
|
||||
public UserProfileRespDTO getUserProfile() {
|
||||
// TODO: 待实现 从数据库查询用户个人资料
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
spring:
|
||||
application:
|
||||
name: user-service
|
||||
name: aioj-user-service
|
||||
profiles:
|
||||
active: @env@
|
||||
server:
|
||||
@@ -42,4 +42,7 @@ jwt:
|
||||
aioj:
|
||||
log:
|
||||
enabled: true
|
||||
max-length: 20000
|
||||
max-length: 20000
|
||||
logging:
|
||||
file:
|
||||
path: ./logs/${spring.application.name}
|
||||
|
||||
110
aioj-backend-user-service/src/main/resources/logback-spring.xml
Normal file
110
aioj-backend-user-service/src/main/resources/logback-spring.xml
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--debug="false" 表示关闭 Logback 框架自身的内部状态信息打印。-->
|
||||
<configuration scan="true" scanPeriod="10 seconds" debug="false">
|
||||
<!-- 引入 spring boot 默认日志颜色和基础配置-->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
<!-- 定义变量 APP_NAME,从 Spring 环境变量中获取 spring.application.name 的值-->
|
||||
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
|
||||
|
||||
<!-- 定义时间格式,yyyy-MM 表示年-月,yyyy-MM-dd 表示年-月-日-->
|
||||
<timestamp key="time-month" datePattern="yyyy-MM"/>
|
||||
<timestamp key="time-month-day" datePattern="yyyy-MM-dd"/>
|
||||
<!-- 定义变量 LOG_FILE_PATH,默认值为 ./logs/${APP_NAME},可以通过环境变量 LOG_PATH 覆盖 日志存储路径-->
|
||||
<property name="LOG_FILE_PATH" value="${LOG_PATH:-./logs/${APP_NAME}}"/>
|
||||
<!-- 定义日志格式
|
||||
格式说明:
|
||||
%d{yyyy-MM-dd HH:mm:ss.SSS}:日志记录时间,格式为年-月-日 时:分:秒.毫秒
|
||||
[%thread]:日志记录线程名称
|
||||
%-5level:日志级别,左对齐,占用 5 个字符宽度
|
||||
%logger{50}:日志记录器名称,最多显示 50 个字符
|
||||
-[%X{traceId:-}]:从 MDC 中获取 traceId 变量值,如果不存在则显示为空
|
||||
%msg%n:日志消息内容,换行符
|
||||
-->
|
||||
<property name="FILE_LOG_PATTERN"
|
||||
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -[%X{traceId:-}] %msg%n"/>
|
||||
|
||||
<!-- appender 控制台输出-->
|
||||
<!--
|
||||
<appender>: 这是 Logback 配置的根标签之一,用于定义一个日志输出目的地。
|
||||
name="CONSOLE": 为这个 Appender 赋予一个唯一的名称,方便在 <root> 或 <logger> 标签中引用它。
|
||||
class="ch.qos.logback.core.ConsoleAppender": 指定使用的实现类。
|
||||
ConsoleAppender 是 Logback 库中专门用于将日志事件写入 System.out (标准输出) 或 System.err (标准错误) 的类。
|
||||
这是我们在本地开发和测试时最常用的 Appender。
|
||||
CONSOLE_LOG_PATTERN 是上面include的默认日志格式,这里直接引用即可
|
||||
-->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 全量info日志-->
|
||||
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
<maxHistory>31</maxHistory>
|
||||
<totalSizeCap>100GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<!-- 只记录 INFO 级别以及以上的日志的日志 -->
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
</appender>
|
||||
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
<maxHistory>31</maxHistory>
|
||||
<totalSizeCap>100GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 只记录 ERROR 级别以及以上的日志的日志 -->
|
||||
<level>ERROR</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 异步写入日志-->
|
||||
<appender name="ASYNC_INFO"
|
||||
class="ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<appender-ref ref="INFO_FILE"/>
|
||||
</appender>
|
||||
<appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<appender-ref ref="ERROR_FILE"/>
|
||||
</appender>
|
||||
|
||||
|
||||
<!-- =========== 环境配置 打印到控制台 ===========-->
|
||||
<springProfile name="dev">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="ASYNC_ERROR"/>
|
||||
<appender-ref ref="ASYNC_INFO"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="prod">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="ASYNC_ERROR"/>
|
||||
<appender-ref ref="ASYNC_INFO"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user