269 lines
6.9 KiB
Go
269 lines
6.9 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
|
|
"accounting-app/internal/repository"
|
|
)
|
|
|
|
// User preference service errors
|
|
var (
|
|
ErrPreferenceNotFound = errors.New("user preference not found")
|
|
)
|
|
|
|
// UserPreferenceOutput represents the output format for user preferences
|
|
type UserPreferenceOutput struct {
|
|
LastAccountID *uint `json:"last_account_id,omitempty"`
|
|
LastCategoryID *uint `json:"last_category_id,omitempty"`
|
|
FrequentAccounts []uint `json:"frequent_accounts,omitempty"`
|
|
FrequentCategories []uint `json:"frequent_categories,omitempty"`
|
|
}
|
|
|
|
// UserPreferenceService handles business logic for user preferences
|
|
type UserPreferenceService struct {
|
|
prefRepo *repository.UserPreferenceRepository
|
|
accountRepo *repository.AccountRepository
|
|
categoryRepo *repository.CategoryRepository
|
|
}
|
|
|
|
// NewUserPreferenceService creates a new UserPreferenceService instance
|
|
func NewUserPreferenceService(
|
|
prefRepo *repository.UserPreferenceRepository,
|
|
accountRepo *repository.AccountRepository,
|
|
categoryRepo *repository.CategoryRepository,
|
|
) *UserPreferenceService {
|
|
return &UserPreferenceService{
|
|
prefRepo: prefRepo,
|
|
accountRepo: accountRepo,
|
|
categoryRepo: categoryRepo,
|
|
}
|
|
}
|
|
|
|
// GetPreferences retrieves user preferences
|
|
func (s *UserPreferenceService) GetPreferences(userID uint) (*UserPreferenceOutput, error) {
|
|
pref, err := s.prefRepo.GetOrCreate(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
output := &UserPreferenceOutput{
|
|
LastAccountID: pref.LastAccountID,
|
|
LastCategoryID: pref.LastCategoryID,
|
|
}
|
|
|
|
// Parse frequent accounts JSON
|
|
if pref.FrequentAccounts != "" {
|
|
var accounts []uint
|
|
if err := json.Unmarshal([]byte(pref.FrequentAccounts), &accounts); err == nil {
|
|
output.FrequentAccounts = accounts
|
|
}
|
|
}
|
|
|
|
// Parse frequent categories JSON
|
|
if pref.FrequentCategories != "" {
|
|
var categories []uint
|
|
if err := json.Unmarshal([]byte(pref.FrequentCategories), &categories); err == nil {
|
|
output.FrequentCategories = categories
|
|
}
|
|
}
|
|
|
|
return output, nil
|
|
}
|
|
|
|
// RecordAccountUsage records that an account was used and updates preferences
|
|
func (s *UserPreferenceService) RecordAccountUsage(userID uint, accountID uint) error {
|
|
// Verify account exists
|
|
exists, err := s.accountRepo.ExistsByID(userID, accountID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
return errors.New("account not found")
|
|
}
|
|
|
|
// Update last account
|
|
if err := s.prefRepo.UpdateLastAccount(userID, accountID); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update frequent accounts
|
|
return s.updateFrequentAccounts(userID, accountID)
|
|
}
|
|
|
|
// RecordCategoryUsage records that a category was used and updates preferences
|
|
func (s *UserPreferenceService) RecordCategoryUsage(userID uint, categoryID uint) error {
|
|
// Verify category exists
|
|
exists, err := s.categoryRepo.ExistsByID(userID, categoryID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
return errors.New("category not found")
|
|
}
|
|
|
|
// Update last category
|
|
if err := s.prefRepo.UpdateLastCategory(userID, categoryID); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update frequent categories
|
|
return s.updateFrequentCategories(userID, categoryID)
|
|
}
|
|
|
|
// RecordTransactionUsage records both account and category usage from a transaction
|
|
func (s *UserPreferenceService) RecordTransactionUsage(userID uint, accountID, categoryID uint) error {
|
|
if err := s.RecordAccountUsage(userID, accountID); err != nil {
|
|
return err
|
|
}
|
|
return s.RecordCategoryUsage(userID, categoryID)
|
|
}
|
|
|
|
// updateFrequentAccounts updates the frequent accounts list
|
|
func (s *UserPreferenceService) updateFrequentAccounts(userID uint, accountID uint) error {
|
|
pref, err := s.prefRepo.GetOrCreate(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var accounts []uint
|
|
if pref.FrequentAccounts != "" {
|
|
if err := json.Unmarshal([]byte(pref.FrequentAccounts), &accounts); err != nil {
|
|
accounts = []uint{}
|
|
}
|
|
}
|
|
|
|
// Move the used account to the front (most recent)
|
|
accounts = moveToFront(accounts, accountID)
|
|
|
|
// Keep only top 10 frequent accounts
|
|
if len(accounts) > 10 {
|
|
accounts = accounts[:10]
|
|
}
|
|
|
|
// Save back
|
|
jsonBytes, err := json.Marshal(accounts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.prefRepo.UpdateFrequentAccounts(userID, string(jsonBytes))
|
|
}
|
|
|
|
// updateFrequentCategories updates the frequent categories list
|
|
func (s *UserPreferenceService) updateFrequentCategories(userID uint, categoryID uint) error {
|
|
pref, err := s.prefRepo.GetOrCreate(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var categories []uint
|
|
if pref.FrequentCategories != "" {
|
|
if err := json.Unmarshal([]byte(pref.FrequentCategories), &categories); err != nil {
|
|
categories = []uint{}
|
|
}
|
|
}
|
|
|
|
// Move the used category to the front (most recent)
|
|
categories = moveToFront(categories, categoryID)
|
|
|
|
// Keep only top 10 frequent categories
|
|
if len(categories) > 10 {
|
|
categories = categories[:10]
|
|
}
|
|
|
|
// Save back
|
|
jsonBytes, err := json.Marshal(categories)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.prefRepo.UpdateFrequentCategories(userID, string(jsonBytes))
|
|
}
|
|
|
|
// GetLastUsedAccount returns the last used account ID
|
|
func (s *UserPreferenceService) GetLastUsedAccount(userID uint) (*uint, error) {
|
|
pref, err := s.prefRepo.GetOrCreate(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return pref.LastAccountID, nil
|
|
}
|
|
|
|
// GetLastUsedCategory returns the last used category ID
|
|
func (s *UserPreferenceService) GetLastUsedCategory(userID uint) (*uint, error) {
|
|
pref, err := s.prefRepo.GetOrCreate(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return pref.LastCategoryID, nil
|
|
}
|
|
|
|
// GetFrequentAccounts returns the list of frequently used account IDs
|
|
func (s *UserPreferenceService) GetFrequentAccounts(userID uint) ([]uint, error) {
|
|
pref, err := s.prefRepo.GetOrCreate(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if pref.FrequentAccounts == "" {
|
|
return []uint{}, nil
|
|
}
|
|
|
|
var accounts []uint
|
|
if err := json.Unmarshal([]byte(pref.FrequentAccounts), &accounts); err != nil {
|
|
return []uint{}, nil
|
|
}
|
|
|
|
return accounts, nil
|
|
}
|
|
|
|
// GetFrequentCategories returns the list of frequently used category IDs
|
|
func (s *UserPreferenceService) GetFrequentCategories(userID uint) ([]uint, error) {
|
|
pref, err := s.prefRepo.GetOrCreate(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if pref.FrequentCategories == "" {
|
|
return []uint{}, nil
|
|
}
|
|
|
|
var categories []uint
|
|
if err := json.Unmarshal([]byte(pref.FrequentCategories), &categories); err != nil {
|
|
return []uint{}, nil
|
|
}
|
|
|
|
return categories, nil
|
|
}
|
|
|
|
// ClearPreferences clears all user preferences
|
|
func (s *UserPreferenceService) ClearPreferences(userID uint) error {
|
|
pref, err := s.prefRepo.GetOrCreate(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pref.LastAccountID = nil
|
|
pref.LastCategoryID = nil
|
|
pref.FrequentAccounts = ""
|
|
pref.FrequentCategories = ""
|
|
|
|
return s.prefRepo.Update(pref)
|
|
}
|
|
|
|
// moveToFront moves an ID to the front of the slice, removing duplicates
|
|
func moveToFront(ids []uint, id uint) []uint {
|
|
// Remove existing occurrence
|
|
result := make([]uint, 0, len(ids)+1)
|
|
result = append(result, id)
|
|
|
|
for _, existingID := range ids {
|
|
if existingID != id {
|
|
result = append(result, existingID)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|