package models import ( "golang.org/x/crypto/bcrypt" "gorm.io/gorm" "time" ) type UserRole string const ( UserRoleSuperAdmin UserRole = "super_admin" UserRoleRegionAdmin UserRole = "region_admin" UserRoleOperator UserRole = "operator" UserRoleViewer UserRole = "viewer" ) type UserFlavorType string const ( UserFlavorAll UserFlavorType = "all" UserFlavorFlink UserFlavorType = "flink" UserFlavorFull UserFlavorType = "full" UserFlavorLight UserFlavorType = "light" UserFlavorHeartRate UserFlavorType = "heartrate" UserFlavorRun50 UserFlavorType = "run50" ) type UserRegionBinding struct { ID uint `gorm:"primaryKey" json:"id"` UserID uint `gorm:"not null;index;uniqueIndex:idx_user_region" json:"userId"` RegionID uint32 `gorm:"not null;index;uniqueIndex:idx_user_region" json:"regionId"` CreatedAt int64 `gorm:"not null" json:"created_at"` UpdatedAt int64 `gorm:"not null" json:"updated_at"` } type User struct { ID uint `gorm:"primaryKey" json:"id"` Username string `gorm:"uniqueIndex;not null" json:"username"` Email *string `gorm:"uniqueIndex;" json:"email"` Phone *string `gorm:"uniqueIndex;" json:"phone"` Password string `gorm:"not null" json:"-"` Role UserRole `gorm:"type:varchar(32);not null;default:'viewer';index" json:"role"` FlavorType UserFlavorType `gorm:"type:varchar(32);not null;default:'all';index" json:"flavorType"` IsActive bool `gorm:"not null;default:true;index" json:"isActive"` TokenInvalidBefore int64 `gorm:"not null;default:0" json:"tokenInvalidBefore"` Regions []UserRegionBinding `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE" json:"regions"` CreatedAt int64 `gorm:"not null" json:"created_at"` UpdatedAt int64 `gorm:"not null" json:"updated_at"` } // HashPassword 密码加密 func (u *User) HashPassword(password string) error { bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14) if err != nil { return err } u.Password = string(bytes) return nil } // CheckPassword 验证密码 func (u *User) CheckPassword(password string) bool { err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) return err == nil } func (u *User) RegionIDs() []uint32 { regionIDs := make([]uint32, 0, len(u.Regions)) for _, region := range u.Regions { regionIDs = append(regionIDs, region.RegionID) } return regionIDs } func (u *User) HasRegionAccess(regionID uint32) bool { if u.Role == UserRoleSuperAdmin { return true } for _, region := range u.Regions { if region.RegionID == regionID { return true } } return false } func (u *User) SupportsFlavor(flavor string) bool { if u.FlavorType == UserFlavorAll { return true } if u.FlavorType == UserFlavorFlink { return flavor == string(UserFlavorFlink) || flavor == string(UserFlavorFull) || flavor == string(UserFlavorLight) } return string(u.FlavorType) == flavor } func (u *User) normalizeDefaults() { if u.Role == "" { u.Role = UserRoleViewer } if u.FlavorType == "" { u.FlavorType = UserFlavorAll } } // BeforeCreate 创建前钩子 func (u *User) BeforeCreate(tx *gorm.DB) (err error) { u.normalizeDefaults() now := time.Now().UnixMilli() u.CreatedAt = now u.UpdatedAt = now u.IsActive = true if u.Password != "" { return u.HashPassword(u.Password) } return nil } func (u *User) BeforeUpdate(tx *gorm.DB) (err error) { u.normalizeDefaults() u.UpdatedAt = time.Now().UnixMilli() return nil } func (r *UserRegionBinding) BeforeCreate(tx *gorm.DB) (err error) { now := time.Now().UnixMilli() r.CreatedAt = now r.UpdatedAt = now return nil } func (r *UserRegionBinding) BeforeUpdate(tx *gorm.DB) (err error) { r.UpdatedAt = time.Now().UnixMilli() return nil }