Files
inchstep/server/internal/services/user_service.go
T
zeronline 545329ac40
CI / Server (Go) (push) Has been cancelled
CI / Web (Next.js) (push) Has been cancelled
CI / Mobile (React Native) (push) Has been cancelled
CI / Admin (React) (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
修改页面
2026-06-25 16:33:32 +08:00

136 lines
3.4 KiB
Go

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
}