package services import ( "crypto/rand" "encoding/hex" "errors" "time" "github.com/inchstep/server/internal/models" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) type UserService struct { db *gorm.DB } func NewUserService(db *gorm.DB) *UserService { return &UserService{db: db} } func generateID() string { b := make([]byte, 16) rand.Read(b) return hex.EncodeToString(b) } func (s *UserService) RegisterByPhone(phone string) (*models.User, error) { var existing models.User if err := s.db.Where("phone = ?", phone).First(&existing).Error; err == nil { return nil, errors.New("该手机号已注册") } user := &models.User{ ID: generateID(), Phone: &phone, Privacy: models.PrivacyFriendsOnly, Points: 0, CreatedAt: time.Now(), UpdatedAt: time.Now(), } if err := s.db.Create(user).Error; err != nil { return nil, err } return user, nil } func (s *UserService) GetByPhone(phone string) (*models.User, error) { var user models.User if err := s.db.Where("phone = ?", phone).First(&user).Error; err != nil { return nil, err } return &user, nil } func (s *UserService) GetByID(id string) (*models.User, error) { var user models.User if err := s.db.First(&user, "id = ?", id).Error; err != nil { return nil, err } return &user, nil } func (s *UserService) UpdateProfile(id string, updates map[string]interface{}) (*models.User, error) { allowed := map[string]bool{"nickname": true, "avatar": true, "bio": true, "motto": true, "privacy": true} filtered := make(map[string]interface{}) for k, v := range updates { if allowed[k] { filtered[k] = v } } if err := s.db.Model(&models.User{}).Where("id = ?", id).Updates(filtered).Error; err != nil { return nil, err } return s.GetByID(id) } func (s *UserService) DeleteAccount(id string) error { return s.db.Model(&models.User{}).Where("id = ?", id).Updates(map[string]interface{}{ "phone": nil, "nickname": nil, "avatar": nil, "bio": nil, "motto": nil, "privacy": models.PrivacyPrivate, }).Error } // LeaderboardEntry 排行榜条目 type LeaderboardEntry struct { UserID string `json:"userId"` Nickname string `json:"nickname"` Avatar string `json:"avatar,omitempty"` Value int `json:"value"` } // GetLeaderboardByPoints 按积分排行 func (s *UserService) GetLeaderboardByPoints(limit int) ([]LeaderboardEntry, error) { var users []models.User if err := s.db.Where("points > 0").Order("points desc").Limit(limit).Find(&users).Error; err != nil { return nil, err } entries := make([]LeaderboardEntry, 0, len(users)) for _, u := range users { nick := "" if u.Nickname != nil { nick = *u.Nickname } entries = append(entries, LeaderboardEntry{ UserID: u.ID, Nickname: nick, Value: u.Points, }) } return entries, nil } // SearchUsers 搜索用户(按昵称或手机号) func (s *UserService) SearchUsers(query string, limit int) ([]models.User, error) { var users []models.User q := "%" + query + "%" if err := s.db.Where("nickname LIKE ? OR phone LIKE ?", q, q). Limit(limit).Find(&users).Error; err != nil { return nil, err } return users, nil } func HashPassword(password string) (string, error) { bytes, err := bcrypt.GenerateFromPassword([]byte(password), 12) return string(bytes), err } func CheckPassword(hash, password string) bool { return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil }