feat: 完成后端API路由、交易服务和数据库模式的初始搭建
This commit is contained in:
@@ -167,6 +167,13 @@ CREATE TABLE IF NOT EXISTS `transaction_tags` (
|
|||||||
CONSTRAINT `fk_transaction_tags_transaction` FOREIGN KEY (`transaction_id`) REFERENCES `transactions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
CONSTRAINT `fk_transaction_tags_transaction` FOREIGN KEY (`transaction_id`) REFERENCES `transactions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- Account Tags table (Many-to-Many)
|
||||||
|
CREATE TABLE IF NOT EXISTS `account_tags` (
|
||||||
|
`account_id` bigint(20) unsigned NOT NULL,
|
||||||
|
`tag_id` bigint(20) unsigned NOT NULL,
|
||||||
|
PRIMARY KEY (`account_id`,`tag_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- Transaction Images table
|
-- Transaction Images table
|
||||||
CREATE TABLE IF NOT EXISTS `transaction_images` (
|
CREATE TABLE IF NOT EXISTS `transaction_images` (
|
||||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
@@ -553,6 +560,7 @@ CREATE TABLE IF NOT EXISTS `user_settings` (
|
|||||||
CONSTRAINT `fk_user_settings_default_expense` FOREIGN KEY (`default_expense_account_id`) REFERENCES `accounts` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
|
CONSTRAINT `fk_user_settings_default_expense` FOREIGN KEY (`default_expense_account_id`) REFERENCES `accounts` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
CONSTRAINT `fk_user_settings_default_income` FOREIGN KEY (`default_income_account_id`) REFERENCES `accounts` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
|
CONSTRAINT `fk_user_settings_default_income` FOREIGN KEY (`default_income_account_id`) REFERENCES `accounts` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- Notifications table
|
-- Notifications table
|
||||||
CREATE TABLE IF NOT EXISTS `notifications` (
|
CREATE TABLE IF NOT EXISTS `notifications` (
|
||||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func Setup(db *gorm.DB, yunAPIClient *service.YunAPIClient, cfg *config.Config)
|
|||||||
categoryService := service.NewCategoryService(categoryRepo)
|
categoryService := service.NewCategoryService(categoryRepo)
|
||||||
tagService := service.NewTagService(tagRepo)
|
tagService := service.NewTagService(tagRepo)
|
||||||
classificationService := service.NewClassificationService(classificationRepo, categoryRepo)
|
classificationService := service.NewClassificationService(classificationRepo, categoryRepo)
|
||||||
transactionService := service.NewTransactionService(transactionRepo, accountRepo, categoryRepo, tagRepo, db)
|
transactionService := service.NewTransactionService(transactionRepo, accountRepo, categoryRepo, tagRepo, ledgerRepo, db)
|
||||||
imageService := service.NewImageService(transactionImageRepo, transactionRepo, db, cfg.ImageUploadDir)
|
imageService := service.NewImageService(transactionImageRepo, transactionRepo, db, cfg.ImageUploadDir)
|
||||||
recurringService := service.NewRecurringTransactionService(recurringRepo, transactionRepo, accountRepo, categoryRepo, allocationRuleRepo, allocationRecordRepo, piggyBankRepo, db)
|
recurringService := service.NewRecurringTransactionService(recurringRepo, transactionRepo, accountRepo, categoryRepo, allocationRuleRepo, allocationRecordRepo, piggyBankRepo, db)
|
||||||
exchangeRateService := service.NewExchangeRateService(exchangeRateRepo)
|
exchangeRateService := service.NewExchangeRateService(exchangeRateRepo)
|
||||||
@@ -347,7 +347,7 @@ func SetupWithRedis(db *gorm.DB, yunAPIClient *service.YunAPIClient, redisClient
|
|||||||
categoryService := service.NewCategoryService(categoryRepo)
|
categoryService := service.NewCategoryService(categoryRepo)
|
||||||
tagService := service.NewTagService(tagRepo)
|
tagService := service.NewTagService(tagRepo)
|
||||||
classificationService := service.NewClassificationService(classificationRepo, categoryRepo)
|
classificationService := service.NewClassificationService(classificationRepo, categoryRepo)
|
||||||
transactionService := service.NewTransactionService(transactionRepo, accountRepo, categoryRepo, tagRepo, db)
|
transactionService := service.NewTransactionService(transactionRepo, accountRepo, categoryRepo, tagRepo, ledgerRepo, db)
|
||||||
imageService := service.NewImageService(transactionImageRepo, transactionRepo, db, cfg.ImageUploadDir)
|
imageService := service.NewImageService(transactionImageRepo, transactionRepo, db, cfg.ImageUploadDir)
|
||||||
recurringService := service.NewRecurringTransactionService(recurringRepo, transactionRepo, accountRepo, categoryRepo, allocationRuleRepo, allocationRecordRepo, piggyBankRepo, db)
|
recurringService := service.NewRecurringTransactionService(recurringRepo, transactionRepo, accountRepo, categoryRepo, allocationRuleRepo, allocationRecordRepo, piggyBankRepo, db)
|
||||||
reportService := service.NewReportService(reportRepo, exchangeRateRepo)
|
reportService := service.NewReportService(reportRepo, exchangeRateRepo)
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ type TransactionInput struct {
|
|||||||
ImagePath string `json:"image_path,omitempty"`
|
ImagePath string `json:"image_path,omitempty"`
|
||||||
ToAccountID *uint `json:"to_account_id,omitempty"`
|
ToAccountID *uint `json:"to_account_id,omitempty"`
|
||||||
TagIDs []uint `json:"tag_ids,omitempty"`
|
TagIDs []uint `json:"tag_ids,omitempty"`
|
||||||
|
LedgerID *uint `json:"ledger_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionListInput represents the input for listing transactions
|
// TransactionListInput represents the input for listing transactions
|
||||||
@@ -65,6 +66,7 @@ type TransactionService struct {
|
|||||||
accountRepo *repository.AccountRepository
|
accountRepo *repository.AccountRepository
|
||||||
categoryRepo *repository.CategoryRepository
|
categoryRepo *repository.CategoryRepository
|
||||||
tagRepo *repository.TagRepository
|
tagRepo *repository.TagRepository
|
||||||
|
ledgerRepo *repository.LedgerRepository
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +76,7 @@ func NewTransactionService(
|
|||||||
accountRepo *repository.AccountRepository,
|
accountRepo *repository.AccountRepository,
|
||||||
categoryRepo *repository.CategoryRepository,
|
categoryRepo *repository.CategoryRepository,
|
||||||
tagRepo *repository.TagRepository,
|
tagRepo *repository.TagRepository,
|
||||||
|
ledgerRepo *repository.LedgerRepository,
|
||||||
db *gorm.DB,
|
db *gorm.DB,
|
||||||
) *TransactionService {
|
) *TransactionService {
|
||||||
return &TransactionService{
|
return &TransactionService{
|
||||||
@@ -81,6 +84,7 @@ func NewTransactionService(
|
|||||||
accountRepo: accountRepo,
|
accountRepo: accountRepo,
|
||||||
categoryRepo: categoryRepo,
|
categoryRepo: categoryRepo,
|
||||||
tagRepo: tagRepo,
|
tagRepo: tagRepo,
|
||||||
|
ledgerRepo: ledgerRepo,
|
||||||
db: db,
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,6 +164,23 @@ func (s *TransactionService) CreateTransaction(userID uint, input TransactionInp
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If LedgerID is not provided, use the default ledger
|
||||||
|
var ledgerID uint
|
||||||
|
if input.LedgerID == nil || *input.LedgerID == 0 {
|
||||||
|
defaultLedger, err := s.ledgerRepo.GetDefault(userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get default ledger: %w", err)
|
||||||
|
}
|
||||||
|
ledgerID = defaultLedger.ID
|
||||||
|
} else {
|
||||||
|
ledgerID = *input.LedgerID
|
||||||
|
// Verify ledger exists
|
||||||
|
_, err := s.ledgerRepo.GetByID(userID, ledgerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to verify ledger: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Execute within a database transaction
|
// Execute within a database transaction
|
||||||
var transaction *models.Transaction
|
var transaction *models.Transaction
|
||||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||||
@@ -216,6 +237,7 @@ func (s *TransactionService) CreateTransaction(userID uint, input TransactionInp
|
|||||||
Note: input.Note,
|
Note: input.Note,
|
||||||
ImagePath: input.ImagePath,
|
ImagePath: input.ImagePath,
|
||||||
ToAccountID: input.ToAccountID,
|
ToAccountID: input.ToAccountID,
|
||||||
|
LedgerID: &ledgerID,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save transaction with tags
|
// Save transaction with tags
|
||||||
@@ -414,6 +436,14 @@ func (s *TransactionService) UpdateTransaction(userID, id uint, input Transactio
|
|||||||
transaction.Note = input.Note
|
transaction.Note = input.Note
|
||||||
transaction.ImagePath = input.ImagePath
|
transaction.ImagePath = input.ImagePath
|
||||||
transaction.ToAccountID = input.ToAccountID
|
transaction.ToAccountID = input.ToAccountID
|
||||||
|
if input.LedgerID != nil && *input.LedgerID > 0 {
|
||||||
|
// Verify ledger exists
|
||||||
|
_, err := s.ledgerRepo.GetByID(userID, *input.LedgerID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to verify ledger: %w", err)
|
||||||
|
}
|
||||||
|
transaction.LedgerID = input.LedgerID
|
||||||
|
}
|
||||||
|
|
||||||
if err := txTransactionRepo.UpdateWithTags(transaction, input.TagIDs); err != nil {
|
if err := txTransactionRepo.UpdateWithTags(transaction, input.TagIDs); err != nil {
|
||||||
return fmt.Errorf("failed to update transaction: %w", err)
|
return fmt.Errorf("failed to update transaction: %w", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user