user.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package service
  2. import (
  3. "time"
  4. "golang.org/x/crypto/bcrypt"
  5. )
  6. type User struct {
  7. ID int64
  8. Email string
  9. Username string
  10. Notes string
  11. AvatarURL string
  12. AvatarSource string
  13. AvatarMIME string
  14. AvatarByteSize int
  15. AvatarSHA256 string
  16. PasswordHash string
  17. Role string
  18. Balance float64
  19. FrozenBalance float64
  20. Concurrency int
  21. Status string
  22. AllowedGroups []int64
  23. // RestrictPublicGroups narrows the public groups this user may bind to the
  24. // ones listed in AllowedGroups. False keeps the default, where every public
  25. // group is bindable.
  26. RestrictPublicGroups bool
  27. TokenVersion int64 // Incremented on password change to invalidate existing tokens
  28. // TokenVersionResolved indicates TokenVersion already contains the fingerprint-derived
  29. // value expected in JWT claims and refresh-token state.
  30. TokenVersionResolved bool
  31. SignupSource string
  32. LastLoginAt *time.Time
  33. LastActiveAt *time.Time
  34. LastUsedAt *time.Time
  35. CreatedAt time.Time
  36. UpdatedAt time.Time
  37. DeletedAt *time.Time // 非 nil 表示用户已软删除
  38. // GroupRates 用户专属分组倍率配置
  39. // map[groupID]rateMultiplier
  40. GroupRates map[int64]float64
  41. // TOTP 双因素认证字段
  42. TotpSecretEncrypted *string // AES-256-GCM 加密的 TOTP 密钥
  43. TotpEnabled bool // 是否启用 TOTP
  44. TotpEnabledAt *time.Time // TOTP 启用时间
  45. // 余额不足通知
  46. BalanceNotifyEnabled bool
  47. BalanceNotifyThresholdType string // "fixed" (default) | "percentage"
  48. BalanceNotifyThreshold *float64
  49. BalanceNotifyExtraEmails []NotifyEmailEntry
  50. TotalRecharged float64
  51. // RPMLimit 用户级每分钟请求数上限(0 = 不限制)。仅在所用分组未设置 rpm_limit
  52. // 且该 (用户, 分组) 无 rpm_override 时作为全局兜底生效,计数键 rpm:u:{userID}:{min}。
  53. RPMLimit int
  54. // UserGroupRPMOverride 来自 auth cache snapshot 的 (user, group) RPM 覆盖值。
  55. // nil = 该 API Key 对应的 (user, group) 无 override;非 nil 时 checkRPM 直接使用,
  56. // 避免每请求查 DB。字段不持久化到数据库。
  57. UserGroupRPMOverride *int
  58. APIKeys []APIKey
  59. Subscriptions []UserSubscription
  60. }
  61. func (u *User) IsAdmin() bool {
  62. return u != nil && IsAdminRole(u.Role)
  63. }
  64. func IsAdminRole(role string) bool { return role == RoleAdmin || role == RoleSuperAdmin }
  65. func (u *User) IsSuperAdmin() bool { return u != nil && u.Role == RoleSuperAdmin }
  66. func (u *User) IsActive() bool {
  67. return u.Status == StatusActive
  68. }
  69. // CanBindGroup checks whether a user can bind to a given group.
  70. // For standard groups:
  71. // - Public groups (non-exclusive): bindable by every user, unless the user has
  72. // RestrictPublicGroups set, in which case the group must be in AllowedGroups
  73. // - Exclusive groups: only users with the group in AllowedGroups can bind
  74. func (u *User) CanBindGroup(groupID int64, isExclusive bool) bool {
  75. // 公开分组(非专属):默认所有用户都可以绑定;仅当该用户开启了公开分组
  76. // 限制时,才需要落在 AllowedGroups 中。
  77. if !isExclusive && !u.RestrictPublicGroups {
  78. return true
  79. }
  80. // 专属分组,以及受限用户的公开分组:需要在 AllowedGroups 中
  81. for _, id := range u.AllowedGroups {
  82. if id == groupID {
  83. return true
  84. }
  85. }
  86. return false
  87. }
  88. func (u *User) SetPassword(password string) error {
  89. hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
  90. if err != nil {
  91. return err
  92. }
  93. u.PasswordHash = string(hash)
  94. return nil
  95. }
  96. func (u *User) CheckPassword(password string) bool {
  97. return bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password)) == nil
  98. }