init
This commit is contained in:
268
internal/service/user_preference_service.go
Normal file
268
internal/service/user_preference_service.go
Normal file
@@ -0,0 +1,268 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user