diff --git a/.gitignore b/.gitignore
index 25e5e64..28c791c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,4 +39,9 @@ build/
### mybatis plus generator
-/generator/
\ No newline at end of file
+/generator/
+
+### Uploads ###
+/uploads/
+### Logs ###
+/logs/
\ No newline at end of file
diff --git a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/clients/UserClient.java b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/clients/UserClient.java
index b0e7c23..5fe0811 100644
--- a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/clients/UserClient.java
+++ b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/clients/UserClient.java
@@ -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")
diff --git a/aioj-backend-auth/src/main/resources/application-dev.yml b/aioj-backend-auth/src/main/resources/application-dev.yml
index 4880e1b..5854faf 100644
--- a/aioj-backend-auth/src/main/resources/application-dev.yml
+++ b/aioj-backend-auth/src/main/resources/application-dev.yml
@@ -1,6 +1,6 @@
spring:
application:
- name: auth-service
+ name: aioj-auth-service
data:
redis:
host: 10.0.0.10
diff --git a/aioj-backend-auth/src/main/resources/application.yml b/aioj-backend-auth/src/main/resources/application.yml
index 914ff8a..3ef233c 100644
--- a/aioj-backend-auth/src/main/resources/application.yml
+++ b/aioj-backend-auth/src/main/resources/application.yml
@@ -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天
\ No newline at end of file
+ refresh-expire: 604800000 # 7天
+logging:
+ file:
+ path: ./logs/${spring.application.name}
diff --git a/aioj-backend-auth/src/main/resources/logback-spring.xml b/aioj-backend-auth/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..8c32daa
--- /dev/null
+++ b/aioj-backend-auth/src/main/resources/logback-spring.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${CONSOLE_LOG_PATTERN}
+ UTF-8
+
+
+
+
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.log
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.%d{yyyy-MM-dd}.%i.log.gz
+ 100MB
+ 31
+ 100GB
+
+
+ ${FILE_LOG_PATTERN}
+ UTF-8
+
+
+
+ INFO
+
+
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.log
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.%d{yyyy-MM-dd}.%i.log.gz
+ 100MB
+ 31
+ 100GB
+
+
+ ${FILE_LOG_PATTERN}
+ UTF-8
+
+
+
+ ERROR
+ ACCEPT
+ DENY
+
+
+
+
+
+ 0
+ 512
+
+
+
+ 0
+ 512
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/aioj-backend-file-service/src/main/resources/application.yml b/aioj-backend-file-service/src/main/resources/application.yml
index 9860021..17be844 100644
--- a/aioj-backend-file-service/src/main/resources/application.yml
+++ b/aioj-backend-file-service/src/main/resources/application.yml
@@ -1,6 +1,6 @@
spring:
application:
- name: file-service
+ name: aioj-file-service
profiles:
active: @env@
servlet:
diff --git a/aioj-backend-file-service/src/main/resources/logback-spring.xml b/aioj-backend-file-service/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..9c5ece5
--- /dev/null
+++ b/aioj-backend-file-service/src/main/resources/logback-spring.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${CONSOLE_LOG_PATTERN}
+ UTF-8
+
+
+
+
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.log
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.%d{yyyy-MM-dd}.%i.log.gz
+ 100MB
+ 31
+ 100GB
+
+
+ ${FILE_LOG_PATTERN}
+ UTF-8
+
+
+
+ INFO
+
+
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.log
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.%d{yyyy-MM-dd}.%i.log.gz
+ 100MB
+ 31
+ 100GB
+
+
+ ${FILE_LOG_PATTERN}
+ UTF-8
+
+
+
+ ERROR
+ ACCEPT
+ DENY
+
+
+
+
+
+ 0
+ 512
+
+
+
+ 0
+ 512
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/aioj-backend-gateway/src/main/java/cn/meowrain/aioj/backend/gateway/filter/AuthGlobalFilter.java b/aioj-backend-gateway/src/main/java/cn/meowrain/aioj/backend/gateway/filter/AuthGlobalFilter.java
index c12689d..5cf1775 100644
--- a/aioj-backend-gateway/src/main/java/cn/meowrain/aioj/backend/gateway/filter/AuthGlobalFilter.java
+++ b/aioj-backend-gateway/src/main/java/cn/meowrain/aioj/backend/gateway/filter/AuthGlobalFilter.java
@@ -105,7 +105,7 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
private Mono 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()
diff --git a/aioj-backend-gateway/src/main/resources/application.yml b/aioj-backend-gateway/src/main/resources/application.yml
index 78bdf50..f2bc956 100644
--- a/aioj-backend-gateway/src/main/resources/application.yml
+++ b/aioj-backend-gateway/src/main/resources/application.yml
@@ -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}
diff --git a/aioj-backend-gateway/src/main/resources/logback-spring.xml b/aioj-backend-gateway/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..d2eb726
--- /dev/null
+++ b/aioj-backend-gateway/src/main/resources/logback-spring.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${CONSOLE_LOG_PATTERN}
+ UTF-8
+
+
+
+
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.log
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.%d{yyyy-MM-dd}.%i.log.gz
+ 100MB
+ 31
+ 100GB
+
+
+ ${FILE_LOG_PATTERN}
+ UTF-8
+
+
+
+ INFO
+
+
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.log
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.%d{yyyy-MM-dd}.%i.log.gz
+ 100MB
+ 31
+ 100GB
+
+
+ ${FILE_LOG_PATTERN}
+ UTF-8
+
+
+
+ ERROR
+ ACCEPT
+ DENY
+
+
+
+
+
+ 0
+ 512
+
+
+
+ 0
+ 512
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/auth.log
+
+ ${FILE_LOG_PATTERN}
+ UTF-8
+
+
+
+
+
+
\ No newline at end of file
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/AsyncConfig.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/AsyncConfig.java
new file mode 100644
index 0000000..d985b08
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/AsyncConfig.java
@@ -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;
+ }
+}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/controller/UserController.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/controller/UserController.java
index 958f77b..7affa75 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/controller/UserController.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/controller/UserController.java
@@ -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 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 bindEmail(@RequestBody BindEmailRequest request) {
- userService.bindEmail(request.getEmail(), request.getCode());
+ public Result 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 unbindEmail() {
- userService.unbindEmail(ContextHolderUtils.getCurrentUserId());
+ userService.unbindEmail();
return Results.success(null);
}
@Operation(summary = "个人资料管理", description = "获取完整个人资料")
@GetMapping("/profile")
- public Result getUserProfile() {
- return Results.success();
+ public Result getUserProfile() {
+ UserProfileRespDTO userProfileRespDTO = userService.getUserProfile();
+ return Results.success(userProfileRespDTO);
}
@Operation(summary = "个人资料管理-更新个人资料", description = "更新个人资料")
@PutMapping("/profile")
- public Result updateUserProfile() {
+ public Result 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 changePassword() {
+ public Result changePassword(@RequestBody ChangePasswordRequestDTO dto) {
+ userService.changePassword(dto);
return Results.success();
}
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/BindEmailRequest.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/BindEmailRequest.java
index 9bd9684..44eb37f 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/BindEmailRequest.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/BindEmailRequest.java
@@ -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;
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/ChangePasswordRequestDTO.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/ChangePasswordRequestDTO.java
new file mode 100644
index 0000000..7fae58d
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/ChangePasswordRequestDTO.java
@@ -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;
+}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserProfileUpdateRequestDTO.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserProfileUpdateRequestDTO.java
new file mode 100644
index 0000000..c49fbdc
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserProfileUpdateRequestDTO.java
@@ -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;
+}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/resp/UserProfileRespDTO.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/resp/UserProfileRespDTO.java
new file mode 100644
index 0000000..a8f755c
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/resp/UserProfileRespDTO.java
@@ -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 {
+
+}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/EmailService.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/EmailService.java
index f1e74c7..f19b589 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/EmailService.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/EmailService.java
@@ -10,4 +10,11 @@ public interface EmailService {
* @param email 收件人邮箱
*/
void sendVerifyCode(String email);
+
+ /**
+ * 获取邮箱验证码
+ * @param email 收件人邮箱
+ * @return 验证码
+ */
+ String getVerifyCode(String email);
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/UserService.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/UserService.java
index 84ec10a..6fa0fee 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/UserService.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/UserService.java
@@ -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 {
@@ -42,7 +45,7 @@ public interface UserService extends IService {
/**
* 解绑邮箱
*/
- void unbindEmail(Long userId);
+ void unbindEmail();
/**
* 设置用户头像
@@ -50,4 +53,19 @@ public interface UserService extends IService {
* @return
*/
void setProfileAvatar(Long fileId);
+
+ /**
+ * 修改密码
+ */
+ void changePassword(ChangePasswordRequestDTO dto);
+
+ /**
+ * 更新用户个人资料
+ */
+ void updateUserProfile(UserProfileUpdateRequestDTO dto);
+
+ /**
+ * 获取用户个人资料
+ */
+ UserProfileRespDTO getUserProfile();
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/EmailServiceImpl.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/EmailServiceImpl.java
index f865215..e7101f9 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/EmailServiceImpl.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/EmailServiceImpl.java
@@ -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();
+ }
+
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/UserServiceImpl.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/UserServiceImpl.java
index 4d963c2..d88afac 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/UserServiceImpl.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/UserServiceImpl.java
@@ -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 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;
+ }
}
diff --git a/aioj-backend-user-service/src/main/resources/application.yml b/aioj-backend-user-service/src/main/resources/application.yml
index acad1cb..bb01c8f 100644
--- a/aioj-backend-user-service/src/main/resources/application.yml
+++ b/aioj-backend-user-service/src/main/resources/application.yml
@@ -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
\ No newline at end of file
+ max-length: 20000
+logging:
+ file:
+ path: ./logs/${spring.application.name}
diff --git a/aioj-backend-user-service/src/main/resources/logback-spring.xml b/aioj-backend-user-service/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..8c32daa
--- /dev/null
+++ b/aioj-backend-user-service/src/main/resources/logback-spring.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${CONSOLE_LOG_PATTERN}
+ UTF-8
+
+
+
+
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.log
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.%d{yyyy-MM-dd}.%i.log.gz
+ 100MB
+ 31
+ 100GB
+
+
+ ${FILE_LOG_PATTERN}
+ UTF-8
+
+
+
+ INFO
+
+
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.log
+
+ ${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.%d{yyyy-MM-dd}.%i.log.gz
+ 100MB
+ 31
+ 100GB
+
+
+ ${FILE_LOG_PATTERN}
+ UTF-8
+
+
+
+ ERROR
+ ACCEPT
+ DENY
+
+
+
+
+
+ 0
+ 512
+
+
+
+ 0
+ 512
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file