Files
EmailBill/Application/TransactionCategoryApplication.cs

232 lines
7.2 KiB
C#
Raw Normal View History

2026-02-10 17:49:19 +08:00
using Application.Dto.Category;
using Service.AI;
using System.Text.Json;
namespace Application;
/// <summary>
/// 交易分类应用服务接口
/// </summary>
public interface ITransactionCategoryApplication
{
Task<List<CategoryResponse>> GetListAsync(TransactionType? type = null);
Task<CategoryResponse> GetByIdAsync(long id);
Task<long> CreateAsync(CreateCategoryRequest request);
Task UpdateAsync(UpdateCategoryRequest request);
Task DeleteAsync(long id);
Task<int> BatchCreateAsync(List<CreateCategoryRequest> requests);
Task<string> GenerateIconAsync(GenerateIconRequest request);
Task UpdateSelectedIconAsync(UpdateSelectedIconRequest request);
}
/// <summary>
/// 交易分类应用服务实现
/// </summary>
public class TransactionCategoryApplication(
ITransactionCategoryRepository categoryRepository,
ITransactionRecordRepository transactionRepository,
IBudgetRepository budgetRepository,
ISmartHandleService smartHandleService,
ILogger<TransactionCategoryApplication> logger
) : ITransactionCategoryApplication
{
public async Task<List<CategoryResponse>> GetListAsync(TransactionType? type = null)
{
List<TransactionCategory> categories;
if (type.HasValue)
{
categories = await categoryRepository.GetCategoriesByTypeAsync(type.Value);
}
else
{
categories = (await categoryRepository.GetAllAsync()).ToList();
}
return categories.Select(MapToResponse).ToList();
}
public async Task<CategoryResponse> GetByIdAsync(long id)
{
var category = await categoryRepository.GetByIdAsync(id);
if (category == null)
{
throw new NotFoundException("分类不存在");
}
return MapToResponse(category);
}
public async Task<long> 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<int> BatchCreateAsync(List<CreateCategoryRequest> 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<string> 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<string>()
: JsonSerializer.Deserialize<List<string>>(category.Icon) ?? new List<string>();
// 添加新图标
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<List<string>>(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("更新图标失败");
}
}
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
};
}
}