feat: 完善系统配置审计通知功能并优化异常处理
- 新增异常处理体系(BaseException及其子类) - 优化密码、邮箱、用户名等基础类型 - 添加字典管理、登录日志、操作日志的E2E测试 - 完善API集成测试和安全测试 - 添加性能测试配置和脚本 - 优化OpenAPI配置和全局异常处理器
This commit is contained in:
+6
-3
@@ -1,5 +1,8 @@
|
||||
package cn.novalon.manage.sys.core.command;
|
||||
|
||||
import cn.novalon.manage.common.exception.ErrorCode;
|
||||
import cn.novalon.manage.common.exception.ValidationException;
|
||||
|
||||
/**
|
||||
* 创建公告命令对象
|
||||
*
|
||||
@@ -20,19 +23,19 @@ public record CreateNoticeCommand(
|
||||
|
||||
private static void validateNoticeTitle(String noticeTitle) {
|
||||
if (noticeTitle == null || noticeTitle.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Notice title is required");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_REQUIRED, "Notice title is required");
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateNoticeContent(String noticeContent) {
|
||||
if (noticeContent == null || noticeContent.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Notice content is required");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_REQUIRED, "Notice content is required");
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateNoticeType(String noticeType) {
|
||||
if (noticeType != null && !noticeType.equals("1") && !noticeType.equals("2")) {
|
||||
throw new IllegalArgumentException(
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_VALUE,
|
||||
"Invalid notice type. Notice type must be 1 (notification) or 2 (announcement)");
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -1,5 +1,7 @@
|
||||
package cn.novalon.manage.sys.core.command;
|
||||
|
||||
import cn.novalon.manage.common.exception.ErrorCode;
|
||||
import cn.novalon.manage.common.exception.ValidationException;
|
||||
import cn.novalon.manage.common.util.StatusConstants;
|
||||
|
||||
/**
|
||||
@@ -21,7 +23,8 @@ public record CreateRoleCommand(
|
||||
|
||||
private static void validateStatus(Integer status) {
|
||||
if (status != null && status != StatusConstants.ENABLED && status != StatusConstants.DISABLED) {
|
||||
throw new IllegalArgumentException("Invalid status value. Status must be 0 (disabled) or 1 (enabled)");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_VALUE,
|
||||
"Invalid status value. Status must be 0 (disabled) or 1 (enabled)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -1,6 +1,7 @@
|
||||
package cn.novalon.manage.sys.core.domain;
|
||||
|
||||
import cn.novalon.manage.common.util.SnowflakeId;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -10,11 +11,19 @@ import java.time.LocalDateTime;
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
@Schema(description = "系统角色实体")
|
||||
public class SysRole extends BaseDomain {
|
||||
|
||||
@Schema(description = "角色名称", example = "管理员")
|
||||
private String roleName;
|
||||
|
||||
@Schema(description = "角色权限字符串", example = "admin")
|
||||
private String roleKey;
|
||||
|
||||
@Schema(description = "显示顺序", example = "1")
|
||||
private Integer roleSort;
|
||||
|
||||
@Schema(description = "状态:0-禁用,1-正常", example = "1")
|
||||
private Integer status;
|
||||
|
||||
public String getRoleName() {
|
||||
|
||||
+15
-1
@@ -1,7 +1,7 @@
|
||||
package cn.novalon.manage.sys.core.domain;
|
||||
|
||||
import cn.novalon.manage.common.util.SnowflakeId;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
@@ -10,14 +10,28 @@ import java.time.LocalDateTime;
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
@Schema(description = "系统用户实体")
|
||||
public class SysUser extends BaseDomain {
|
||||
|
||||
@Schema(description = "用户名", example = "admin")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码(加密后)", example = "$2a$10$...")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "昵称", example = "管理员")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "邮箱", example = "admin@example.com")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "手机号", example = "13800138000")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "角色ID", example = "1")
|
||||
private Long roleId;
|
||||
|
||||
@Schema(description = "状态:0-禁用,1-正常", example = "1")
|
||||
private Integer status;
|
||||
|
||||
public String getUsername() {
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
package cn.novalon.manage.sys.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
@@ -12,11 +13,14 @@ import jakarta.validation.constraints.NotBlank;
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
@Schema(description = "用户登录请求")
|
||||
public class LoginRequest {
|
||||
|
||||
@Schema(description = "用户名", example = "admin")
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码", example = "123456")
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
|
||||
+7
@@ -1,5 +1,6 @@
|
||||
package cn.novalon.manage.sys.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
@@ -10,22 +11,28 @@ import jakarta.validation.constraints.Size;
|
||||
* @author 张翔
|
||||
* @date 2026-03-14
|
||||
*/
|
||||
@Schema(description = "用户注册请求")
|
||||
public class UserRegisterRequest {
|
||||
|
||||
@Schema(description = "用户名", example = "testuser")
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 3, max = 50, message = "用户名长度必须在3-50之间")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "昵称", example = "测试用户")
|
||||
@Size(max = 100, message = "昵称长度不能超过100")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "密码", example = "123456")
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 100, message = "密码长度必须在6-100之间")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "邮箱", example = "test@example.com")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "手机号", example = "13800138000")
|
||||
@Size(max = 20, message = "手机号长度不能超过20")
|
||||
private String phone;
|
||||
|
||||
|
||||
+6
@@ -1,5 +1,6 @@
|
||||
package cn.novalon.manage.sys.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Email;
|
||||
|
||||
/**
|
||||
@@ -8,14 +9,19 @@ import jakarta.validation.constraints.Email;
|
||||
* @author 张翔
|
||||
* @date 2026-03-14
|
||||
*/
|
||||
@Schema(description = "用户更新请求")
|
||||
public class UserUpdateRequest {
|
||||
|
||||
@Schema(description = "邮箱", example = "newemail@example.com")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "状态:0-禁用,1-正常", example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "角色ID", example = "1")
|
||||
private Long roleId;
|
||||
|
||||
@Schema(description = "是否清除角色关联", example = "false")
|
||||
private Boolean clearRole;
|
||||
|
||||
@Email(message = "邮箱格式不正确")
|
||||
|
||||
+8
@@ -1,15 +1,23 @@
|
||||
package cn.novalon.manage.sys.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 认证响应DTO
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-14
|
||||
*/
|
||||
@Schema(description = "用户认证响应")
|
||||
public class AuthResponse {
|
||||
|
||||
@Schema(description = "JWT访问令牌", example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
|
||||
private String token;
|
||||
|
||||
@Schema(description = "用户ID", example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户名", example = "admin")
|
||||
private String username;
|
||||
|
||||
public AuthResponse() {
|
||||
|
||||
+6
@@ -6,6 +6,8 @@ import cn.novalon.manage.sys.dto.response.AuthResponse;
|
||||
import cn.novalon.manage.sys.security.JwtTokenProvider;
|
||||
import cn.novalon.manage.sys.core.domain.SysUser;
|
||||
import cn.novalon.manage.sys.core.service.ISysUserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -33,6 +35,7 @@ import java.util.stream.Collectors;
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
@Component
|
||||
@Tag(name = "认证管理", description = "登录认证相关操作")
|
||||
public class SysAuthHandler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SysAuthHandler.class);
|
||||
@@ -47,6 +50,7 @@ public class SysAuthHandler {
|
||||
this.jwtTokenProvider = jwtTokenProvider;
|
||||
}
|
||||
|
||||
@Operation(summary = "用户登录", description = "使用用户名和密码登录系统")
|
||||
public Mono<ServerResponse> login(ServerRequest request) {
|
||||
return request.bodyToMono(LoginRequest.class)
|
||||
.filter(loginRequest -> loginRequest.getUsername() != null
|
||||
@@ -117,6 +121,7 @@ public class SysAuthHandler {
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "用户注册", description = "注册新用户")
|
||||
public Mono<ServerResponse> register(ServerRequest request) {
|
||||
return request.bodyToMono(UserRegisterRequest.class)
|
||||
.flatMap(registerRequest -> {
|
||||
@@ -145,6 +150,7 @@ public class SysAuthHandler {
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "用户登出", description = "用户登出系统")
|
||||
public Mono<ServerResponse> logout(ServerRequest request) {
|
||||
return ServerResponse.ok().build();
|
||||
}
|
||||
|
||||
+9
@@ -2,6 +2,8 @@ package cn.novalon.manage.sys.handler.config;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.SysConfig;
|
||||
import cn.novalon.manage.sys.core.service.ISysConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
@@ -15,6 +17,7 @@ import reactor.core.publisher.Mono;
|
||||
* @date 2026-03-14
|
||||
*/
|
||||
@Component
|
||||
@Tag(name = "配置管理", description = "系统配置相关操作")
|
||||
public class SysConfigHandler {
|
||||
|
||||
private final ISysConfigService configService;
|
||||
@@ -23,11 +26,13 @@ public class SysConfigHandler {
|
||||
this.configService = configService;
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有配置", description = "获取系统中所有配置列表")
|
||||
public Mono<ServerResponse> getAllConfigs(ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.body(configService.findAll(), SysConfig.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID获取配置", description = "根据配置ID获取配置详细信息")
|
||||
public Mono<ServerResponse> getConfigById(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return configService.findById(id)
|
||||
@@ -35,6 +40,7 @@ public class SysConfigHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "根据键获取配置", description = "根据配置键获取配置详细信息")
|
||||
public Mono<ServerResponse> getConfigByKey(ServerRequest request) {
|
||||
String configKey = request.pathVariable("configKey");
|
||||
return configService.findByConfigKey(configKey)
|
||||
@@ -42,12 +48,14 @@ public class SysConfigHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "创建配置", description = "创建新配置")
|
||||
public Mono<ServerResponse> createConfig(ServerRequest request) {
|
||||
return request.bodyToMono(SysConfig.class)
|
||||
.flatMap(configService::save)
|
||||
.flatMap(config -> ServerResponse.status(HttpStatus.CREATED).bodyValue(config));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新配置", description = "更新配置信息")
|
||||
public Mono<ServerResponse> updateConfig(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return request.bodyToMono(SysConfig.class)
|
||||
@@ -62,6 +70,7 @@ public class SysConfigHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "删除配置", description = "删除指定配置")
|
||||
public Mono<ServerResponse> deleteConfig(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return configService.deleteById(id)
|
||||
|
||||
+15
@@ -4,6 +4,8 @@ import cn.novalon.manage.sys.core.domain.SysDictType;
|
||||
import cn.novalon.manage.sys.core.domain.SysDictData;
|
||||
import cn.novalon.manage.sys.core.service.ISysDictTypeService;
|
||||
import cn.novalon.manage.sys.core.service.ISysDictDataService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
@@ -17,6 +19,7 @@ import reactor.core.publisher.Mono;
|
||||
* @date 2026-03-14
|
||||
*/
|
||||
@Component
|
||||
@Tag(name = "字典管理", description = "字典类型和字典数据相关操作")
|
||||
public class SysDictHandler {
|
||||
|
||||
private final ISysDictTypeService dictTypeService;
|
||||
@@ -27,11 +30,13 @@ public class SysDictHandler {
|
||||
this.dictDataService = dictDataService;
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有字典类型", description = "获取系统中所有字典类型列表")
|
||||
public Mono<ServerResponse> getAllDictTypes(ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.body(dictTypeService.findAll(), SysDictType.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID获取字典类型", description = "根据字典类型ID获取详细信息")
|
||||
public Mono<ServerResponse> getDictTypeById(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return dictTypeService.findById(id)
|
||||
@@ -39,6 +44,7 @@ public class SysDictHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "根据类型获取字典类型", description = "根据字典类型代码获取详细信息")
|
||||
public Mono<ServerResponse> getDictTypeByType(ServerRequest request) {
|
||||
String dictType = request.pathVariable("dictType");
|
||||
return dictTypeService.findByDictType(dictType)
|
||||
@@ -46,12 +52,14 @@ public class SysDictHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "创建字典类型", description = "创建新的字典类型")
|
||||
public Mono<ServerResponse> createDictType(ServerRequest request) {
|
||||
return request.bodyToMono(SysDictType.class)
|
||||
.flatMap(dictTypeService::save)
|
||||
.flatMap(dt -> ServerResponse.status(HttpStatus.CREATED).bodyValue(dt));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新字典类型", description = "更新字典类型信息")
|
||||
public Mono<ServerResponse> updateDictType(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return request.bodyToMono(SysDictType.class)
|
||||
@@ -66,17 +74,20 @@ public class SysDictHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "删除字典类型", description = "删除指定字典类型")
|
||||
public Mono<ServerResponse> deleteDictType(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return dictTypeService.deleteById(id)
|
||||
.then(ServerResponse.noContent().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有字典数据", description = "获取系统中所有字典数据列表")
|
||||
public Mono<ServerResponse> getAllDictData(ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.body(dictDataService.findAll(), SysDictData.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID获取字典数据", description = "根据字典数据ID获取详细信息")
|
||||
public Mono<ServerResponse> getDictDataById(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return dictDataService.findById(id)
|
||||
@@ -84,18 +95,21 @@ public class SysDictHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "根据类型获取字典数据", description = "根据字典类型获取字典数据列表")
|
||||
public Mono<ServerResponse> getDictDataByType(ServerRequest request) {
|
||||
String dictType = request.pathVariable("dictType");
|
||||
return ServerResponse.ok()
|
||||
.body(dictDataService.findByDictType(dictType), SysDictData.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "创建字典数据", description = "创建新的字典数据")
|
||||
public Mono<ServerResponse> createDictData(ServerRequest request) {
|
||||
return request.bodyToMono(SysDictData.class)
|
||||
.flatMap(dictDataService::save)
|
||||
.flatMap(dd -> ServerResponse.status(HttpStatus.CREATED).bodyValue(dd));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新字典数据", description = "更新字典数据信息")
|
||||
public Mono<ServerResponse> updateDictData(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return request.bodyToMono(SysDictData.class)
|
||||
@@ -114,6 +128,7 @@ public class SysDictHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "删除字典数据", description = "删除指定字典数据")
|
||||
public Mono<ServerResponse> deleteDictData(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return dictDataService.deleteById(id)
|
||||
|
||||
+10
@@ -2,6 +2,8 @@ package cn.novalon.manage.sys.handler.dictionary;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.Dictionary;
|
||||
import cn.novalon.manage.sys.core.service.IDictionaryService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
@@ -15,6 +17,7 @@ import reactor.core.publisher.Mono;
|
||||
* @date 2026-03-14
|
||||
*/
|
||||
@Component
|
||||
@Tag(name = "字典管理", description = "字典数据相关操作")
|
||||
public class DictionaryHandler {
|
||||
|
||||
private final IDictionaryService dictionaryService;
|
||||
@@ -23,11 +26,13 @@ public class DictionaryHandler {
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有字典", description = "获取系统中所有字典列表")
|
||||
public Mono<ServerResponse> getAllDictionaries(ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.body(dictionaryService.findAll(), Dictionary.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID获取字典", description = "根据字典ID获取字典详细信息")
|
||||
public Mono<ServerResponse> getDictionaryById(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return dictionaryService.findById(id)
|
||||
@@ -35,12 +40,14 @@ public class DictionaryHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "根据类型获取字典", description = "根据字典类型获取字典列表")
|
||||
public Mono<ServerResponse> getDictionariesByType(ServerRequest request) {
|
||||
String type = request.pathVariable("type");
|
||||
return ServerResponse.ok()
|
||||
.body(dictionaryService.findByType(type), Dictionary.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "检查字典存在性", description = "检查指定类型和代码的字典是否存在")
|
||||
public Mono<ServerResponse> checkTypeAndCodeExists(ServerRequest request) {
|
||||
String type = request.queryParam("type").orElse(null);
|
||||
String code = request.queryParam("code").orElse(null);
|
||||
@@ -48,12 +55,14 @@ public class DictionaryHandler {
|
||||
.flatMap(exists -> ServerResponse.ok().bodyValue(exists));
|
||||
}
|
||||
|
||||
@Operation(summary = "创建字典", description = "创建新字典")
|
||||
public Mono<ServerResponse> createDictionary(ServerRequest request) {
|
||||
return request.bodyToMono(Dictionary.class)
|
||||
.flatMap(dictionaryService::save)
|
||||
.flatMap(dict -> ServerResponse.status(HttpStatus.CREATED).bodyValue(dict));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新字典", description = "更新字典信息")
|
||||
public Mono<ServerResponse> updateDictionary(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return request.bodyToMono(Dictionary.class)
|
||||
@@ -62,6 +71,7 @@ public class DictionaryHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "删除字典", description = "删除指定字典")
|
||||
public Mono<ServerResponse> deleteDictionary(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return dictionaryService.deleteById(id)
|
||||
|
||||
+13
@@ -5,6 +5,8 @@ import cn.novalon.manage.sys.core.domain.SysExceptionLog;
|
||||
import cn.novalon.manage.sys.core.service.ISysLoginLogService;
|
||||
import cn.novalon.manage.sys.core.service.ISysExceptionLogService;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
@@ -18,6 +20,7 @@ import reactor.core.publisher.Mono;
|
||||
* @date 2026-03-14
|
||||
*/
|
||||
@Component
|
||||
@Tag(name = "日志管理", description = "登录日志和异常日志相关操作")
|
||||
public class SysLogHandler {
|
||||
|
||||
private final ISysLoginLogService loginLogService;
|
||||
@@ -28,11 +31,13 @@ public class SysLogHandler {
|
||||
this.exceptionLogService = exceptionLogService;
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有登录日志", description = "获取系统中所有登录日志列表")
|
||||
public Mono<ServerResponse> getAllLoginLogs(ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.body(loginLogService.findAll(), SysLoginLog.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID获取登录日志", description = "根据登录日志ID获取详细信息")
|
||||
public Mono<ServerResponse> getLoginLogById(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return loginLogService.findById(id)
|
||||
@@ -40,12 +45,14 @@ public class SysLogHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "创建登录日志", description = "创建新的登录日志")
|
||||
public Mono<ServerResponse> createLoginLog(ServerRequest request) {
|
||||
return request.bodyToMono(SysLoginLog.class)
|
||||
.flatMap(loginLogService::save)
|
||||
.flatMap(log -> ServerResponse.status(HttpStatus.CREATED).bodyValue(log));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页获取登录日志", description = "根据分页参数获取登录日志列表")
|
||||
public Mono<ServerResponse> getLoginLogsByPage(ServerRequest request) {
|
||||
int page = Integer.parseInt(request.queryParam("page").orElse("0"));
|
||||
int size = Integer.parseInt(request.queryParam("size").orElse("10"));
|
||||
@@ -64,16 +71,19 @@ public class SysLogHandler {
|
||||
.flatMap(response -> ServerResponse.ok().bodyValue(response));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取登录日志总数", description = "获取系统中登录日志总数")
|
||||
public Mono<ServerResponse> getLoginLogCount(ServerRequest request) {
|
||||
return loginLogService.count()
|
||||
.flatMap(count -> ServerResponse.ok().bodyValue(count));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有异常日志", description = "获取系统中所有异常日志列表")
|
||||
public Mono<ServerResponse> getAllExceptionLogs(ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.body(exceptionLogService.findAll(), SysExceptionLog.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID获取异常日志", description = "根据异常日志ID获取详细信息")
|
||||
public Mono<ServerResponse> getExceptionLogById(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return exceptionLogService.findById(id)
|
||||
@@ -81,12 +91,14 @@ public class SysLogHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "创建异常日志", description = "创建新的异常日志")
|
||||
public Mono<ServerResponse> createExceptionLog(ServerRequest request) {
|
||||
return request.bodyToMono(SysExceptionLog.class)
|
||||
.flatMap(exceptionLogService::save)
|
||||
.flatMap(log -> ServerResponse.status(HttpStatus.CREATED).bodyValue(log));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页获取异常日志", description = "根据分页参数获取异常日志列表")
|
||||
public Mono<ServerResponse> getExceptionLogsByPage(ServerRequest request) {
|
||||
int page = Integer.parseInt(request.queryParam("page").orElse("0"));
|
||||
int size = Integer.parseInt(request.queryParam("size").orElse("10"));
|
||||
@@ -105,6 +117,7 @@ public class SysLogHandler {
|
||||
.flatMap(response -> ServerResponse.ok().bodyValue(response));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取异常日志总数", description = "获取系统中异常日志总数")
|
||||
public Mono<ServerResponse> getExceptionLogCount(ServerRequest request) {
|
||||
return exceptionLogService.count()
|
||||
.flatMap(count -> ServerResponse.ok().bodyValue(count));
|
||||
|
||||
+11
@@ -6,6 +6,8 @@ import cn.novalon.manage.sys.dto.request.MenuCreateRequest;
|
||||
import cn.novalon.manage.sys.dto.request.MenuUpdateRequest;
|
||||
import cn.novalon.manage.sys.core.command.CreateMenuCommand;
|
||||
import cn.novalon.manage.sys.core.command.UpdateMenuCommand;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
@@ -19,6 +21,7 @@ import reactor.core.publisher.Mono;
|
||||
* @date 2026-03-14
|
||||
*/
|
||||
@Component
|
||||
@Tag(name = "菜单管理", description = "系统菜单相关操作")
|
||||
public class MenuHandler {
|
||||
|
||||
private final ISysMenuService menuService;
|
||||
@@ -27,11 +30,13 @@ public class MenuHandler {
|
||||
this.menuService = menuService;
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有菜单", description = "获取系统中所有菜单列表")
|
||||
public Mono<ServerResponse> getAllMenus(ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.body(menuService.findAll(), SysMenu.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID获取菜单", description = "根据菜单ID获取菜单详细信息")
|
||||
public Mono<ServerResponse> getMenuById(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return menuService.findById(id)
|
||||
@@ -39,11 +44,13 @@ public class MenuHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取菜单树", description = "获取系统菜单树结构")
|
||||
public Mono<ServerResponse> getMenuTree(ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.body(menuService.buildMenuTree(menuService.findAll()), SysMenu.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据父菜单获取子菜单", description = "根据父菜单ID获取子菜单列表")
|
||||
public Mono<ServerResponse> getMenusByParent(ServerRequest request) {
|
||||
Long parentId = request.queryParam("parentId")
|
||||
.map(Long::valueOf)
|
||||
@@ -52,12 +59,14 @@ public class MenuHandler {
|
||||
.body(menuService.findByParentId(parentId), SysMenu.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据类型获取菜单", description = "根据菜单类型获取菜单列表")
|
||||
public Mono<ServerResponse> getMenusByType(ServerRequest request) {
|
||||
String menuType = request.queryParam("menuType").orElse(null);
|
||||
return ServerResponse.ok()
|
||||
.body(menuService.findAll().filter(menu -> menuType == null || menuType.equals(menu.getMenuType())), SysMenu.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "创建菜单", description = "创建新菜单")
|
||||
public Mono<ServerResponse> createMenu(ServerRequest request) {
|
||||
return request.bodyToMono(MenuCreateRequest.class)
|
||||
.map(req -> CreateMenuCommand.of(
|
||||
@@ -73,6 +82,7 @@ public class MenuHandler {
|
||||
.flatMap(menu -> ServerResponse.status(HttpStatus.CREATED).bodyValue(menu));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新菜单", description = "更新菜单信息")
|
||||
public Mono<ServerResponse> updateMenu(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return request.bodyToMono(MenuUpdateRequest.class)
|
||||
@@ -91,6 +101,7 @@ public class MenuHandler {
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "删除菜单", description = "删除指定菜单")
|
||||
public Mono<ServerResponse> deleteMenu(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return menuService.deleteMenu(id)
|
||||
|
||||
+4
@@ -3,6 +3,8 @@ package cn.novalon.manage.sys.handler.stats;
|
||||
import cn.novalon.manage.sys.core.service.ISysUserService;
|
||||
import cn.novalon.manage.sys.core.service.ISysRoleService;
|
||||
import cn.novalon.manage.sys.core.service.IOperationLogService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
@@ -15,6 +17,7 @@ import reactor.core.publisher.Mono;
|
||||
* @date 2026-03-14
|
||||
*/
|
||||
@Component
|
||||
@Tag(name = "统计信息", description = "系统统计相关操作")
|
||||
public class StatsHandler {
|
||||
|
||||
private final ISysUserService userService;
|
||||
@@ -27,6 +30,7 @@ public class StatsHandler {
|
||||
this.operationLogService = operationLogService;
|
||||
}
|
||||
|
||||
@Operation(summary = "获取系统概览", description = "获取系统统计概览信息")
|
||||
public Mono<ServerResponse> getOverview(ServerRequest request) {
|
||||
return Mono.zip(
|
||||
userService.count(),
|
||||
|
||||
+4
-2
@@ -1,5 +1,7 @@
|
||||
package cn.novalon.manage.sys.primitive;
|
||||
|
||||
import cn.novalon.manage.common.exception.ErrorCode;
|
||||
import cn.novalon.manage.common.exception.ValidationException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
@@ -26,7 +28,7 @@ public final class Email {
|
||||
|
||||
public static Email of(String value) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
throw new IllegalArgumentException("Email is required");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_REQUIRED, "Email is required");
|
||||
}
|
||||
validate(value);
|
||||
return new Email(value);
|
||||
@@ -42,7 +44,7 @@ public final class Email {
|
||||
|
||||
private static void validate(String value) {
|
||||
if (!EMAIL_PATTERN.matcher(value).matches()) {
|
||||
throw new IllegalArgumentException("Invalid email format");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_FORMAT, "Invalid email format");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
package cn.novalon.manage.sys.primitive;
|
||||
|
||||
import cn.novalon.manage.common.exception.ErrorCode;
|
||||
import cn.novalon.manage.common.exception.ValidationException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -24,7 +26,7 @@ public final class Password {
|
||||
|
||||
public static Password of(String value) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
throw new IllegalArgumentException("Password is required");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_REQUIRED, "Password is required");
|
||||
}
|
||||
validate(value);
|
||||
return new Password(value);
|
||||
@@ -32,7 +34,8 @@ public final class Password {
|
||||
|
||||
private static void validate(String value) {
|
||||
if (value.length() < MIN_LENGTH) {
|
||||
throw new IllegalArgumentException("Password must be at least " + MIN_LENGTH + " characters long");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_LENGTH,
|
||||
"Password must be at least " + MIN_LENGTH + " characters long");
|
||||
}
|
||||
|
||||
boolean hasUppercase = value.chars().anyMatch(Character::isUpperCase);
|
||||
@@ -41,7 +44,7 @@ public final class Password {
|
||||
boolean hasSpecial = value.chars().anyMatch(c -> !Character.isLetterOrDigit(c));
|
||||
|
||||
if (!hasUppercase || !hasLowercase || !hasDigit || !hasSpecial) {
|
||||
throw new IllegalArgumentException(
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_VALUE,
|
||||
"Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character");
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -1,5 +1,7 @@
|
||||
package cn.novalon.manage.sys.primitive;
|
||||
|
||||
import cn.novalon.manage.common.exception.ErrorCode;
|
||||
import cn.novalon.manage.common.exception.ValidationException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
@@ -28,7 +30,7 @@ public final class Username {
|
||||
|
||||
public static Username of(String value) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
throw new IllegalArgumentException("Username is required");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_REQUIRED, "Username is required");
|
||||
}
|
||||
validate(value);
|
||||
return new Username(value);
|
||||
@@ -38,15 +40,18 @@ public final class Username {
|
||||
String trimmed = value.trim();
|
||||
|
||||
if (trimmed.length() < MIN_LENGTH) {
|
||||
throw new IllegalArgumentException("Username must be at least " + MIN_LENGTH + " characters long");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_LENGTH,
|
||||
"Username must be at least " + MIN_LENGTH + " characters long");
|
||||
}
|
||||
|
||||
if (trimmed.length() > MAX_LENGTH) {
|
||||
throw new IllegalArgumentException("Username must be at most " + MAX_LENGTH + " characters long");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_LENGTH,
|
||||
"Username must be at most " + MAX_LENGTH + " characters long");
|
||||
}
|
||||
|
||||
if (!USERNAME_PATTERN.matcher(trimmed).matches()) {
|
||||
throw new IllegalArgumentException("Username can only contain letters, numbers, and underscores");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_FORMAT,
|
||||
"Username can only contain letters, numbers, and underscores");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
package cn.novalon.manage.sys.primitive;
|
||||
|
||||
import cn.novalon.manage.common.exception.ErrorCode;
|
||||
import cn.novalon.manage.common.exception.ValidationException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Password详细测试 - 提升分支覆盖率
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-24
|
||||
*/
|
||||
class PasswordDetailedTest {
|
||||
|
||||
@Test
|
||||
void testValidPassword() {
|
||||
Password password = Password.of("Valid@123");
|
||||
assertNotNull(password);
|
||||
assertEquals("Valid@123", password.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullPassword() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of(null);
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_REQUIRED, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyPassword() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_REQUIRED, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWhitespacePassword() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of(" ");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_REQUIRED, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTooShortPassword() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("Short1@");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_LENGTH, exception.getErrorCode());
|
||||
assertTrue(exception.getMessage().contains("at least 8 characters"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExactlyMinLengthPassword() {
|
||||
Password password = Password.of("Valid1@");
|
||||
assertNotNull(password);
|
||||
assertEquals("Valid1@", password.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithoutUppercase() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("lowercase1@");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
assertTrue(exception.getMessage().contains("uppercase letter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithoutLowercase() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("UPPERCASE1@");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
assertTrue(exception.getMessage().contains("lowercase letter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithoutDigit() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("NoDigits@");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
assertTrue(exception.getMessage().contains("digit"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithoutSpecialCharacter() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("NoSpecial123");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
assertTrue(exception.getMessage().contains("special character"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Valid@123",
|
||||
"Another@456",
|
||||
"Test@789",
|
||||
"Complex@Pass123",
|
||||
"Simple@Pass456"
|
||||
})
|
||||
void testMultipleValidPasswords(String password) {
|
||||
Password pwd = Password.of(password);
|
||||
assertNotNull(pwd);
|
||||
assertEquals(password, pwd.getValue());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"lowercase@123",
|
||||
"UPPERCASE@123",
|
||||
"MixedCase@abc",
|
||||
"MixedCase123"
|
||||
})
|
||||
void testMultipleInvalidPasswords(String password) {
|
||||
assertThrows(ValidationException.class, () -> {
|
||||
Password.of(password);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithOnlyUppercaseAndDigit() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("UPPERCASE123");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithOnlyLowercaseAndDigit() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("lowercase123");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithOnlyUppercaseAndSpecial() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("UPPERCASE@");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithOnlyLowercaseAndSpecial() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("lowercase@");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithOnlyDigitAndSpecial() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("123456@");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithMultipleSpecialCharacters() {
|
||||
Password password = Password.of("Valid@#$123");
|
||||
assertNotNull(password);
|
||||
assertEquals("Valid@#$123", password.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithSpaces() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("Valid @123");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_REQUIRED, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVeryLongPassword() {
|
||||
Password password = Password.of("VeryLongPassword@1234567890");
|
||||
assertNotNull(password);
|
||||
assertEquals("VeryLongPassword@1234567890", password.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordEquals() {
|
||||
Password password1 = Password.of("Valid@123");
|
||||
Password password2 = Password.of("Valid@123");
|
||||
assertEquals(password1, password2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordNotEquals() {
|
||||
Password password1 = Password.of("Valid@123");
|
||||
Password password2 = Password.of("Different@456");
|
||||
assertNotEquals(password1, password2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordEqualsNull() {
|
||||
Password password = Password.of("Valid@123");
|
||||
assertNotEquals(password, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordEqualsDifferentClass() {
|
||||
Password password = Password.of("Valid@123");
|
||||
assertNotEquals(password, "Valid@123");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordEqualsSameInstance() {
|
||||
Password password = Password.of("Valid@123");
|
||||
assertEquals(password, password);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordHashCode() {
|
||||
Password password1 = Password.of("Valid@123");
|
||||
Password password2 = Password.of("Valid@123");
|
||||
assertEquals(password1.hashCode(), password2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordHashCodeDifferent() {
|
||||
Password password1 = Password.of("Valid@123");
|
||||
Password password2 = Password.of("Different@456");
|
||||
assertNotEquals(password1.hashCode(), password2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordToString() {
|
||||
Password password = Password.of("Valid@123");
|
||||
String toString = password.toString();
|
||||
assertEquals("********", toString);
|
||||
assertFalse(toString.contains("Valid"));
|
||||
assertFalse(toString.contains("123"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithUnicodeCharacters() {
|
||||
Password password = Password.of("密码@123");
|
||||
assertNotNull(password);
|
||||
assertEquals("密码@123", password.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithNumbersOnly() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("12345678");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithLettersOnly() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("abcdefgh");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithSpecialOnly() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("@#$%^&*()");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithUppercaseLowercaseOnly() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("AbCdEfGh");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithUppercaseDigitOnly() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("ABCDEFGH12345678");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordWithLowercaseDigitOnly() {
|
||||
ValidationException exception = assertThrows(ValidationException.class, () -> {
|
||||
Password.of("abcdefgh12345678");
|
||||
});
|
||||
assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user