feat(架构): 完善基础架构设计

This commit is contained in:
2025-07-02 16:17:59 +08:00
parent 03e615a8fd
commit 5b4392894f
89 changed files with 18555 additions and 3521 deletions

View File

@@ -0,0 +1,88 @@
package entities
import (
"time"
"gorm.io/gorm"
)
// SMSCode 短信验证码记录
type SMSCode struct {
ID string `gorm:"primaryKey;type:varchar(36)" json:"id"`
Phone string `gorm:"type:varchar(20);not null;index" json:"phone"`
Code string `gorm:"type:varchar(10);not null" json:"-"` // 不返回给前端
Scene SMSScene `gorm:"type:varchar(20);not null" json:"scene"`
Used bool `gorm:"default:false" json:"used"`
ExpiresAt time.Time `gorm:"not null" json:"expires_at"`
UsedAt *time.Time `json:"used_at,omitempty"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
// 额外信息
IP string `gorm:"type:varchar(45)" json:"ip"`
UserAgent string `gorm:"type:varchar(500)" json:"user_agent"`
}
// SMSScene 短信验证码使用场景
type SMSScene string
const (
SMSSceneRegister SMSScene = "register" // 注册
SMSSceneLogin SMSScene = "login" // 登录
SMSSceneChangePassword SMSScene = "change_password" // 修改密码
SMSSceneResetPassword SMSScene = "reset_password" // 重置密码
SMSSceneBind SMSScene = "bind" // 绑定手机号
SMSSceneUnbind SMSScene = "unbind" // 解绑手机号
)
// 实现 Entity 接口
func (s *SMSCode) GetID() string {
return s.ID
}
func (s *SMSCode) GetCreatedAt() time.Time {
return s.CreatedAt
}
func (s *SMSCode) GetUpdatedAt() time.Time {
return s.UpdatedAt
}
// Validate 验证短信验证码
func (s *SMSCode) Validate() error {
if s.Phone == "" {
return &ValidationError{Message: "手机号不能为空"}
}
if s.Code == "" {
return &ValidationError{Message: "验证码不能为空"}
}
if s.Scene == "" {
return &ValidationError{Message: "使用场景不能为空"}
}
if s.ExpiresAt.IsZero() {
return &ValidationError{Message: "过期时间不能为空"}
}
return nil
}
// 业务方法
func (s *SMSCode) IsExpired() bool {
return time.Now().After(s.ExpiresAt)
}
func (s *SMSCode) IsValid() bool {
return !s.Used && !s.IsExpired()
}
func (s *SMSCode) MarkAsUsed() {
s.Used = true
now := time.Now()
s.UsedAt = &now
}
// TableName 指定表名
func (SMSCode) TableName() string {
return "sms_codes"
}

View File

@@ -8,37 +8,14 @@ import (
// User 用户实体
type User struct {
ID string `gorm:"primaryKey;type:varchar(36)" json:"id"`
Username string `gorm:"uniqueIndex;type:varchar(50);not null" json:"username"`
Email string `gorm:"uniqueIndex;type:varchar(100);not null" json:"email"`
Password string `gorm:"type:varchar(255);not null" json:"-"`
FirstName string `gorm:"type:varchar(50)" json:"first_name"`
LastName string `gorm:"type:varchar(50)" json:"last_name"`
Phone string `gorm:"type:varchar(20)" json:"phone"`
Avatar string `gorm:"type:varchar(255)" json:"avatar"`
Status UserStatus `gorm:"type:varchar(20);default:'active'" json:"status"`
LastLoginAt *time.Time `json:"last_login_at"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
// 软删除字段
IsDeleted bool `gorm:"default:false" json:"is_deleted"`
// 版本控制
Version int `gorm:"default:1" json:"version"`
ID string `gorm:"primaryKey;type:varchar(36)" json:"id"`
Phone string `gorm:"uniqueIndex;type:varchar(20);not null" json:"phone"`
Password string `gorm:"type:varchar(255);not null" json:"-"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// UserStatus 用户状态枚举
type UserStatus string
const (
UserStatusActive UserStatus = "active"
UserStatusInactive UserStatus = "inactive"
UserStatusSuspended UserStatus = "suspended"
UserStatusPending UserStatus = "pending"
)
// 实现 Entity 接口
func (u *User) GetID() string {
return u.ID
@@ -52,47 +29,13 @@ func (u *User) GetUpdatedAt() time.Time {
return u.UpdatedAt
}
// 业务方法
func (u *User) IsActive() bool {
return u.Status == UserStatusActive && !u.IsDeleted
}
func (u *User) GetFullName() string {
if u.FirstName == "" && u.LastName == "" {
return u.Username
}
return u.FirstName + " " + u.LastName
}
func (u *User) CanLogin() bool {
return u.IsActive() && u.Status != UserStatusSuspended
}
func (u *User) MarkAsDeleted() {
u.IsDeleted = true
u.Status = UserStatusInactive
}
func (u *User) Restore() {
u.IsDeleted = false
u.Status = UserStatusActive
}
func (u *User) UpdateLastLogin() {
now := time.Now()
u.LastLoginAt = &now
}
// 验证方法
func (u *User) Validate() error {
if u.Username == "" {
return NewValidationError("username is required")
}
if u.Email == "" {
return NewValidationError("email is required")
if u.Phone == "" {
return NewValidationError("手机号不能为空")
}
if u.Password == "" {
return NewValidationError("password is required")
return NewValidationError("密码不能为空")
}
return nil
}
@@ -114,25 +57,3 @@ func (e *ValidationError) Error() string {
func NewValidationError(message string) *ValidationError {
return &ValidationError{Message: message}
}
// UserProfile 用户档案(扩展信息)
type UserProfile struct {
ID string `gorm:"primaryKey;type:varchar(36)" json:"id"`
UserID string `gorm:"type:varchar(36);not null;index" json:"user_id"`
Bio string `gorm:"type:text" json:"bio"`
Location string `gorm:"type:varchar(100)" json:"location"`
Website string `gorm:"type:varchar(255)" json:"website"`
Birthday *time.Time `json:"birthday"`
Gender string `gorm:"type:varchar(10)" json:"gender"`
Timezone string `gorm:"type:varchar(50)" json:"timezone"`
Language string `gorm:"type:varchar(10);default:'zh-CN'" json:"language"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
// 关联关系
User *User `gorm:"foreignKey:UserID;references:ID" json:"user,omitempty"`
}
func (UserProfile) TableName() string {
return "user_profiles"
}