fix
This commit is contained in:
330
.doc/ai-service-refactoring-summary.md
Normal file
330
.doc/ai-service-refactoring-summary.md
Normal file
@@ -0,0 +1,330 @@
|
||||
# AI 服务重构总结
|
||||
|
||||
---
|
||||
title: AI调用统一封装到SmartHandleService
|
||||
author: AI Assistant
|
||||
date: 2026-02-10
|
||||
status: final
|
||||
category: 重构
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本次重构将项目中所有分散的 AI 调用统一封装到 `SmartHandleService` 中,实现了AI功能的集中管理和统一维护。
|
||||
|
||||
## 重构前的问题
|
||||
|
||||
在重构前,项目中有多个地方直接依赖 `IOpenAiService` 进行 AI 调用:
|
||||
|
||||
1. **EmailParseServicesBase** (`Service/EmailServices/EmailParse/IEmailParseServices.cs`)
|
||||
- 邮件解析AI兜底逻辑
|
||||
- 包含大量重复的类型判断代码
|
||||
|
||||
2. **CategoryIconGenerationJob** (`Service/Jobs/CategoryIconGenerationJob.cs`)
|
||||
- 定时任务批量生成分类图标
|
||||
- 包含复杂的Prompt构建逻辑
|
||||
|
||||
3. **TransactionCategoryApplication** (`Application/TransactionCategoryApplication.cs`)
|
||||
- 手动触发单个图标生成
|
||||
- 包含SVG提取和验证逻辑
|
||||
|
||||
4. **BudgetService** (`Service/Budget/BudgetService.cs`)
|
||||
- 生成预算执行报告
|
||||
- 包含复杂的数据格式化逻辑
|
||||
|
||||
### 存在的问题
|
||||
|
||||
- **代码重复**:多处存在相似的AI调用代码
|
||||
- **职责分散**:AI相关逻辑散落在不同层级
|
||||
- **难以维护**:修改AI调用逻辑需要改动多个文件
|
||||
- **测试困难**:需要在多个测试类中Mock `IOpenAiService`
|
||||
|
||||
## 重构方案
|
||||
|
||||
### 1. 扩展 SmartHandleService 接口
|
||||
|
||||
在 `Service/AI/SmartHandleService.cs` 中新增以下方法:
|
||||
|
||||
```csharp
|
||||
public interface ISmartHandleService
|
||||
{
|
||||
// 原有方法
|
||||
Task SmartClassifyAsync(long[] transactionIds, Action<(string type, string data)> chunkAction);
|
||||
Task AnalyzeBillAsync(string userInput, Action<string> chunkAction);
|
||||
Task<TransactionParseResult?> ParseOneLineBillAsync(string text);
|
||||
|
||||
// 新增方法
|
||||
|
||||
/// <summary>
|
||||
/// 从邮件正文中使用AI提取交易记录(AI兜底方案)
|
||||
/// </summary>
|
||||
Task<(string card, string reason, decimal amount, decimal balance, TransactionType type, DateTime? occurredAt)[]?>
|
||||
ParseEmailByAiAsync(string emailBody);
|
||||
|
||||
/// <summary>
|
||||
/// 为分类生成多个SVG图标(定时任务使用)
|
||||
/// </summary>
|
||||
Task<List<string>?> GenerateCategoryIconsAsync(string categoryName, TransactionType categoryType, int iconCount = 5);
|
||||
|
||||
/// <summary>
|
||||
/// 为分类生成单个SVG图标(手动触发使用)
|
||||
/// </summary>
|
||||
Task<string?> GenerateSingleCategoryIconAsync(string categoryName, TransactionType categoryType);
|
||||
|
||||
/// <summary>
|
||||
/// 生成预算执行报告(HTML格式)
|
||||
/// </summary>
|
||||
Task<string?> GenerateBudgetReportAsync(string promptWithData, int year, int month);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 实现新增方法
|
||||
|
||||
#### ParseEmailByAiAsync - 邮件解析
|
||||
|
||||
将 `EmailParseServicesBase.ParseByAiAsync` 方法迁移到 `SmartHandleService`:
|
||||
|
||||
- 保留完整的JSON解析逻辑
|
||||
- 保留markdown代码块清理逻辑
|
||||
- 保留单个对象兼容处理
|
||||
- 将类型判断逻辑保留在基类中供子类使用
|
||||
|
||||
#### GenerateCategoryIconsAsync - 批量图标生成
|
||||
|
||||
将 `CategoryIconGenerationJob.GenerateIconsForCategoryAsync` 的核心逻辑迁移:
|
||||
|
||||
- 封装5种不同风格的图标生成Prompt
|
||||
- 处理JSON数组解析和验证
|
||||
- 统一的错误处理和日志记录
|
||||
|
||||
#### GenerateSingleCategoryIconAsync - 单个图标生成
|
||||
|
||||
将 `TransactionCategoryApplication.GenerateIconAsync` 的核心逻辑迁移:
|
||||
|
||||
- 简化的极简风格Prompt
|
||||
- SVG标签提取逻辑
|
||||
- 统一的返回格式
|
||||
|
||||
#### GenerateBudgetReportAsync - 预算报告生成
|
||||
|
||||
将 `BudgetService` 中的报告生成逻辑迁移:
|
||||
|
||||
- 接收完整的Prompt(包含数据和格式要求)
|
||||
- 统一的HTML格式要求
|
||||
- 统一的错误处理
|
||||
|
||||
### 3. 更新调用方
|
||||
|
||||
#### EmailParseServicesBase
|
||||
|
||||
```csharp
|
||||
// 修改前
|
||||
public abstract class EmailParseServicesBase(
|
||||
ILogger<EmailParseServicesBase> logger,
|
||||
IOpenAiService openAiService
|
||||
) : IEmailParseServices
|
||||
|
||||
// 修改后
|
||||
public abstract class EmailParseServicesBase(
|
||||
ILogger<EmailParseServicesBase> logger,
|
||||
ISmartHandleService smartHandleService
|
||||
) : IEmailParseServices
|
||||
```
|
||||
|
||||
调用改为:
|
||||
```csharp
|
||||
result = await smartHandleService.ParseEmailByAiAsync(emailContent) ?? [];
|
||||
```
|
||||
|
||||
#### CategoryIconGenerationJob
|
||||
|
||||
```csharp
|
||||
// 修改前
|
||||
public class CategoryIconGenerationJob(
|
||||
ITransactionCategoryRepository categoryRepository,
|
||||
IOpenAiService openAiService,
|
||||
ILogger<CategoryIconGenerationJob> logger) : IJob
|
||||
|
||||
// 修改后
|
||||
public class CategoryIconGenerationJob(
|
||||
ITransactionCategoryRepository categoryRepository,
|
||||
ISmartHandleService smartHandleService,
|
||||
ILogger<CategoryIconGenerationJob> logger) : IJob
|
||||
```
|
||||
|
||||
调用简化为:
|
||||
```csharp
|
||||
var icons = await smartHandleService.GenerateCategoryIconsAsync(category.Name, category.Type, iconCount: 5);
|
||||
```
|
||||
|
||||
#### TransactionCategoryApplication
|
||||
|
||||
```csharp
|
||||
// 修改前
|
||||
public class TransactionCategoryApplication(
|
||||
...
|
||||
IOpenAiService openAiService,
|
||||
...) : ITransactionCategoryApplication
|
||||
|
||||
// 修改后
|
||||
public class TransactionCategoryApplication(
|
||||
...
|
||||
ISmartHandleService smartHandleService,
|
||||
...) : ITransactionCategoryApplication
|
||||
```
|
||||
|
||||
调用简化为:
|
||||
```csharp
|
||||
var svg = await smartHandleService.GenerateSingleCategoryIconAsync(category.Name, category.Type);
|
||||
```
|
||||
|
||||
#### BudgetService
|
||||
|
||||
```csharp
|
||||
// 修改前
|
||||
public class BudgetService(
|
||||
...
|
||||
IOpenAiService openAiService,
|
||||
...) : IBudgetService
|
||||
|
||||
// 修改后
|
||||
public class BudgetService(
|
||||
...
|
||||
ISmartHandleService smartHandleService,
|
||||
...) : IBudgetService
|
||||
```
|
||||
|
||||
调用简化为:
|
||||
```csharp
|
||||
var htmlReport = await smartHandleService.GenerateBudgetReportAsync(dataPrompt, year, month);
|
||||
```
|
||||
|
||||
### 4. 更新测试代码
|
||||
|
||||
更新所有测试类,将 `IOpenAiService` 的Mock改为 `ISmartHandleService`:
|
||||
|
||||
- `BudgetStatsTest.cs`
|
||||
- `TransactionCategoryApplicationTest.cs`
|
||||
|
||||
## 重构收益
|
||||
|
||||
### 1. 代码集中管理
|
||||
|
||||
- 所有AI调用逻辑集中在 `SmartHandleService` 中
|
||||
- 便于统一修改Prompt策略
|
||||
- 便于添加新的AI功能
|
||||
|
||||
### 2. 职责清晰
|
||||
|
||||
- `OpenAiService`:底层AI API调用(同步/流式)
|
||||
- `SmartHandleService`:业务级AI功能封装
|
||||
- 业务服务层:专注业务逻辑,无需关心AI调用细节
|
||||
|
||||
### 3. 易于测试
|
||||
|
||||
- 只需Mock `ISmartHandleService`
|
||||
- 测试更简洁,Mock对象更少
|
||||
- 可以为 `SmartHandleService` 编写独立的单元测试
|
||||
|
||||
### 4. 易于扩展
|
||||
|
||||
- 新增AI功能只需在 `SmartHandleService` 中添加方法
|
||||
- 不影响现有业务代码
|
||||
- 符合开闭原则(对扩展开放,对修改封闭)
|
||||
|
||||
### 5. 代码复用
|
||||
|
||||
- 消除了多处重复的AI调用代码
|
||||
- 统一的错误处理和日志记录
|
||||
- 统一的返回格式和验证逻辑
|
||||
|
||||
## 测试结果
|
||||
|
||||
重构后运行全部211个单元测试,全部通过:
|
||||
|
||||
```
|
||||
已通过! - 失败: 0,通过: 211,已跳过: 0,总计: 211,持续时间: 22 s
|
||||
```
|
||||
|
||||
## 受影响的文件清单
|
||||
|
||||
### 新增/修改的文件
|
||||
|
||||
1. **Service/AI/SmartHandleService.cs**
|
||||
- 新增4个方法接口定义
|
||||
- 新增4个方法实现
|
||||
- 新增辅助方法:`ParseEmailSingleRecord`, `DetermineTransactionType`
|
||||
|
||||
### 修改的业务服务
|
||||
|
||||
2. **Service/EmailServices/EmailParse/IEmailParseServices.cs**
|
||||
- 构造函数参数改为 `ISmartHandleService`
|
||||
- 移除 `ParseByAiAsync` 方法,改为调用 `smartHandleService.ParseEmailByAiAsync`
|
||||
- 保留 `DetermineTransactionType` 为protected方法供子类使用
|
||||
|
||||
3. **Service/EmailServices/EmailParse/EmailParseFormCcsvc.cs**
|
||||
- 构造函数参数改为 `ISmartHandleService`
|
||||
|
||||
4. **Service/EmailServices/EmailParse/EmailParseForm95555.cs**
|
||||
- 构造函数参数改为 `ISmartHandleService`
|
||||
|
||||
5. **Service/Jobs/CategoryIconGenerationJob.cs**
|
||||
- 构造函数参数改为 `ISmartHandleService`
|
||||
- 简化 `GenerateIconsForCategoryAsync` 方法
|
||||
|
||||
6. **Application/TransactionCategoryApplication.cs**
|
||||
- 构造函数参数改为 `ISmartHandleService`
|
||||
- 简化 `GenerateIconAsync` 方法
|
||||
|
||||
7. **Service/Budget/BudgetService.cs**
|
||||
- 构造函数参数改为 `ISmartHandleService`
|
||||
- 简化报告生成调用
|
||||
|
||||
### 修改的测试文件
|
||||
|
||||
8. **WebApi.Test/Budget/BudgetStatsTest.cs**
|
||||
- Mock对象改为 `ISmartHandleService`
|
||||
|
||||
9. **WebApi.Test/Application/TransactionCategoryApplicationTest.cs**
|
||||
- Mock对象改为 `ISmartHandleService`
|
||||
|
||||
## 架构图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 业务层 (Business Layer) │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ BudgetService│ │CategoryApp │ │EmailParse │ │
|
||||
│ │ │ │ │ │Services │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
│ │ │ │ │
|
||||
│ └─────────────────┼─────────────────┘ │
|
||||
│ │ │
|
||||
└───────────────────────────┼────────────────────────────────┘
|
||||
│ 依赖
|
||||
┌───────────────────────────┼────────────────────────────────┐
|
||||
│ AI 服务层 (AI Service Layer) │
|
||||
│ │ │
|
||||
│ ┌───────▼────────┐ │
|
||||
│ │SmartHandleService│ ◄── 统一封装AI调用 │
|
||||
│ │ (业务级AI功能) │ │
|
||||
│ └───────┬────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────▼────────┐ │
|
||||
│ │ OpenAiService │ ◄── 底层API调用 │
|
||||
│ │ (HTTP Client) │ │
|
||||
│ └────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 后续建议
|
||||
|
||||
1. **添加单元测试**:为 `SmartHandleService` 的新方法添加独立的单元测试
|
||||
2. **监控日志**:关注AI调用的性能和错误日志
|
||||
3. **Prompt优化**:基于实际使用反馈持续优化Prompt
|
||||
4. **缓存机制**:考虑为图标生成等场景添加缓存
|
||||
5. **重试机制**:考虑为AI调用添加重试逻辑
|
||||
|
||||
## 总结
|
||||
|
||||
本次重构成功将项目中所有AI调用统一封装到 `SmartHandleService`,实现了代码的集中管理和职责分离。重构后的代码更易维护、更易测试、更易扩展,符合单一职责原则和依赖倒置原则。所有测试用例全部通过,证明重构没有引入功能回归问题。
|
||||
Reference in New Issue
Block a user