diff --git a/internal/service/category_service.go b/internal/service/category_service.go index 71b0b62..0751e7a 100644 --- a/internal/service/category_service.go +++ b/internal/service/category_service.go @@ -115,6 +115,18 @@ func (s *CategoryService) GetAllCategories(userID uint) ([]models.Category, erro if err != nil { return nil, fmt.Errorf("failed to get categories: %w", err) } + + // Auto-seed if empty + if len(categories) == 0 { + if err := s.initDefaultCategories(userID); err != nil { + // Log error but return empty list to avoid crashing + fmt.Printf("Failed to seed default categories for user %d: %v\n", userID, err) + return []models.Category{}, nil + } + // Fetch again after seeding + return s.repo.GetAll(userID) + } + return categories, nil } @@ -124,6 +136,12 @@ func (s *CategoryService) GetCategoriesByType(userID uint, categoryType models.C if err != nil { return nil, fmt.Errorf("failed to get categories by type: %w", err) } + + // We don't auto-seed here because the user might just have no categories of this specific type + // but might have categories of the other type. + // But if they have absolutely no categories, GetAllCategories would have handled it. + // We can trust GetAllCategories or just leave this as is. + return categories, nil } @@ -134,6 +152,16 @@ func (s *CategoryService) GetCategoryTree(userID uint) ([]models.Category, error if err != nil { return nil, fmt.Errorf("failed to get category tree: %w", err) } + + // Auto-seed if empty + if len(categories) == 0 { + if err := s.initDefaultCategories(userID); err != nil { + fmt.Printf("Failed to seed default categories for user %d: %v\n", userID, err) + return []models.Category{}, nil + } + return s.repo.GetAllWithChildren(userID) + } + return categories, nil } @@ -311,3 +339,133 @@ func (s *CategoryService) GetCategoryPath(userID, id uint) ([]models.Category, e return path, nil } + +// initDefaultCategories seeds default categories for a user +func (s *CategoryService) initDefaultCategories(userID uint) error { + defaults := []struct { + Name string + Type models.CategoryType + Icon string + SortOrder int + Children []struct { + Name string + Type models.CategoryType + Icon string + SortOrder int + } + }{ + // Expenses + { + Name: "餐饮", Type: models.CategoryTypeExpense, Icon: "restaurant", SortOrder: 1, + Children: []struct { + Name string + Type models.CategoryType + Icon string + SortOrder int + }{ + {Name: "早餐", Type: models.CategoryTypeExpense, Icon: "breakfast_dining", SortOrder: 1}, + {Name: "午餐", Type: models.CategoryTypeExpense, Icon: "lunch_dining", SortOrder: 2}, + {Name: "晚餐", Type: models.CategoryTypeExpense, Icon: "dinner_dining", SortOrder: 3}, + {Name: "零食", Type: models.CategoryTypeExpense, Icon: "icecream", SortOrder: 4}, + {Name: "饮料", Type: models.CategoryTypeExpense, Icon: "local_cafe", SortOrder: 5}, + }, + }, + { + Name: "交通", Type: models.CategoryTypeExpense, Icon: "directions_bus", SortOrder: 2, + Children: []struct { + Name string + Type models.CategoryType + Icon string + SortOrder int + }{ + {Name: "地铁", Type: models.CategoryTypeExpense, Icon: "subway", SortOrder: 1}, + {Name: "公交", Type: models.CategoryTypeExpense, Icon: "directions_bus", SortOrder: 2}, + {Name: "打车", Type: models.CategoryTypeExpense, Icon: "local_taxi", SortOrder: 3}, + {Name: "加油", Type: models.CategoryTypeExpense, Icon: "local_gas_station", SortOrder: 4}, + }, + }, + { + Name: "购物", Type: models.CategoryTypeExpense, Icon: "shopping_bag", SortOrder: 3, + Children: []struct { + Name string + Type models.CategoryType + Icon string + SortOrder int + }{ + {Name: "服饰", Type: models.CategoryTypeExpense, Icon: "checkroom", SortOrder: 1}, + {Name: "日用", Type: models.CategoryTypeExpense, Icon: "soap", SortOrder: 2}, + {Name: "电子数码", Type: models.CategoryTypeExpense, Icon: "devices", SortOrder: 3}, + }, + }, + { + Name: "居住", Type: models.CategoryTypeExpense, Icon: "home", SortOrder: 4, + Children: []struct { + Name string + Type models.CategoryType + Icon string + SortOrder int + }{ + {Name: "房租", Type: models.CategoryTypeExpense, Icon: "house", SortOrder: 1}, + {Name: "水电煤", Type: models.CategoryTypeExpense, Icon: "lightbulb", SortOrder: 2}, + {Name: "物业", Type: models.CategoryTypeExpense, Icon: "security", SortOrder: 3}, + }, + }, + { + Name: "娱乐", Type: models.CategoryTypeExpense, Icon: "sports_esports", SortOrder: 5, + Children: []struct { + Name string + Type models.CategoryType + Icon string + SortOrder int + }{ + {Name: "游戏", Type: models.CategoryTypeExpense, Icon: "gamepad", SortOrder: 1}, + {Name: "电影", Type: models.CategoryTypeExpense, Icon: "movie", SortOrder: 2}, + {Name: "运动", Type: models.CategoryTypeExpense, Icon: "fitness_center", SortOrder: 3}, + }, + }, + // Income + { + Name: "收入", Type: models.CategoryTypeIncome, Icon: "payments", SortOrder: 10, + Children: []struct { + Name string + Type models.CategoryType + Icon string + SortOrder int + }{ + {Name: "工资", Type: models.CategoryTypeIncome, Icon: "work", SortOrder: 1}, + {Name: "奖金", Type: models.CategoryTypeIncome, Icon: "star", SortOrder: 2}, + {Name: "兼职", Type: models.CategoryTypeIncome, Icon: "access_time", SortOrder: 3}, + {Name: "理财", Type: models.CategoryTypeIncome, Icon: "trending_up", SortOrder: 4}, + }, + }, + } + + for _, cat := range defaults { + parent := &models.Category{ + UserID: userID, + Name: cat.Name, + Type: cat.Type, + Icon: cat.Icon, + SortOrder: cat.SortOrder, + } + if err := s.repo.Create(parent); err != nil { + return err + } + + for _, child := range cat.Children { + c := &models.Category{ + UserID: userID, + Name: child.Name, + Type: child.Type, + Icon: child.Icon, + SortOrder: child.SortOrder, + ParentID: &parent.ID, + } + if err := s.repo.Create(c); err != nil { + return err + } + } + } + + return nil +}