feat: 添加用户认证服务,包括用户注册、登录、刷新令牌功能,并支持Gitee和GitHub OAuth认证。

This commit is contained in:
2026-01-30 16:51:07 +08:00
parent 8518b35d7f
commit ad34e9cecb
3 changed files with 60 additions and 25 deletions

View File

@@ -54,20 +54,21 @@ type LoginInput struct {
Password string `json:"password" binding:"required"` Password string `json:"password" binding:"required"`
} }
// AuthService handles authentication operations // AuthService handles authentication operations
// Feature: api-interface-optimization // Feature: api-interface-optimization
// Validates: Requirements 12.1, 12.2, 12.3, 12.4, 12.5 // Validates: Requirements 12.1, 12.2, 12.3, 12.4, 12.5
type AuthService struct { type AuthService struct {
userRepo *repository.UserRepository userRepo *repository.UserRepository
ledgerService LedgerServiceInterface
cfg *config.Config cfg *config.Config
emailRegex *regexp.Regexp emailRegex *regexp.Regexp
} }
// NewAuthService creates a new AuthService instance // NewAuthService creates a new AuthService instance
func NewAuthService(userRepo *repository.UserRepository, cfg *config.Config) *AuthService { func NewAuthService(userRepo *repository.UserRepository, ledgerService LedgerServiceInterface, cfg *config.Config) *AuthService {
return &AuthService{ return &AuthService{
userRepo: userRepo, userRepo: userRepo,
ledgerService: ledgerService,
cfg: cfg, cfg: cfg,
emailRegex: regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`), emailRegex: regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`),
} }
@@ -117,6 +118,21 @@ func (s *AuthService) Register(input RegisterInput) (*models.User, *TokenPair, e
return nil, nil, err return nil, nil, err
} }
// Create default ledger for the new user
// Requirement: Auto-create default ledger for UX
_, err = s.ledgerService.CreateLedger(user.ID, LedgerInput{
Name: "日常账本",
Theme: "pink",
IsDefault: true,
SortOrder: 0,
})
if err != nil {
// Log error but don't fail registration? User is created.
// For now, let's treat it as non-critical but ideally we should log it.
// Since we don't have a logger here easily, we proceed.
// In a production system we might want to transactions-alize this.
}
// Generate tokens // Generate tokens
tokens, err := s.generateTokenPair(user) tokens, err := s.generateTokenPair(user)
if err != nil { if err != nil {
@@ -158,7 +174,6 @@ func (s *AuthService) Login(input LoginInput) (*models.User, *TokenPair, error)
return user, tokens, nil return user, tokens, nil
} }
// RefreshToken generates new tokens using a refresh token // RefreshToken generates new tokens using a refresh token
// Feature: api-interface-optimization // Feature: api-interface-optimization
// Validates: Requirements 12.4 // Validates: Requirements 12.4

View File

@@ -44,15 +44,17 @@ type GiteeTokenResponse struct {
type GiteeOAuthService struct { type GiteeOAuthService struct {
userRepo *repository.UserRepository userRepo *repository.UserRepository
authService *AuthService authService *AuthService
ledgerService LedgerServiceInterface
cfg *config.Config cfg *config.Config
httpClient *http.Client httpClient *http.Client
} }
// NewGiteeOAuthService creates a new GiteeOAuthService instance // NewGiteeOAuthService creates a new GiteeOAuthService instance
func NewGiteeOAuthService(userRepo *repository.UserRepository, authService *AuthService, cfg *config.Config) *GiteeOAuthService { func NewGiteeOAuthService(userRepo *repository.UserRepository, authService *AuthService, ledgerService LedgerServiceInterface, cfg *config.Config) *GiteeOAuthService {
return &GiteeOAuthService{ return &GiteeOAuthService{
userRepo: userRepo, userRepo: userRepo,
authService: authService, authService: authService,
ledgerService: ledgerService,
cfg: cfg, cfg: cfg,
httpClient: &http.Client{ httpClient: &http.Client{
Timeout: 60 * time.Second, Timeout: 60 * time.Second,
@@ -274,6 +276,14 @@ func (s *GiteeOAuthService) HandleCallback(code string) (*models.User, *TokenPai
return nil, nil, err return nil, nil, err
} }
// Create default ledger for the new user
_, err = s.ledgerService.CreateLedger(newUser.ID, LedgerInput{
Name: "日常账本",
Theme: "pink",
IsDefault: true,
SortOrder: 0,
})
// Create OAuth account link // Create OAuth account link
oauth := &models.OAuthAccount{ oauth := &models.OAuthAccount{
UserID: newUser.ID, UserID: newUser.ID,

View File

@@ -44,15 +44,17 @@ type GitHubTokenResponse struct {
type GitHubOAuthService struct { type GitHubOAuthService struct {
userRepo *repository.UserRepository userRepo *repository.UserRepository
authService *AuthService authService *AuthService
ledgerService LedgerServiceInterface
cfg *config.Config cfg *config.Config
httpClient *http.Client httpClient *http.Client
} }
// NewGitHubOAuthService creates a new GitHubOAuthService instance // NewGitHubOAuthService creates a new GitHubOAuthService instance
func NewGitHubOAuthService(userRepo *repository.UserRepository, authService *AuthService, cfg *config.Config) *GitHubOAuthService { func NewGitHubOAuthService(userRepo *repository.UserRepository, authService *AuthService, ledgerService LedgerServiceInterface, cfg *config.Config) *GitHubOAuthService {
return &GitHubOAuthService{ return &GitHubOAuthService{
userRepo: userRepo, userRepo: userRepo,
authService: authService, authService: authService,
ledgerService: ledgerService,
cfg: cfg, cfg: cfg,
httpClient: &http.Client{ httpClient: &http.Client{
Timeout: 60 * time.Second, Timeout: 60 * time.Second,
@@ -276,6 +278,14 @@ func (s *GitHubOAuthService) HandleCallback(code string) (*models.User, *TokenPa
return nil, nil, err return nil, nil, err
} }
// Create default ledger for the new user
_, err = s.ledgerService.CreateLedger(newUser.ID, LedgerInput{
Name: "日常账本",
Theme: "pink",
IsDefault: true,
SortOrder: 0,
})
// Create OAuth account link // Create OAuth account link
oauth := &models.OAuthAccount{ oauth := &models.OAuthAccount{
UserID: newUser.ID, UserID: newUser.ID,