package service import ( "errors" "fmt" "time" "github.com/golang-jwt/jwt/v5" "github.com/gouki/lunar-server/internal/config" "github.com/gouki/lunar-server/internal/model" "gorm.io/gorm" ) // UserService 用户服务 type UserService struct { db *gorm.DB } // NewUserService 创建用户服务 func NewUserService() *UserService { return &UserService{ db: config.GetDB(), } } // GetUserByOpenID 根据OpenID获取用户 func (s *UserService) GetUserByOpenID(openID string) (*model.User, error) { var user model.User if err := s.db.Where("open_id = ?", openID).First(&user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return nil, err } return &user, nil } // GetUserByID 根据ID获取用户 func (s *UserService) GetUserByID(id uint) (*model.User, error) { var user model.User if err := s.db.First(&user, id).Error; err != nil { return nil, err } return &user, nil } // CreateUser 创建用户 func (s *UserService) CreateUser(user *model.User) error { return s.db.Create(user).Error } // UpdateUser 更新用户 func (s *UserService) UpdateUser(user *model.User) error { return s.db.Save(user).Error } // UpdateUserProfile 更新用户资料 func (s *UserService) UpdateUserProfile(profile *model.UserProfile) error { return s.db.Save(profile).Error } // GetUserProfile 获取用户资料 func (s *UserService) GetUserProfile(userID uint) (*model.UserProfile, error) { var profile model.UserProfile if err := s.db.Where("user_id = ?", userID).First(&profile).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return nil, err } return &profile, nil } // ErrBirthProfileLimit 生辰档案数量达到免费上限 var ErrBirthProfileLimit = fmt.Errorf("最多可免费绑定 %d 个生辰", model.MaxFreeBirthProfiles) // ListBirthProfiles 获取用户的全部生辰档案 func (s *UserService) ListBirthProfiles(userID uint) ([]model.BirthProfile, error) { var profiles []model.BirthProfile if err := s.db.Where("user_id = ?", userID).Order("id ASC").Find(&profiles).Error; err != nil { return nil, err } return profiles, nil } // CreateBirthProfile 新增生辰档案,超过免费上限时返回 ErrBirthProfileLimit func (s *UserService) CreateBirthProfile(profile *model.BirthProfile) error { var count int64 if err := s.db.Model(&model.BirthProfile{}).Where("user_id = ?", profile.UserID).Count(&count).Error; err != nil { return err } if count >= model.MaxFreeBirthProfiles { return ErrBirthProfileLimit } return s.db.Create(profile).Error } // DeleteBirthProfile 删除生辰档案(仅限本人) func (s *UserService) DeleteBirthProfile(id, userID uint) error { return s.db.Where("id = ? AND user_id = ?", id, userID).Delete(&model.BirthProfile{}).Error } // GenerateToken 生成JWT Token func (s *UserService) GenerateToken(userID uint, secret string) (string, error) { claims := jwt.MapClaims{ "user_id": userID, "exp": time.Now().Add(time.Hour * 24 * 7).Unix(), // 7天过期 "iat": time.Now().Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(secret)) } // ParseToken 解析JWT Token func (s *UserService) ParseToken(tokenString, secret string) (uint, error) { token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { return []byte(secret), nil }) if err != nil { return 0, err } if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { // 管理员 token 不绑定具体用户,不能当普通用户 token 使用 if role, _ := claims["role"].(string); role == "admin" { return 0, errors.New("admin token") } userIDFloat, ok := claims["user_id"].(float64) if !ok { return 0, errors.New("invalid token claims") } return uint(userIDFloat), nil } return 0, errors.New("invalid token") } // AdminTokenCookie 管理员会话 Cookie 名 const AdminTokenCookie = "lunar_admin_token" // GenerateAdminToken 签发管理员 JWT(12 小时过期) func (s *UserService) GenerateAdminToken(secret string) (string, error) { claims := jwt.MapClaims{ "role": "admin", "exp": time.Now().Add(time.Hour * 12).Unix(), "iat": time.Now().Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(secret)) } // IsAdminToken 校验是否为有效的管理员 JWT func (s *UserService) IsAdminToken(tokenString, secret string) bool { token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { return []byte(secret), nil }) if err != nil { return false } claims, ok := token.Claims.(jwt.MapClaims) if !ok || !token.Valid { return false } role, _ := claims["role"].(string) return role == "admin" }