using Application.Dto.Category; using Service.AI; using System.Text.Json; namespace Application; /// /// 交易分类应用服务接口 /// public interface ITransactionCategoryApplication { Task> GetListAsync(TransactionType? type = null); Task GetByIdAsync(long id); Task CreateAsync(CreateCategoryRequest request); Task UpdateAsync(UpdateCategoryRequest request); Task DeleteAsync(long id); Task BatchCreateAsync(List requests); Task GenerateIconAsync(GenerateIconRequest request); Task UpdateSelectedIconAsync(UpdateSelectedIconRequest request); Task DeleteIconAsync(long classificationId); } /// /// 交易分类应用服务实现 /// public class TransactionCategoryApplication( ITransactionCategoryRepository categoryRepository, ITransactionRecordRepository transactionRepository, IBudgetRepository budgetRepository, ISmartHandleService smartHandleService ) : ITransactionCategoryApplication { public async Task> GetListAsync(TransactionType? type = null) { List categories; if (type.HasValue) { categories = await categoryRepository.GetCategoriesByTypeAsync(type.Value); } else { categories = (await categoryRepository.GetAllAsync()).ToList(); } return categories.Select(MapToResponse).ToList(); } public async Task GetByIdAsync(long id) { var category = await categoryRepository.GetByIdAsync(id); if (category == null) { throw new NotFoundException("分类不存在"); } return MapToResponse(category); } public async Task CreateAsync(CreateCategoryRequest request) { // 检查同名分类 var existing = await categoryRepository.GetByNameAndTypeAsync(request.Name, request.Type); if (existing != null) { throw new ValidationException("已存在相同名称的分类"); } var category = new TransactionCategory { Name = request.Name, Type = request.Type }; var result = await categoryRepository.AddAsync(category); if (!result) { throw new BusinessException("创建分类失败"); } return category.Id; } public async Task UpdateAsync(UpdateCategoryRequest request) { var category = await categoryRepository.GetByIdAsync(request.Id); if (category == null) { throw new NotFoundException("分类不存在"); } // 如果修改了名称,检查同名 if (category.Name != request.Name) { var existing = await categoryRepository.GetByNameAndTypeAsync(request.Name, category.Type); if (existing != null && existing.Id != request.Id) { throw new ValidationException("已存在相同名称的分类"); } // 同步更新交易记录中的分类名称 await transactionRepository.UpdateCategoryNameAsync(category.Name, request.Name, category.Type); await budgetRepository.UpdateBudgetCategoryNameAsync(category.Name, request.Name, category.Type); } category.Name = request.Name; category.UpdateTime = DateTime.Now; var success = await categoryRepository.UpdateAsync(category); if (!success) { throw new BusinessException("更新分类失败"); } } public async Task DeleteAsync(long id) { // 检查是否被使用 var inUse = await categoryRepository.IsCategoryInUseAsync(id); if (inUse) { throw new ValidationException("该分类已被使用,无法删除"); } var success = await categoryRepository.DeleteAsync(id); if (!success) { throw new BusinessException("删除分类失败,分类不存在"); } } public async Task BatchCreateAsync(List requests) { var categories = requests.Select(r => new TransactionCategory { Name = r.Name, Type = r.Type }).ToList(); var result = await categoryRepository.AddRangeAsync(categories); if (!result) { throw new BusinessException("批量创建分类失败"); } return categories.Count; } public async Task GenerateIconAsync(GenerateIconRequest request) { var category = await categoryRepository.GetByIdAsync(request.CategoryId); if (category == null) { throw new NotFoundException("分类不存在"); } // 使用 SmartHandleService 统一封装的图标生成方法 var svg = await smartHandleService.GenerateSingleCategoryIconAsync(category.Name, category.Type); if (string.IsNullOrWhiteSpace(svg)) { throw new BusinessException("AI生成图标失败"); } // 解析现有图标数组 var icons = string.IsNullOrWhiteSpace(category.Icon) ? new List() : JsonSerializer.Deserialize>(category.Icon) ?? new List(); // 添加新图标 icons.Add(svg); // 更新数据库 category.Icon = JsonSerializer.Serialize(icons); category.UpdateTime = DateTime.Now; var success = await categoryRepository.UpdateAsync(category); if (!success) { throw new BusinessException("更新分类图标失败"); } return svg; } public async Task UpdateSelectedIconAsync(UpdateSelectedIconRequest request) { var category = await categoryRepository.GetByIdAsync(request.CategoryId); if (category == null) { throw new NotFoundException("分类不存在"); } // 验证索引有效性 if (string.IsNullOrWhiteSpace(category.Icon)) { throw new ValidationException("该分类没有可用图标"); } var icons = JsonSerializer.Deserialize>(category.Icon); if (icons == null || request.SelectedIndex < 0 || request.SelectedIndex >= icons.Count) { throw new ValidationException("无效的图标索引"); } // 将选中的图标移到数组第一位 var selectedIcon = icons[request.SelectedIndex]; icons.RemoveAt(request.SelectedIndex); icons.Insert(0, selectedIcon); category.Icon = JsonSerializer.Serialize(icons); category.UpdateTime = DateTime.Now; var success = await categoryRepository.UpdateAsync(category); if (!success) { throw new BusinessException("更新图标失败"); } } public async Task DeleteIconAsync(long classificationId) { var category = await categoryRepository.GetByIdAsync(classificationId); if (category == null) { throw new NotFoundException("分类不存在"); } // 将 Icon 字段设置为 null category.Icon = null; category.UpdateTime = DateTime.Now; var success = await categoryRepository.UpdateAsync(category); if (!success) { throw new BusinessException("删除图标失败"); } } private static CategoryResponse MapToResponse(TransactionCategory category) { return new CategoryResponse { Id = category.Id, Name = category.Name, Type = category.Type, Icon = category.Icon, CreateTime = category.CreateTime, UpdateTime = category.UpdateTime }; } }