using Application.Dto.Transaction; using Service.AI; namespace Application; /// /// 交易应用服务接口 /// public interface ITransactionApplication { /// /// 获取交易记录列表(分页) /// Task> GetListAsync(TransactionQueryRequest request); /// /// 根据ID获取交易记录详情 /// Task GetByIdAsync(long id); /// /// 创建交易记录 /// Task CreateAsync(CreateTransactionRequest request); /// /// 更新交易记录 /// Task UpdateAsync(UpdateTransactionRequest request); /// /// 删除交易记录 /// Task DeleteByIdAsync(long id); /// /// 根据邮件ID获取交易记录列表 /// Task> GetByEmailIdAsync(long emailId); /// /// 根据日期获取交易记录列表 /// Task> GetByDateAsync(DateTime date); /// /// 获取未确认的交易记录列表 /// Task> GetUnconfirmedListAsync(); /// /// 获取未确认的交易记录数量 /// Task GetUnconfirmedCountAsync(); /// /// 获取未分类的账单数量 /// Task GetUnclassifiedCountAsync(); /// /// 获取未分类的账单列表 /// Task> GetUnclassifiedAsync(int pageSize); /// /// 确认所有未确认的记录 /// Task ConfirmAllUnconfirmedAsync(long[] ids); /// /// 智能分类(AI分类,支持回调) /// Task SmartClassifyAsync(long[] transactionIds, Action<(string type, string data)> onChunk); /// /// 一句话录账解析 /// Task ParseOneLineAsync(string text); /// /// 账单分析(AI分析,支持回调) /// Task AnalyzeBillAsync(string userInput, Action onChunk); /// /// 批量更新分类 /// Task BatchUpdateClassifyAsync(List items); /// /// 按摘要批量更新分类 /// Task BatchUpdateByReasonAsync(BatchUpdateByReasonRequest request); } /// /// 交易应用服务实现 /// public class TransactionApplication( ITransactionRecordRepository transactionRepository, ISmartHandleService smartHandleService, ILogger logger ) : ITransactionApplication { public async Task> GetListAsync(TransactionQueryRequest request) { var classifies = string.IsNullOrWhiteSpace(request.Classify) ? null : request.Classify.Split(',', StringSplitOptions.RemoveEmptyEntries); TransactionType? transactionType = request.Type.HasValue ? (TransactionType)request.Type.Value : null; var list = await transactionRepository.QueryAsync( year: request.Year, month: request.Month, startDate: request.StartDate, endDate: request.EndDate, type: transactionType, classifies: classifies, searchKeyword: request.SearchKeyword, reason: request.Reason, pageIndex: request.PageIndex, pageSize: request.PageSize, sortByAmount: request.SortByAmount); var total = await transactionRepository.CountAsync( year: request.Year, month: request.Month, startDate: request.StartDate, endDate: request.EndDate, type: transactionType, classifies: classifies, searchKeyword: request.SearchKeyword, reason: request.Reason); return new PagedResult { Data = list.Select(MapToResponse).ToArray(), Total = (int)total }; } public async Task GetByIdAsync(long id) { var transaction = await transactionRepository.GetByIdAsync(id); if (transaction == null) { throw new NotFoundException("交易记录不存在"); } return MapToResponse(transaction); } public async Task CreateAsync(CreateTransactionRequest request) { // 解析日期字符串 if (!DateTime.TryParse(request.OccurredAt, out var occurredAt)) { throw new ValidationException("交易时间格式不正确"); } var transaction = new TransactionRecord { OccurredAt = occurredAt, Reason = request.Reason ?? string.Empty, Amount = request.Amount, Type = request.Type, Classify = request.Classify ?? string.Empty, ImportFrom = "手动录入", ImportNo = Guid.NewGuid().ToString("N"), Card = "手动", EmailMessageId = 0 }; var result = await transactionRepository.AddAsync(transaction); if (!result) { throw new BusinessException("创建交易记录失败"); } } public async Task UpdateAsync(UpdateTransactionRequest request) { var transaction = await transactionRepository.GetByIdAsync(request.Id); if (transaction == null) { throw new NotFoundException("交易记录不存在"); } // 更新可编辑字段 transaction.Reason = request.Reason ?? string.Empty; transaction.Amount = request.Amount; transaction.Balance = request.Balance; transaction.Type = request.Type; transaction.Classify = request.Classify ?? string.Empty; // 更新交易时间 if (!string.IsNullOrEmpty(request.OccurredAt) && DateTime.TryParse(request.OccurredAt, out var occurredAt)) { transaction.OccurredAt = occurredAt; } // 清除待确认状态 transaction.UnconfirmedClassify = null; transaction.UnconfirmedType = null; var success = await transactionRepository.UpdateAsync(transaction); if (!success) { throw new BusinessException("更新交易记录失败"); } } public async Task DeleteByIdAsync(long id) { var success = await transactionRepository.DeleteAsync(id); if (!success) { throw new BusinessException("删除交易记录失败,记录不存在"); } } public async Task> GetByEmailIdAsync(long emailId) { var transactions = await transactionRepository.GetByEmailIdAsync(emailId); return transactions.Select(MapToResponse).ToList(); } public async Task> GetByDateAsync(DateTime date) { // 获取当天的开始和结束时间 var startDate = date.Date; var endDate = startDate.AddDays(1); var records = await transactionRepository.QueryAsync(startDate: startDate, endDate: endDate); return records.Select(MapToResponse).ToList(); } public async Task> GetUnconfirmedListAsync() { var records = await transactionRepository.GetUnconfirmedRecordsAsync(); return records.Select(MapToResponse).ToList(); } public async Task GetUnconfirmedCountAsync() { var records = await transactionRepository.GetUnconfirmedRecordsAsync(); return records.Count; } public async Task GetUnclassifiedCountAsync() { return (int)await transactionRepository.CountAsync(); } public async Task> GetUnclassifiedAsync(int pageSize) { var records = await transactionRepository.GetUnclassifiedAsync(pageSize); return records.Select(MapToResponse).ToList(); } public async Task ConfirmAllUnconfirmedAsync(long[] ids) { if (ids == null || ids.Length == 0) { throw new ValidationException("请提供要确认的交易ID列表"); } return await transactionRepository.ConfirmAllUnconfirmedAsync(ids); } public async Task SmartClassifyAsync(long[] transactionIds, Action<(string type, string data)> onChunk) { // 验证 if (transactionIds == null || transactionIds.Length == 0) { throw new ValidationException("请提供要分类的账单ID"); } // 调用Service进行智能分类 await smartHandleService.SmartClassifyAsync(transactionIds, onChunk); } public async Task ParseOneLineAsync(string text) { if (string.IsNullOrWhiteSpace(text)) { throw new ValidationException("解析文本不能为空"); } var result = await smartHandleService.ParseOneLineBillAsync(text); if (result == null) { throw new BusinessException("AI解析失败"); } return result; } public async Task AnalyzeBillAsync(string userInput, Action onChunk) { if (string.IsNullOrWhiteSpace(userInput)) { throw new ValidationException("请输入分析内容"); } await smartHandleService.AnalyzeBillAsync(userInput, onChunk); } public async Task BatchUpdateClassifyAsync(List items) { if (items == null || items.Count == 0) { throw new ValidationException("请提供要更新的记录"); } var successCount = 0; foreach (var item in items) { var record = await transactionRepository.GetByIdAsync(item.Id); if (record != null) { record.Classify = item.Classify ?? string.Empty; // 如果提供了Type,也更新Type if (item.Type.HasValue) { record.Type = item.Type.Value; } // 清除待确认状态 if (!string.IsNullOrEmpty(record.Classify)) { record.UnconfirmedClassify = null; } if (item.Type.HasValue && record.Type == item.Type.Value) { record.UnconfirmedType = null; } var success = await transactionRepository.UpdateAsync(record); if (success) { successCount++; } } } return successCount; } public async Task BatchUpdateByReasonAsync(BatchUpdateByReasonRequest request) { if (string.IsNullOrWhiteSpace(request.Reason)) { throw new ValidationException("摘要不能为空"); } if (string.IsNullOrWhiteSpace(request.Classify)) { throw new ValidationException("分类不能为空"); } return await transactionRepository.BatchUpdateByReasonAsync( request.Reason, request.Type, request.Classify); } private static TransactionResponse MapToResponse(TransactionRecord record) { return new TransactionResponse { Id = record.Id, OccurredAt = record.OccurredAt, Reason = record.Reason, Amount = record.Amount, Balance = record.Balance, Type = record.Type, Classify = record.Classify, UnconfirmedClassify = record.UnconfirmedClassify, UnconfirmedType = record.UnconfirmedType }; } }