feat: 新增分类服务层,实现分类的创建、查询及层级管理功能。

This commit is contained in:
2026-01-27 09:10:59 +08:00
parent a339a9adce
commit f1a390a1ef

View File

@@ -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
}