feat: 初始化核心应用结构,新增循环交易、短信、分配规则和用户设置等服务,并更新相关依赖和配置。

This commit is contained in:
2026-02-02 01:41:51 +08:00
parent 49fcce531d
commit fa8a35a85b
13 changed files with 253 additions and 37 deletions

View File

@@ -11,6 +11,7 @@ import (
"gorm.io/gorm"
)
// RecurringTransactionService handles business logic for recurring transactions
// RecurringTransactionService handles business logic for recurring transactions
type RecurringTransactionService struct {
recurringRepo *repository.RecurringTransactionRepository
@@ -20,6 +21,8 @@ type RecurringTransactionService struct {
allocationRuleRepo *repository.AllocationRuleRepository
recordRepo *repository.AllocationRecordRepository
piggyBankRepo *repository.PiggyBankRepository
userSettingsRepo *repository.UserSettingsRepository
smsService *SmsService
db *gorm.DB
}
@@ -32,6 +35,8 @@ func NewRecurringTransactionService(
allocationRuleRepo *repository.AllocationRuleRepository,
recordRepo *repository.AllocationRecordRepository,
piggyBankRepo *repository.PiggyBankRepository,
userSettingsRepo *repository.UserSettingsRepository,
smsService *SmsService,
db *gorm.DB,
) *RecurringTransactionService {
return &RecurringTransactionService{
@@ -42,6 +47,8 @@ func NewRecurringTransactionService(
allocationRuleRepo: allocationRuleRepo,
recordRepo: recordRepo,
piggyBankRepo: piggyBankRepo,
userSettingsRepo: userSettingsRepo,
smsService: smsService,
db: db,
}
}
@@ -377,6 +384,27 @@ func (s *RecurringTransactionService) ProcessDueTransactions(userID uint, now ti
result.Transactions = append(result.Transactions, transaction)
}
// Send SMS notifications for auto-executed allocations
for _, allocation := range result.Allocations {
if allocation.AutoExecuted {
// Get user phone number
userSettings, err := s.userSettingsRepo.GetOrCreate(userID)
if err != nil {
// Log error but don't fail the request
fmt.Printf("Failed to get user settings for SMS: %v\n", err)
continue
}
if userSettings != nil && userSettings.Phone != "" && s.smsService != nil {
go func(phone string, amount float64, ruleName string) {
if err := s.smsService.SendAllocationNotification(phone, amount, ruleName); err != nil {
fmt.Printf("Failed to send allocation SMS: %v\n", err)
}
}(userSettings.Phone, allocation.TotalAmount, allocation.RuleName)
}
}
}
return result, nil
}
@@ -429,7 +457,7 @@ func (s *RecurringTransactionService) applyAllocationRulesForIncome(userID uint,
continue
}
// Get target name and apply allocation
// Get target name
targetName := ""
switch target.TargetType {
@@ -440,20 +468,23 @@ func (s *RecurringTransactionService) applyAllocationRulesForIncome(userID uint,
}
targetName = targetAccount.Name
// Add to target account
targetAccount.Balance += allocatedAmount
if err := tx.Save(&targetAccount).Error; err != nil {
return nil, fmt.Errorf("failed to update target account balance: %w", err)
}
// Execute transfer only if AutoExecute is true
if rule.AutoExecute {
// Add to target account
targetAccount.Balance += allocatedAmount
if err := tx.Save(&targetAccount).Error; err != nil {
return nil, fmt.Errorf("failed to update target account balance: %w", err)
}
// Deduct from source account
var sourceAccount models.Account
if err := tx.First(&sourceAccount, accountID).Error; err != nil {
return nil, fmt.Errorf("failed to get source account: %w", err)
}
sourceAccount.Balance -= allocatedAmount
if err := tx.Save(&sourceAccount).Error; err != nil {
return nil, fmt.Errorf("failed to update source account balance: %w", err)
// Deduct from source account
var sourceAccount models.Account
if err := tx.First(&sourceAccount, accountID).Error; err != nil {
return nil, fmt.Errorf("failed to get source account: %w", err)
}
sourceAccount.Balance -= allocatedAmount
if err := tx.Save(&sourceAccount).Error; err != nil {
return nil, fmt.Errorf("failed to update source account balance: %w", err)
}
}
case models.TargetTypePiggyBank:
@@ -463,20 +494,23 @@ func (s *RecurringTransactionService) applyAllocationRulesForIncome(userID uint,
}
targetName = piggyBank.Name
// Add to piggy bank
piggyBank.CurrentAmount += allocatedAmount
if err := tx.Save(&piggyBank).Error; err != nil {
return nil, fmt.Errorf("failed to update piggy bank balance: %w", err)
}
// Execute transfer only if AutoExecute is true
if rule.AutoExecute {
// Add to piggy bank
piggyBank.CurrentAmount += allocatedAmount
if err := tx.Save(&piggyBank).Error; err != nil {
return nil, fmt.Errorf("failed to update piggy bank balance: %w", err)
}
// Deduct from source account
var sourceAccount models.Account
if err := tx.First(&sourceAccount, accountID).Error; err != nil {
return nil, fmt.Errorf("failed to get source account: %w", err)
}
sourceAccount.Balance -= allocatedAmount
if err := tx.Save(&sourceAccount).Error; err != nil {
return nil, fmt.Errorf("failed to update source account balance: %w", err)
// Deduct from source account
var sourceAccount models.Account
if err := tx.First(&sourceAccount, accountID).Error; err != nil {
return nil, fmt.Errorf("failed to get source account: %w", err)
}
sourceAccount.Balance -= allocatedAmount
if err := tx.Save(&sourceAccount).Error; err != nil {
return nil, fmt.Errorf("failed to update source account balance: %w", err)
}
}
default: