diff --git a/backend/gateway/pkg/response/response.go b/backend/gateway/pkg/response/response.go index 383fd9e..fe372be 100644 --- a/backend/gateway/pkg/response/response.go +++ b/backend/gateway/pkg/response/response.go @@ -170,27 +170,28 @@ func HandleError(c *gin.Context, err error) { } msg := CleanErrorMessage(err) - msgLower := strings.ToLower(msg) // 根据错误类型返回对应的 HTTP 状态码 switch { - case strings.Contains(msgLower, "not found") || strings.Contains(msgLower, "不存在"): + case strings.Contains(msg, "not found") || strings.Contains(msg, "不存在"): NotFound(c, msg) - case strings.Contains(msgLower, "unauthorized") || - strings.Contains(msgLower, "token") || - strings.Contains(msgLower, "请先登录"): + case strings.Contains(msg, "unauthorized") || + strings.Contains(msg, "token") || + strings.Contains(msg, "请先登录"): Unauthorized(c, msg) - case strings.Contains(msgLower, "invalid") || - strings.Contains(msgLower, "格式不正确") || - strings.Contains(msgLower, "过长") || - strings.Contains(msgLower, "过短"): + case strings.Contains(msg, "invalid") || + strings.Contains(msg, "格式不正确") || + strings.Contains(msg, "过长") || + strings.Contains(msg, "过短"): BadRequest(c, msg) - case strings.Contains(msgLower, "already exists") || - strings.Contains(msgLower, "已被注册"): + case strings.Contains(msg, "already exists") || + strings.Contains(msg, "已被注册"): Error(c, http.StatusConflict, msg) - case strings.Contains(msgLower, "forbidden") || - strings.Contains(msgLower, "permission denied") || - strings.Contains(msgLower, "权限不足"): + case strings.Contains(msg, "forbidden") || + strings.Contains(msg, "permission denied") || + strings.Contains(msg, "权限不足") || + strings.Contains(msg, "账号已被冻结") || + strings.Contains(msg, "账号已被封禁"): Forbidden(c, msg) default: InternalError(c, msg) diff --git a/backend/pkg/errors/errors.go b/backend/pkg/errors/errors.go index 983e3e2..0b37d65 100644 --- a/backend/pkg/errors/errors.go +++ b/backend/pkg/errors/errors.go @@ -55,6 +55,10 @@ var ( ErrInvalidAssetStatus = errors.New("资产状态无效") ErrInvalidMintOrderStatus = errors.New("订单状态无效") + // 账号状态相关错误 + ErrAccountFrozen = errors.New("账号已被冻结") + ErrAccountBanned = errors.New("账号已被封禁") + // 活动服务相关错误 ErrActivityNotFound = errors.New("活动不存在") ErrActivityItemNotFound = errors.New("活动道具不存在") @@ -79,7 +83,7 @@ func ToStatusCode(err error) pb.StatusCode { return pb.StatusCode_STATUS_BAD_REQUEST case ErrInvalidPassword, ErrInvalidToken, ErrTokenExpired, ErrTokenMismatch: return pb.StatusCode_STATUS_UNAUTHORIZED - case ErrUserInactive: + case ErrAccountFrozen, ErrAccountBanned: return pb.StatusCode_STATUS_FORBIDDEN case ErrInvalidMobile, ErrPasswordTooShort, ErrInvalidStarID, ErrInvalidUserID, ErrMaxIdentitiesReached, ErrInvalidNickname: return pb.StatusCode_STATUS_BAD_REQUEST diff --git a/backend/pkg/models/user_account_status.go b/backend/pkg/models/user_account_status.go new file mode 100644 index 0000000..cb08607 --- /dev/null +++ b/backend/pkg/models/user_account_status.go @@ -0,0 +1,67 @@ +package models + +import ( + "time" + + "gorm.io/gorm" +) + +// UserAccountStatus 用户账号状态表模型 +type UserAccountStatus struct { + ID int64 `gorm:"primaryKey;autoIncrement;column:id"` + UserID int64 `gorm:"not null;uniqueIndex:uk_user_account_status_user_id;column:user_id"` + Status string `gorm:"type:varchar(20);not null;column:status"` // frozen/normal/banned + Reason *string `gorm:"type:text;column:reason"` // 冻结/封号原因 + FrozenUntil *int64 `gorm:"column:frozen_until"` // 冻结解封时间(毫秒时间戳) + CreatedAt int64 `gorm:"not null;column:created_at"` + UpdatedAt int64 `gorm:"not null;column:updated_at"` +} + +// TableName 指定表名 +func (UserAccountStatus) TableName() string { + return "user_account_status" +} + +// BeforeCreate 创建前钩子 +func (u *UserAccountStatus) BeforeCreate(tx *gorm.DB) error { + now := time.Now().UnixMilli() + u.CreatedAt = now + u.UpdatedAt = now + return nil +} + +// BeforeUpdate 更新前钩子 +func (u *UserAccountStatus) BeforeUpdate(tx *gorm.DB) error { + u.UpdatedAt = time.Now().UnixMilli() + return nil +} + +// Status constants +const ( + AccountStatusNormal = "normal" + AccountStatusFrozen = "frozen" + AccountStatusBanned = "banned" +) + +// IsNormal 判断账号状态是否正常 +func (u *UserAccountStatus) IsNormal() bool { + return u.Status == AccountStatusNormal +} + +// IsFrozen 判断账号是否被冻结 +func (u *UserAccountStatus) IsFrozen() bool { + return u.Status == AccountStatusFrozen +} + +// IsBanned 判断账号是否被封号 +func (u *UserAccountStatus) IsBanned() bool { + return u.Status == AccountStatusBanned +} + +// GetFrozenUntilTime 返回冻结解封时间(如果是被冻结状态) +func (u *UserAccountStatus) GetFrozenUntilTime() time.Time { + if u.FrozenUntil != nil { + return time.UnixMilli(*u.FrozenUntil) + } + return time.Time{} +} diff --git a/backend/services/userService/repository/user_repository.go b/backend/services/userService/repository/user_repository.go index 42a7640..9a4edf6 100644 --- a/backend/services/userService/repository/user_repository.go +++ b/backend/services/userService/repository/user_repository.go @@ -38,6 +38,9 @@ type UserRepository interface { // UpdateAvatar 更新用户头像 UpdateAvatar(userID int64, avatarURL string) error + + // GetAccountStatus 获取用户账号状态 + GetAccountStatus(userID int64) (*models.UserAccountStatus, error) } // userRepository 用户Repository实现 @@ -228,3 +231,20 @@ func (r *userRepository) UpdateAvatar(userID int64, avatarURL string) error { return nil } + +// GetAccountStatus 获取用户账号状态 +func (r *userRepository) GetAccountStatus(userID int64) (*models.UserAccountStatus, error) { + if userID <= 0 { + return nil, errors.New("invalid user id") + } + + var status models.UserAccountStatus + if err := r.db.Where("user_id = ?", userID).First(&status).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil // 没有记录表示正常 + } + return nil, err + } + + return &status, nil +} diff --git a/frontend/pages/login/login.vue b/frontend/pages/login/login.vue index ab3937e..a4cb1cc 100644 --- a/frontend/pages/login/login.vue +++ b/frontend/pages/login/login.vue @@ -106,6 +106,7 @@