添加账号修改,后台管理

This commit is contained in:
yoyuzh
2026-03-19 17:52:58 +08:00
parent c39fde6b19
commit ff8d47f44f
60 changed files with 4264 additions and 58 deletions

View File

@@ -3,8 +3,14 @@ package com.yoyuzh.auth;
import com.yoyuzh.auth.dto.AuthResponse;
import com.yoyuzh.auth.dto.LoginRequest;
import com.yoyuzh.auth.dto.RegisterRequest;
import com.yoyuzh.auth.dto.UpdateUserAvatarRequest;
import com.yoyuzh.auth.dto.UpdateUserPasswordRequest;
import com.yoyuzh.auth.dto.UpdateUserProfileRequest;
import com.yoyuzh.common.BusinessException;
import com.yoyuzh.files.FileService;
import com.yoyuzh.files.InitiateUploadResponse;
import com.yoyuzh.files.storage.FileContentStorage;
import com.yoyuzh.files.storage.PreparedUpload;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -12,6 +18,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -21,6 +28,8 @@ import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,6 +54,9 @@ class AuthServiceTest {
@Mock
private FileService fileService;
@Mock
private FileContentStorage fileContentStorage;
@InjectMocks
private AuthService authService;
@@ -137,6 +149,17 @@ class AuthServiceTest {
.hasMessageContaining("用户名或密码错误");
}
@Test
void shouldRejectBannedUserLogin() {
LoginRequest request = new LoginRequest("alice", "plain-password");
when(authenticationManager.authenticate(any()))
.thenThrow(new DisabledException("disabled"));
assertThatThrownBy(() -> authService.login(request))
.isInstanceOf(BusinessException.class)
.hasMessageContaining("账号已被封禁");
}
@Test
void shouldCreateDefaultDirectoriesForDevLoginUser() {
when(userRepository.findByUsername("demo")).thenReturn(Optional.empty());
@@ -157,4 +180,128 @@ class AuthServiceTest {
assertThat(response.refreshToken()).isEqualTo("refresh-token");
verify(fileService).ensureDefaultDirectories(any(User.class));
}
@Test
void shouldUpdateCurrentUserProfile() {
User user = new User();
user.setId(1L);
user.setUsername("alice");
user.setDisplayName("Alice");
user.setEmail("alice@example.com");
user.setBio("old bio");
user.setPreferredLanguage("zh-CN");
user.setRole(UserRole.USER);
user.setCreatedAt(LocalDateTime.now());
UpdateUserProfileRequest request = new UpdateUserProfileRequest(
"Alicia",
"newalice@example.com",
"new bio",
"en-US"
);
when(userRepository.findByUsername("alice")).thenReturn(Optional.of(user));
when(userRepository.existsByEmail("newalice@example.com")).thenReturn(false);
when(userRepository.save(user)).thenReturn(user);
var response = authService.updateProfile("alice", request);
assertThat(response.displayName()).isEqualTo("Alicia");
assertThat(response.email()).isEqualTo("newalice@example.com");
assertThat(response.bio()).isEqualTo("new bio");
assertThat(response.preferredLanguage()).isEqualTo("en-US");
}
@Test
void shouldChangePasswordAndIssueFreshTokens() {
User user = new User();
user.setId(1L);
user.setUsername("alice");
user.setDisplayName("Alice");
user.setEmail("alice@example.com");
user.setPreferredLanguage("zh-CN");
user.setRole(UserRole.USER);
user.setPasswordHash("encoded-old");
user.setCreatedAt(LocalDateTime.now());
UpdateUserPasswordRequest request = new UpdateUserPasswordRequest("OldPass1!", "NewPass1!A");
when(userRepository.findByUsername("alice")).thenReturn(Optional.of(user));
when(passwordEncoder.matches("OldPass1!", "encoded-old")).thenReturn(true);
when(passwordEncoder.encode("NewPass1!A")).thenReturn("encoded-new");
when(userRepository.save(user)).thenReturn(user);
when(jwtTokenProvider.generateAccessToken(1L, "alice")).thenReturn("new-access");
when(refreshTokenService.issueRefreshToken(user)).thenReturn("new-refresh");
AuthResponse response = authService.changePassword("alice", request);
assertThat(response.accessToken()).isEqualTo("new-access");
assertThat(response.refreshToken()).isEqualTo("new-refresh");
verify(refreshTokenService).revokeAllForUser(1L);
verify(passwordEncoder).encode("NewPass1!A");
}
@Test
void shouldRejectPasswordChangeWhenCurrentPasswordIsWrong() {
User user = new User();
user.setId(1L);
user.setUsername("alice");
user.setPasswordHash("encoded-old");
when(userRepository.findByUsername("alice")).thenReturn(Optional.of(user));
when(passwordEncoder.matches("WrongPass1!", "encoded-old")).thenReturn(false);
assertThatThrownBy(() -> authService.changePassword("alice", new UpdateUserPasswordRequest("WrongPass1!", "NewPass1!A")))
.isInstanceOf(BusinessException.class)
.hasMessageContaining("当前密码错误");
}
@Test
void shouldInitiateAvatarUploadThroughStorage() {
User user = new User();
user.setId(1L);
user.setUsername("alice");
when(userRepository.findByUsername("alice")).thenReturn(Optional.of(user));
when(fileContentStorage.prepareUpload(eq(1L), eq("/.avatar"), any(), eq("image/png"), eq(2048L)))
.thenReturn(new PreparedUpload(true, "https://upload.example.com/avatar", "PUT", java.util.Map.of("Content-Type", "image/png"), "avatar-generated.png"));
InitiateUploadResponse response = authService.initiateAvatarUpload(
"alice",
new UpdateUserAvatarRequest("face.png", "image/png", 2048L, "avatar-generated.png")
);
assertThat(response.direct()).isTrue();
assertThat(response.uploadUrl()).isEqualTo("https://upload.example.com/avatar");
assertThat(response.storageName()).endsWith(".png");
}
@Test
void shouldCompleteAvatarUploadAndReplacePreviousAvatar() {
User user = new User();
user.setId(1L);
user.setUsername("alice");
user.setDisplayName("Alice");
user.setEmail("alice@example.com");
user.setPreferredLanguage("zh-CN");
user.setRole(UserRole.USER);
user.setAvatarStorageName("old-avatar.png");
user.setAvatarContentType("image/png");
user.setCreatedAt(LocalDateTime.now());
when(userRepository.findByUsername("alice")).thenReturn(Optional.of(user));
when(fileContentStorage.supportsDirectDownload()).thenReturn(true);
when(fileContentStorage.createDownloadUrl(anyLong(), eq("/.avatar"), eq("new-avatar.webp"), any()))
.thenReturn("https://cdn.example.com/avatar.webp");
when(userRepository.save(user)).thenReturn(user);
var response = authService.completeAvatarUpload(
"alice",
new UpdateUserAvatarRequest("face.webp", "image/webp", 4096L, "new-avatar.webp")
);
verify(fileContentStorage).completeUpload(1L, "/.avatar", "new-avatar.webp", "image/webp", 4096L);
verify(fileContentStorage).deleteFile(1L, "/.avatar", "old-avatar.png");
assertThat(response.avatarUrl()).isEqualTo("https://cdn.example.com/avatar.webp");
}
}