feat: 用户 UUID 持久化与统一异常契约(Flyway baseline / UUIDv7 / 错误码透传)
- Flyway V1 baseline:identity 全部 5 表 + media.assets + platform 最小硬依赖(set_updated_at/regions),无 fixture 凭据;dev 种子独立为 afterMigrate 回调且默认不执行 - patbond-user 迁移到 PostgreSQL:UUIDv7 主键、JdbcClient 仓储、bcrypt 密码、唯一性依赖 DB 约束翻译为 409、E.164 手机号校验对齐 ck_users_phone - 统一异常契约:common 新增 ErrorCode/BusinessException,两服务 GlobalExceptionHandler;auth 经 ApiErrorDecoder 原码透传下游错误,修复状态码折叠(审计 M1) - 门禁:JAVA_HOME=jdk17 ./mvnw clean test,37 个测试 0 失败(含 Testcontainers postgres:16 集成测试),BUILD SUCCESS Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package com.patbond.patbond.common.error;
|
||||
|
||||
/**
|
||||
* Carries a business error code plus its HTTP status through the service
|
||||
* layers. Also used on the auth side to re-raise errors decoded from a
|
||||
* downstream {@code ApiResponse} body verbatim, which is why it stores raw
|
||||
* ints instead of only an {@link ErrorCode} constant.
|
||||
*/
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
private final int code;
|
||||
private final int httpStatus;
|
||||
|
||||
public BusinessException(ErrorCode errorCode) {
|
||||
this(errorCode.getCode(), errorCode.getHttpStatus(), errorCode.getDefaultMessage());
|
||||
}
|
||||
|
||||
public BusinessException(ErrorCode errorCode, String message) {
|
||||
this(errorCode.getCode(), errorCode.getHttpStatus(), message);
|
||||
}
|
||||
|
||||
public BusinessException(int code, int httpStatus, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.httpStatus = httpStatus;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public int getHttpStatus() {
|
||||
return httpStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.patbond.patbond.common.error;
|
||||
|
||||
/**
|
||||
* Stable business error codes shared by all services (development-plan 6.1:
|
||||
* errors must carry both a correct HTTP status and a stable business code).
|
||||
* The numeric code is what clients switch on; the HTTP status is the
|
||||
* transport-level view of the same failure. Codes are contract: never reuse
|
||||
* or renumber a released value.
|
||||
*/
|
||||
public enum ErrorCode {
|
||||
|
||||
VALIDATION_ERROR(40000, 400, "参数校验失败"),
|
||||
INVALID_CREDENTIALS(40100, 401, "用户名或密码错误"),
|
||||
USER_NOT_FOUND(40400, 404, "用户不存在"),
|
||||
USERNAME_EXISTS(40900, 409, "用户名已存在"),
|
||||
PHONE_EXISTS(40901, 409, "手机号已被使用"),
|
||||
INTERNAL_ERROR(50000, 500, "服务器内部错误"),
|
||||
DOWNSTREAM_UNAVAILABLE(50300, 503, "依赖服务暂不可用");
|
||||
|
||||
private final int code;
|
||||
private final int httpStatus;
|
||||
private final String defaultMessage;
|
||||
|
||||
ErrorCode(int code, int httpStatus, String defaultMessage) {
|
||||
this.code = code;
|
||||
this.httpStatus = httpStatus;
|
||||
this.defaultMessage = defaultMessage;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public int getHttpStatus() {
|
||||
return httpStatus;
|
||||
}
|
||||
|
||||
public String getDefaultMessage() {
|
||||
return defaultMessage;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.patbond.patbond.common.user;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class CreateUserRequest {
|
||||
@@ -16,7 +17,8 @@ public class CreateUserRequest {
|
||||
@Size(max = 32, message = "昵称长度不能超过32位")
|
||||
private String nickname;
|
||||
|
||||
@Size(max = 20, message = "手机号长度不能超过20位")
|
||||
// Aligned with the identity.users ck_users_phone CHECK constraint (E.164).
|
||||
@Pattern(regexp = "^\\+[1-9][0-9]{7,14}$", message = "手机号必须为 E.164 格式,例如 +8613800138000")
|
||||
private String phone;
|
||||
|
||||
public CreateUserRequest() {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
package com.patbond.patbond.common.user;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
public class UserProfile {
|
||||
|
||||
private Long id;
|
||||
private UUID id;
|
||||
private String username;
|
||||
private String nickname;
|
||||
private String phone;
|
||||
private LocalDateTime createdAt;
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
public UserProfile() {
|
||||
}
|
||||
|
||||
public UserProfile(Long id, String username, String nickname, String phone, LocalDateTime createdAt) {
|
||||
public UserProfile(UUID id, String username, String nickname, String phone, OffsetDateTime createdAt) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.nickname = nickname;
|
||||
@@ -21,11 +22,11 @@ public class UserProfile {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@@ -53,11 +54,11 @@ public class UserProfile {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
public OffsetDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
public void setCreatedAt(OffsetDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -1,25 +1,27 @@
|
||||
package com.patbond.patbond.common.user;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class VerifyPasswordResponse {
|
||||
|
||||
private Long userId;
|
||||
private UUID userId;
|
||||
private String username;
|
||||
private String nickname;
|
||||
|
||||
public VerifyPasswordResponse() {
|
||||
}
|
||||
|
||||
public VerifyPasswordResponse(Long userId, String username, String nickname) {
|
||||
public VerifyPasswordResponse(UUID userId, String username, String nickname) {
|
||||
this.userId = userId;
|
||||
this.username = username;
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
public UUID getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
public void setUserId(UUID userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user