Build and Publish Server / build (push) Successful in 4m30s
- 新增商业化模块:Stripe/Binance Pay 支付、邮箱登录、订单管理 - 新增数据模型:ReportProduct, ReportEntitlement, ReportSnapshot, PaymentEvent, EmailLoginCode - 新增 API:/api/commerce/checkout, /api/commerce/webhooks/*, /api/commerce/orders/* - 新增管理后台:订单列表、统计图表、统一 AdminHeader 组件 - 新增中间件测试:RateLimit, Auth, CORS 共 7 个测试用例 - 新增 AGENTS.md 项目代理指令文件 - 新增 REMINDER.md 商业化功能开发规范 - 修复 JWT 解析:添加 WithValidMethods 限制 HS256 - 修复 GetPayStatus:增加用户权限校验 - 更新 .gitignore 和 README.md
171 lines
4.8 KiB
Go
171 lines
4.8 KiB
Go
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
|
||
}, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))
|
||
|
||
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
|
||
}, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))
|
||
if err != nil {
|
||
return false
|
||
}
|
||
|
||
claims, ok := token.Claims.(jwt.MapClaims)
|
||
if !ok || !token.Valid {
|
||
return false
|
||
}
|
||
role, _ := claims["role"].(string)
|
||
return role == "admin"
|
||
}
|