Files
2026-01-25 21:59:00 +08:00

507 lines
21 KiB
Go

package router
import (
"net/http"
"accounting-app/internal/cache"
"accounting-app/internal/config"
"accounting-app/internal/handler"
"accounting-app/internal/middleware"
"accounting-app/internal/repository"
"accounting-app/internal/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// Setup creates and configures the Gin router
func Setup(db *gorm.DB, yunAPIClient *service.YunAPIClient, cfg *config.Config) *gin.Engine {
r := gin.Default()
// Add CORS middleware
r.Use(corsMiddleware())
// Health check endpoint
r.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "ok",
"message": "Accounting App API is running",
})
})
// Initialize repositories
accountRepo := repository.NewAccountRepository(db)
categoryRepo := repository.NewCategoryRepository(db)
tagRepo := repository.NewTagRepository(db)
classificationRepo := repository.NewClassificationRepository(db)
transactionRepo := repository.NewTransactionRepository(db)
transactionImageRepo := repository.NewTransactionImageRepository(db)
recurringRepo := repository.NewRecurringTransactionRepository(db)
exchangeRateRepo := repository.NewExchangeRateRepository(db)
reportRepo := repository.NewReportRepository(db)
budgetRepo := repository.NewBudgetRepository(db)
piggyBankRepo := repository.NewPiggyBankRepository(db)
allocationRuleRepo := repository.NewAllocationRuleRepository(db)
allocationRecordRepo := repository.NewAllocationRecordRepository(db)
billingRepo := repository.NewBillingRepository(db)
repaymentRepo := repository.NewRepaymentRepository(db)
appLockRepo := repository.NewAppLockRepository(db)
ledgerRepo := repository.NewLedgerRepository(db)
userSettingsRepo := repository.NewUserSettingsRepository(db)
userRepo := repository.NewUserRepository(db)
// Initialize auth services
authService := service.NewAuthService(userRepo, cfg)
var gitHubOAuthService *service.GitHubOAuthService
if cfg.GitHubClientID != "" && cfg.GitHubClientSecret != "" {
gitHubOAuthService = service.NewGitHubOAuthService(userRepo, authService, cfg)
}
authHandler := handler.NewAuthHandlerWithConfig(authService, gitHubOAuthService, cfg)
authMiddleware := middleware.NewAuthMiddleware(authService)
// Initialize services
accountService := service.NewAccountService(accountRepo, db)
categoryService := service.NewCategoryService(categoryRepo)
tagService := service.NewTagService(tagRepo)
classificationService := service.NewClassificationService(classificationRepo, categoryRepo)
transactionService := service.NewTransactionService(transactionRepo, accountRepo, categoryRepo, tagRepo, db)
imageService := service.NewImageService(transactionImageRepo, transactionRepo, db, cfg.ImageUploadDir)
recurringService := service.NewRecurringTransactionService(recurringRepo, transactionRepo, accountRepo, categoryRepo, allocationRuleRepo, allocationRecordRepo, piggyBankRepo, db)
exchangeRateService := service.NewExchangeRateService(exchangeRateRepo)
reportService := service.NewReportService(reportRepo, exchangeRateRepo)
pdfExportService := service.NewPDFExportService(reportRepo, transactionRepo, exchangeRateRepo)
excelExportService := service.NewExcelExportService(reportRepo, transactionRepo, exchangeRateRepo)
budgetService := service.NewBudgetService(budgetRepo, db)
piggyBankService := service.NewPiggyBankService(piggyBankRepo, accountRepo, db)
allocationRuleService := service.NewAllocationRuleService(allocationRuleRepo, allocationRecordRepo, accountRepo, piggyBankRepo, db)
allocationRecordService := service.NewAllocationRecordService(allocationRecordRepo)
billingService := service.NewBillingService(billingRepo, accountRepo, transactionRepo, db)
repaymentService := service.NewRepaymentService(repaymentRepo, billingRepo, accountRepo, db)
backupService := service.NewBackupService(db)
appLockService := service.NewAppLockService(appLockRepo)
ledgerService := service.NewLedgerService(ledgerRepo, db)
reimbursementService := service.NewReimbursementService(db, transactionRepo, accountRepo)
refundService := service.NewRefundService(db, transactionRepo, accountRepo)
userSettingsService := service.NewUserSettingsService(userSettingsRepo)
// Feature: financial-core-upgrade - Initialize new services
subAccountService := service.NewSubAccountService(accountRepo, db)
savingsPotService := service.NewSavingsPotService(accountRepo, transactionRepo, db)
interestService := service.NewInterestService(accountRepo, transactionRepo, db)
userSettingsServiceWithAccounts := service.NewUserSettingsServiceWithAccountRepo(userSettingsRepo, accountRepo)
// Initialize handlers
accountHandler := handler.NewAccountHandler(accountService)
categoryHandler := handler.NewCategoryHandler(categoryService)
tagHandler := handler.NewTagHandler(tagService)
classificationHandler := handler.NewClassificationHandler(classificationService)
transactionHandler := handler.NewTransactionHandler(transactionService)
imageHandler := handler.NewImageHandler(imageService)
recurringHandler := handler.NewRecurringTransactionHandler(recurringService)
exchangeRateHandler := handler.NewExchangeRateHandlerWithClient(exchangeRateService, yunAPIClient)
reportHandler := handler.NewReportHandler(reportService, pdfExportService, excelExportService)
budgetHandler := handler.NewBudgetHandler(budgetService)
piggyBankHandler := handler.NewPiggyBankHandler(piggyBankService)
allocationRuleHandler := handler.NewAllocationRuleHandler(allocationRuleService)
allocationRecordHandler := handler.NewAllocationRecordHandler(allocationRecordService)
creditAccountHandler := handler.NewCreditAccountHandler(billingService, repaymentService)
repaymentHandler := handler.NewRepaymentHandler(repaymentService)
backupHandler := handler.NewBackupHandler(backupService)
appLockHandler := handler.NewAppLockHandler(appLockService)
ledgerHandler := handler.NewLedgerHandler(ledgerService)
reimbursementHandler := handler.NewReimbursementHandler(reimbursementService)
refundHandler := handler.NewRefundHandler(refundService)
settingsHandler := handler.NewSettingsHandler(userSettingsService)
// Feature: financial-core-upgrade - Initialize new handlers
subAccountHandler := handler.NewSubAccountHandler(subAccountService)
savingsPotHandler := handler.NewSavingsPotHandler(savingsPotService)
defaultAccountHandler := handler.NewDefaultAccountHandler(userSettingsServiceWithAccounts)
interestHandler := handler.NewInterestHandler(interestService, nil)
// AI Bookkeeping Service and Handler
aiBookkeepingService := service.NewAIBookkeepingService(
cfg,
transactionRepo,
accountRepo,
categoryRepo,
userSettingsRepo,
db,
)
aiHandler := handler.NewAIHandler(aiBookkeepingService)
// API v1 routes
v1 := r.Group("/api/v1")
{
// Placeholder routes - will be implemented in subsequent tasks
v1.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
// Register auth routes (public)
authHandler.RegisterRoutes(v1)
// Protected routes group - all routes requiring authentication
protected := v1.Group("")
protected.Use(authMiddleware.RequireAuth())
{
// Register auth protected routes
authHandler.RegisterProtectedRoutes(protected)
// Register account routes
accountHandler.RegisterRoutes(protected)
// Register category routes
categoryHandler.RegisterRoutes(protected)
// Register tag routes
tagHandler.RegisterRoutes(protected)
// Register classification routes (smart classification suggestion)
classificationHandler.RegisterRoutes(protected)
// Register transaction routes
transactionHandler.RegisterRoutes(protected)
// Register image routes
imageHandler.RegisterRoutes(protected)
// Register recurring transaction routes
recurringHandler.RegisterRoutes(protected)
// Register exchange rate routes
exchangeRateHandler.RegisterRoutes(protected)
// Register budget routes
budgetHandler.RegisterRoutes(protected)
// Register piggy bank routes
piggyBankHandler.RegisterRoutes(protected)
// Register allocation rule routes
allocationRuleHandler.RegisterRoutes(protected)
// Register allocation record routes
allocationRecordHandler.RegisterRoutes(protected)
// Register credit account routes (bills and repayment)
creditAccountHandler.RegisterRoutes(protected)
// Register repayment plan routes
repaymentHandler.RegisterRoutes(protected)
// Register ledger routes
ledgerHandler.RegisterRoutes(protected)
// Register reimbursement routes
reimbursementHandler.RegisterRoutes(protected)
// Register refund routes
refundHandler.RegisterRoutes(protected)
// Register settings routes
settingsHandler.RegisterRoutes(protected)
// Feature: financial-core-upgrade - Register new routes
// Sub-account routes
subAccountHandler.RegisterRoutes(protected)
// Savings pot routes
savingsPotHandler.RegisterRoutes(protected)
// Default account routes
defaultAccountHandler.RegisterRoutes(protected)
// Interest routes
interestHandler.RegisterRoutes(protected)
// AI bookkeeping routes
aiHandler.RegisterRoutes(protected)
// Register report routes
protected.GET("/reports/summary", reportHandler.GetTransactionSummary)
protected.GET("/reports/category", reportHandler.GetCategorySummary)
protected.GET("/reports/trend", reportHandler.GetTrendData)
protected.GET("/reports/comparison", reportHandler.GetComparisonData)
protected.GET("/reports/assets", reportHandler.GetAssetsSummary)
protected.GET("/reports/consumption-habits", reportHandler.GetConsumptionHabits)
protected.GET("/reports/asset-liability-analysis", reportHandler.GetAssetLiabilityAnalysis)
protected.POST("/reports/export", reportHandler.ExportReport)
// Register backup routes
protected.POST("/backup/export", backupHandler.ExportBackup)
protected.POST("/backup/import", backupHandler.ImportBackup)
protected.POST("/backup/verify", backupHandler.VerifyBackup)
// Register app lock routes
protected.GET("/app-lock/status", appLockHandler.GetStatus)
protected.POST("/app-lock/password", appLockHandler.SetPassword)
protected.POST("/app-lock/verify", appLockHandler.VerifyPassword)
protected.POST("/app-lock/disable", appLockHandler.DisableLock)
protected.POST("/app-lock/password/change", appLockHandler.ChangePassword)
}
}
return r
}
// corsMiddleware handles Cross-Origin Resource Sharing
func corsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization")
c.Header("Access-Control-Max-Age", "86400")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
return
}
c.Next()
}
}
// SetupWithRedis creates and configures the Gin router with Redis support for exchange rates
// This function uses ExchangeRateHandlerV2 with Redis caching and SyncScheduler
// Requirements: 2.1, 2.2, 2.3, 2.5
func SetupWithRedis(db *gorm.DB, yunAPIClient *service.YunAPIClient, redisClient *cache.RedisClient, cfg *config.Config) (*gin.Engine, *service.SyncScheduler) {
r := gin.Default()
// Add CORS middleware
r.Use(corsMiddleware())
// Health check endpoint
r.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "ok",
"message": "Accounting App API is running",
})
})
// Initialize repositories
accountRepo := repository.NewAccountRepository(db)
categoryRepo := repository.NewCategoryRepository(db)
tagRepo := repository.NewTagRepository(db)
classificationRepo := repository.NewClassificationRepository(db)
transactionRepo := repository.NewTransactionRepository(db)
transactionImageRepo := repository.NewTransactionImageRepository(db)
recurringRepo := repository.NewRecurringTransactionRepository(db)
exchangeRateRepo := repository.NewExchangeRateRepository(db)
reportRepo := repository.NewReportRepository(db)
budgetRepo := repository.NewBudgetRepository(db)
piggyBankRepo := repository.NewPiggyBankRepository(db)
allocationRuleRepo := repository.NewAllocationRuleRepository(db)
allocationRecordRepo := repository.NewAllocationRecordRepository(db)
billingRepo := repository.NewBillingRepository(db)
repaymentRepo := repository.NewRepaymentRepository(db)
appLockRepo := repository.NewAppLockRepository(db)
ledgerRepo := repository.NewLedgerRepository(db)
userSettingsRepo := repository.NewUserSettingsRepository(db)
userRepo := repository.NewUserRepository(db)
// Initialize auth services
authService := service.NewAuthService(userRepo, cfg)
var gitHubOAuthService *service.GitHubOAuthService
if cfg.GitHubClientID != "" && cfg.GitHubClientSecret != "" {
gitHubOAuthService = service.NewGitHubOAuthService(userRepo, authService, cfg)
}
authHandler := handler.NewAuthHandlerWithConfig(authService, gitHubOAuthService, cfg)
authMiddleware := middleware.NewAuthMiddleware(authService)
// Initialize services
accountService := service.NewAccountService(accountRepo, db)
categoryService := service.NewCategoryService(categoryRepo)
tagService := service.NewTagService(tagRepo)
classificationService := service.NewClassificationService(classificationRepo, categoryRepo)
transactionService := service.NewTransactionService(transactionRepo, accountRepo, categoryRepo, tagRepo, db)
imageService := service.NewImageService(transactionImageRepo, transactionRepo, db, cfg.ImageUploadDir)
recurringService := service.NewRecurringTransactionService(recurringRepo, transactionRepo, accountRepo, categoryRepo, allocationRuleRepo, allocationRecordRepo, piggyBankRepo, db)
reportService := service.NewReportService(reportRepo, exchangeRateRepo)
pdfExportService := service.NewPDFExportService(reportRepo, transactionRepo, exchangeRateRepo)
excelExportService := service.NewExcelExportService(reportRepo, transactionRepo, exchangeRateRepo)
budgetService := service.NewBudgetService(budgetRepo, db)
piggyBankService := service.NewPiggyBankService(piggyBankRepo, accountRepo, db)
allocationRuleService := service.NewAllocationRuleService(allocationRuleRepo, allocationRecordRepo, accountRepo, piggyBankRepo, db)
allocationRecordService := service.NewAllocationRecordService(allocationRecordRepo)
billingService := service.NewBillingService(billingRepo, accountRepo, transactionRepo, db)
repaymentService := service.NewRepaymentService(repaymentRepo, billingRepo, accountRepo, db)
backupService := service.NewBackupService(db)
appLockService := service.NewAppLockService(appLockRepo)
ledgerService := service.NewLedgerService(ledgerRepo, db)
reimbursementService := service.NewReimbursementService(db, transactionRepo, accountRepo)
refundService := service.NewRefundService(db, transactionRepo, accountRepo)
userSettingsService := service.NewUserSettingsService(userSettingsRepo)
// Feature: financial-core-upgrade - Initialize new services
subAccountService := service.NewSubAccountService(accountRepo, db)
savingsPotService := service.NewSavingsPotService(accountRepo, transactionRepo, db)
interestService := service.NewInterestService(accountRepo, transactionRepo, db)
userSettingsServiceWithAccounts := service.NewUserSettingsServiceWithAccountRepo(userSettingsRepo, accountRepo)
// Initialize ExchangeRateServiceV2 with Redis cache
exchangeRateCache := cache.NewExchangeRateCache(redisClient, cfg)
exchangeRateServiceV2 := service.NewExchangeRateServiceV2(exchangeRateCache, yunAPIClient)
// Initialize SyncScheduler with configured interval
syncScheduler := service.NewSyncScheduler(exchangeRateServiceV2, cfg.SyncInterval)
// Initialize handlers
accountHandler := handler.NewAccountHandler(accountService)
categoryHandler := handler.NewCategoryHandler(categoryService)
tagHandler := handler.NewTagHandler(tagService)
classificationHandler := handler.NewClassificationHandler(classificationService)
transactionHandler := handler.NewTransactionHandler(transactionService)
imageHandler := handler.NewImageHandler(imageService)
recurringHandler := handler.NewRecurringTransactionHandler(recurringService)
reportHandler := handler.NewReportHandler(reportService, pdfExportService, excelExportService)
budgetHandler := handler.NewBudgetHandler(budgetService)
piggyBankHandler := handler.NewPiggyBankHandler(piggyBankService)
allocationRuleHandler := handler.NewAllocationRuleHandler(allocationRuleService)
allocationRecordHandler := handler.NewAllocationRecordHandler(allocationRecordService)
creditAccountHandler := handler.NewCreditAccountHandler(billingService, repaymentService)
repaymentHandler := handler.NewRepaymentHandler(repaymentService)
backupHandler := handler.NewBackupHandler(backupService)
appLockHandler := handler.NewAppLockHandler(appLockService)
ledgerHandler := handler.NewLedgerHandler(ledgerService)
reimbursementHandler := handler.NewReimbursementHandler(reimbursementService)
refundHandler := handler.NewRefundHandler(refundService)
settingsHandler := handler.NewSettingsHandler(userSettingsService)
// Feature: financial-core-upgrade - Initialize new handlers
subAccountHandler := handler.NewSubAccountHandler(subAccountService)
savingsPotHandler := handler.NewSavingsPotHandler(savingsPotService)
defaultAccountHandler := handler.NewDefaultAccountHandler(userSettingsServiceWithAccounts)
interestHandler := handler.NewInterestHandler(interestService, nil)
// AI Bookkeeping Service and Handler for Redis setup
aiBookkeepingServiceRedis := service.NewAIBookkeepingService(
cfg,
transactionRepo,
accountRepo,
categoryRepo,
userSettingsRepo,
db,
)
aiHandlerRedis := handler.NewAIHandler(aiBookkeepingServiceRedis)
// Initialize ExchangeRateHandlerV2 with Redis-backed service and scheduler
exchangeRateHandlerV2 := handler.NewExchangeRateHandlerV2(exchangeRateServiceV2, syncScheduler)
// API v1 routes
v1 := r.Group("/api/v1")
{
// Placeholder routes - will be implemented in subsequent tasks
v1.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
// Register auth routes (public)
authHandler.RegisterRoutes(v1)
// Protected routes group
protected := v1.Group("")
protected.Use(authMiddleware.RequireAuth())
{
// Register auth protected routes
authHandler.RegisterProtectedRoutes(protected)
}
// Register account routes
accountHandler.RegisterRoutes(v1)
// Register category routes
categoryHandler.RegisterRoutes(v1)
// Register tag routes
tagHandler.RegisterRoutes(v1)
// Register classification routes (smart classification suggestion)
classificationHandler.RegisterRoutes(v1)
// Register transaction routes
transactionHandler.RegisterRoutes(v1)
// Register image routes
imageHandler.RegisterRoutes(v1)
// Register recurring transaction routes
recurringHandler.RegisterRoutes(v1)
// Register exchange rate routes (V2 with Redis caching)
// Routes:
// - GET /api/v1/exchange-rates - Get all rates with sync status
// - GET /api/v1/exchange-rates/:currency - Get single currency rate
// - POST /api/v1/exchange-rates/convert - Currency conversion
// - POST /api/v1/exchange-rates/refresh - Manual refresh
// - GET /api/v1/exchange-rates/sync-status - Get sync status
exchangeRateHandlerV2.RegisterRoutes(v1)
// Register budget routes
budgetHandler.RegisterRoutes(v1)
// Register piggy bank routes
piggyBankHandler.RegisterRoutes(v1)
// Register allocation rule routes
allocationRuleHandler.RegisterRoutes(v1)
// Register allocation record routes
allocationRecordHandler.RegisterRoutes(v1)
// Register credit account routes (bills and repayment)
creditAccountHandler.RegisterRoutes(v1)
// Register repayment plan routes
repaymentHandler.RegisterRoutes(v1)
// Register ledger routes
ledgerHandler.RegisterRoutes(v1)
// Register reimbursement routes
reimbursementHandler.RegisterRoutes(v1)
// Register refund routes
refundHandler.RegisterRoutes(v1)
// Register settings routes
settingsHandler.RegisterRoutes(v1)
// Feature: financial-core-upgrade - Register new routes
// Sub-account routes
subAccountHandler.RegisterRoutes(v1)
// Savings pot routes
savingsPotHandler.RegisterRoutes(v1)
// Default account routes
defaultAccountHandler.RegisterRoutes(v1)
// Interest routes
interestHandler.RegisterRoutes(v1)
// AI bookkeeping routes
aiHandlerRedis.RegisterRoutes(v1)
// Register report routes
v1.GET("/reports/summary", reportHandler.GetTransactionSummary)
v1.GET("/reports/category", reportHandler.GetCategorySummary)
v1.GET("/reports/trend", reportHandler.GetTrendData)
v1.GET("/reports/comparison", reportHandler.GetComparisonData)
v1.GET("/reports/assets", reportHandler.GetAssetsSummary)
v1.GET("/reports/consumption-habits", reportHandler.GetConsumptionHabits)
v1.GET("/reports/asset-liability-analysis", reportHandler.GetAssetLiabilityAnalysis)
v1.POST("/reports/export", reportHandler.ExportReport)
// Register backup routes
v1.POST("/backup/export", backupHandler.ExportBackup)
v1.POST("/backup/import", backupHandler.ImportBackup)
v1.POST("/backup/verify", backupHandler.VerifyBackup)
// Register app lock routes
v1.GET("/app-lock/status", appLockHandler.GetStatus)
v1.POST("/app-lock/password", appLockHandler.SetPassword)
v1.POST("/app-lock/verify", appLockHandler.VerifyPassword)
v1.POST("/app-lock/disable", appLockHandler.DisableLock)
v1.POST("/app-lock/password/change", appLockHandler.ChangePassword)
}
return r, syncScheduler
}