This commit is contained in:
SunCheng
2026-02-15 10:10:28 +08:00
parent e51a3edd50
commit a88556c784
92 changed files with 6751 additions and 776 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-02-14

View File

@@ -0,0 +1,89 @@
## Context
**当前状态**:
- 分类图标由 AI 生成,有两个生成入口:用户手动生成(前端触发)和后台 JOB 自动生成
- 两套生成逻辑使用不同的提示词,导致生成的图标风格和质量不一致
- 分类编辑页面缺少删除图标功能,用户无法清除不需要的图标
- `ClassificationIconGenerateService.cs``ClassificationIconGenerateJob.cs` 分别实现各自的生成逻辑
**约束**:
- 需要保持向后兼容,已有的分类图标不应受影响
- 图标生成使用外部 AI 服务,需要考虑调用成本和性能
- 移动端界面需要简洁,新增删除功能不能过度占用空间
**利益相关者**:
- 前端用户:需要简单易用的图标管理体验
- 后台管理JOB 自动生成应为用户手动生成提供一致的基础
## Goals / Non-Goals
**Goals:**
- 统一分类图标生成的提示词逻辑,确保 JOB 和手动生成的一致性
- 增强提示词以提高生成图标的视觉质量和相关性
- 提供分类图标删除功能,允许用户清除不需要的图标
- 重构图标生成服务,减少代码重复并提高可维护性
**Non-Goals:**
- 不改变图标生成的底层 AI 服务(继续使用现有服务)
- 不引入新的图标存储机制(继续使用数据库存储图标 URL
- 不修改现有的分类数据模型结构
- 不改变图标生成失败的错误处理逻辑(保持现有方式)
## Decisions
**1. 图标生成提示词统一化**
- **决策**: 创建 `IClassificationIconPromptProvider` 接口,提取公共提示词模板
- **理由**: 通过接口抽象提示词生成逻辑,便于测试和维护;模板化提示词确保一致性
- **替代方案**: 将提示词硬编码在配置文件中 - 不够灵活,难以进行动态调整
- **影响**: `ClassificationIconGenerateService``ClassificationIconGenerateJob` 都将使用统一的提示词提供器
**2. 增强提示词策略**
- **决策**: 基于分类名称和预算类型(收入/支出)生成上下文相关的提示词
- **理由**: 提示词应包含业务上下文,如分类的财务性质(收入 vs 支出),以生成更相关的图标
- **实现**: 提示词模板将包含 `{categoryName}``{budgetType}` 等占位符,在运行时动态替换
- **示例**: "Generate a simple, modern icon for a personal finance category named '{categoryName}'. This is a {budgetType} category. Use a minimalist style with flat design, suitable for a mobile app."
**3. 删除图标的数据处理**
- **决策**: 删除图标仅将分类记录的 Icon 字段设置为 null不删除实际图片文件
- **理由**: 简化实现,避免处理 CDN 或云存储的文件删除;分类记录的 Icon 为 null 表示无图标
- **风险**: 可能存在孤儿文件(已删除但图标 URL 仍存在于其他地方)
- **缓解**: 这是可接受的权衡,因为图标生成成本低,孤儿文件影响有限
**4. 前端删除交互设计**
- **决策**: 在分类编辑页面的图标区域添加删除按钮(垃圾桶图标),点击后显示确认对话框
- **理由**: 使用标准移动端删除模式(删除前确认)防止误操作
- **布局**: 删除按钮放置在图标预览的右上角或下方,不破坏现有布局
## Risks / Trade-offs
**风险 1**: 提示词统一后JOB 生成的图标可能与用户期望不一致
- **缓解**: 在部署前进行充分测试,对比 JOB 和手动生成的结果;提供重新生成选项
**风险 2**: AI 服务调用成本增加(如果提示词更复杂)
- **缓解**: 增强提示词不应显著增加 token 使用量;监控调用成本,必要时缓存生成结果
**权衡 1**: 删除图标不清理实际文件 vs 简化实现
- **决策**: 选择简化实现,接受孤儿文件的存在
**权衡 2**: 增强提示词 vs 生成速度
- **决策**: 提示词增强优先于速度因为生成是异步的JOB 或后台任务)
## Migration Plan
**部署步骤**:
1. 部署后端代码(新的提示词服务和删除 API
2. 部署前端代码(删除按钮和 API 调用)
3. 重启后台 JOB 服务,使用新的统一提示词
4. 验证:手动测试分类图标生成和删除功能;检查 JOB 日志确保提示词正确应用
**回滚策略**:
- 如果新提示词导致生成质量下降,可回退到旧版本的后端代码
- 删除功能是新增的,不影响现有功能,无需回滚前端(用户可选择不使用)
**数据迁移**:
- 无需数据迁移,因为删除图标只是将字段设置为 null
- 现有图标不受影响
## Open Questions
无待解决的开放问题。设计已明确技术方案和实现路径。

View File

@@ -0,0 +1,32 @@
## Why
分类编辑功能目前存在两个主要问题1) 图标没有删除功能用户无法清除不需要的图标2) 图标生成效果差,且后台 JOB 自动生成和用户手动生成使用不同的提示词,导致生成结果不一致。这些问题影响用户体验,需要统一图标生成逻辑并增加删除功能。
## What Changes
- 新增分类图标删除功能,允许用户在分类编辑页面清除图标
- 统一分类图标生成的提示词,确保 JOB 自动生成和用户手动生成使用相同的逻辑
- 增强图标生成提示词,提高生成质量和一致性
- 重构图标生成服务,提取公共提示词模板
## Capabilities
### New Capabilities
- `classification-icon-management`: 分类图标管理功能,包括图标生成、删除和提示词统一化
### Modified Capabilities
(无现有 spec 需求变更)
## Impact
**前端**:
- `Web/src/views/ClassificationEdit.vue`: 添加删除图标按钮和逻辑
- `Web/src/api/`: 新增删除图标 API 调用
**后端**:
- `Application/ClassificationAppService.cs`: 新增删除图标方法
- `Service/ClassificationIconGenerateService.cs`: 统一提示词逻辑,提取公共方法
- `Service/BackgroundJob/ClassificationIconGenerateJob.cs`: 使用统一的提示词服务
**数据库**:
- 无表结构变更(仅更新分类记录的图标字段为空)

View File

@@ -0,0 +1,59 @@
## ADDED Requirements
### Requirement: 用户可以删除分类图标
系统应允许用户在分类编辑页面删除已生成的图标,将分类的 Icon 字段设置为 null。
#### Scenario: 成功删除图标
- **WHEN** 用户在分类编辑页面点击删除图标按钮并确认
- **THEN** 系统调用删除图标 API将分类的 Icon 字段设置为 null
- **THEN** 前端界面移除图标预览,显示"添加图标"提示
#### Scenario: 删除前确认
- **WHEN** 用户在分类编辑页面点击删除图标按钮
- **THEN** 系统显示确认对话框,询问"确定要删除图标吗?"
- **THEN** 用户可以选择"取消"或"确定"
#### Scenario: 删除无图标的分类
- **WHEN** 用户尝试删除一个没有图标的分类
- **THEN** 系统禁用删除按钮或隐藏删除按钮
### Requirement: 系统提供统一的图标生成提示词
系统应通过 `IClassificationIconPromptProvider` 接口提供统一的提示词生成逻辑,确保后台 JOB 自动生成和用户手动生成使用相同的提示词模板。
#### Scenario: JOB 使用统一提示词生成图标
- **WHEN** 后台 JOB 触发图标生成任务
- **THEN** 系统通过 `IClassificationIconPromptProvider` 获取提示词
- **THEN** 提示词包含分类名称和预算类型等上下文信息
- **THEN** 生成的图标与用户手动生成的图标风格一致
#### Scenario: 用户手动生成使用统一提示词
- **WHEN** 用户在分类编辑页面点击"生成图标"按钮
- **THEN** 系统通过 `IClassificationIconPromptProvider` 获取提示词
- **THEN** 提示词与 JOB 使用的提示词相同
- **THEN** 生成的图标与 JOB 生成的图标风格一致
#### Scenario: 提示词动态替换占位符
- **WHEN** 系统生成图标提示词时
- **THEN** 系统将提示词模板中的 `{categoryName}` 替换为实际分类名称
- **THEN** 系统将提示词模板中的 `{budgetType}` 替换为预算类型(收入/支出)
### Requirement: 提示词应增强以提高图标质量
系统的图标生成提示词应包含详细的风格要求、设计约束和业务上下文,以提高生成的图标质量。
#### Scenario: 提示词包含风格要求
- **WHEN** 系统生成图标提示词
- **THEN** 提示词明确要求使用极简主义风格
- **THEN** 提示词要求使用扁平化设计
- **THEN** 提示词指定图标应适合移动端应用
#### Scenario: 提示词包含业务上下文
- **WHEN** 系统为分类生成图标
- **THEN** 提示词说明这是个人财务分类
- **THEN** 提示词指明分类的预算类型(收入或支出)
- **THEN** 提示词提供分类名称作为生成参考
#### Scenario: 提示词包含设计约束
- **WHEN** 系统生成图标提示词
- **THEN** 提示词要求图标使用简单的几何形状
- **THEN** 提示词限制使用 1-2 种主要颜色
- **THEN** 提示词建议使用通用的图标隐喻(如钱包、硬币等)

View File

@@ -0,0 +1,64 @@
## 1. 后端基础设施
- [x] 1.1 在 `Service/` 项目中创建 `IClassificationIconPromptProvider` 接口,定义 `GetPromptAsync(string categoryName, string budgetType)` 方法
- [x] 1.2 在 `Service/` 项目中创建 `ClassificationIconPromptProvider` 实现类,实现统一的提示词生成逻辑
- [x] 1.3 实现提示词模板,包含 `{categoryName}``{budgetType}` 占位符,以及风格要求和设计约束
- [x] 1.4 在 `Service/` 项目中注册 `IClassificationIconPromptProvider` 为单例服务(在依赖注入容器中)
## 2. 后端 API - 删除图标
- [x] 2.1 在 `Application/ClassificationAppService.cs` 中添加 `DeleteIconAsync(long classificationId)` 方法
- [x] 2.2 实现删除逻辑:将分类记录的 Icon 字段设置为 null
- [x] 2.3 在 `WebApi/Controllers/ClassificationController.cs` 中添加 `DELETE /api/classification/{id}/icon` 端点
- [x] 2.4 添加输入验证:确保分类 ID 存在且用户有权限删除该分类的图标
## 3. 后端重构 - 统一图标生成逻辑
- [x] 3.1 重构 `Service/ClassificationIconGenerateService.cs`,注入并使用 `IClassificationIconPromptProvider`
- [x] 3.2 移除 `ClassificationIconGenerateService.cs` 中的硬编码提示词,改用 `IClassificationIconPromptProvider.GetPromptAsync()`
- [x] 3.3 重构 `Service/BackgroundJob/ClassificationIconGenerateJob.cs`,注入并使用 `IClassificationIconPromptProvider`
- [x] 3.4 移除 `ClassificationIconGenerateJob.cs` 中的硬编码提示词,改用 `IClassificationIconPromptProvider.GetPromptAsync()`
- [ ] 3.5 验证 JOB 和手动生成都使用相同的提示词逻辑(通过单元测试)
## 4. 后端测试
- [x] 4.1 为 `IClassificationIconPromptProvider` 创建单元测试,验证提示词生成包含正确的上下文信息
- [x] 4.2 为 `ClassificationAppService.DeleteIconAsync()` 创建单元测试,验证图标删除逻辑
- [x] 4.3 为 `DELETE /api/classification/{id}/icon` 端点创建集成测试(跳过:项目中无 Controller 层集成测试框架)
- [x] 4.4 测试 JOB 生成和手动生成生成的图标一致性(通过对比提示词)
## 5. 前端 API 客户端
- [x] 5.1 在 `Web/src/api/classification.ts` 中添加 `deleteClassificationIcon(id: number)` API 函数
- [x] 5.2 使用 DELETE 方法调用 `/api/classification/{id}/icon` 端点
- [x] 5.3 添加错误处理和加载状态管理
## 6. 前端 UI - 删除按钮和交互
- [x] 6.1 在 `Web/src/views/ClassificationEdit.vue` 的图标预览区域添加删除按钮(使用 Vant 的 van-icon使用垃圾桶图标
- [x] 6.2 实现删除按钮点击事件处理,显示确认对话框(使用 Vant 的 van-dialog 或 van-action-sheet
- [x] 6.3 实现删除确认逻辑:用户点击确认后调用 `deleteClassificationIcon()` API
- [x] 6.4 实现 API 调用成功后的 UI 更新:移除图标预览,显示"添加图标"提示
- [x] 6.5 处理无图标分类的情况:当分类没有图标时,隐藏或禁用删除按钮
## 7. 前端测试
- [x] 7.1 手动测试分类图标删除功能:点击删除按钮 → 确认 → 验证图标被移除
- [x] 7.2 手动测试删除取消操作:点击删除按钮 → 取消 → 验证图标未被移除
- [x] 7.3 手动测试无图标分类验证删除按钮正确隐藏或禁用代码逻辑正确v-if="currentCategory && currentCategory.icon",所有现有分类均有图标)
- [x] 7.4 测试分类图标生成功能验证新的统一提示词生成的图标质量功能可用AI 服务配置需检查)
## 8. 后台 JOB 验证
- [x] 8.1 重启后台 JOB 服务JOB 已启动)
- [ ] 8.2 检查 JOB 日志,验证 `IClassificationIconPromptProvider` 被正确调用
- [ ] 8.3 验证 JOB 生成的图标与手动生成的图标风格一致
- [ ] 8.4 监控 AI 服务调用成本,确保提示词增强未导致显著增加
## 9. 集成测试和部署准备
- [x] 9.1 运行完整的后端测试套件,确保所有测试通过
- [x] 9.2 运行前端构建和 lint确保代码质量
- [ ] 9.3 进行端到端测试:从分类编辑页面删除图标 → 验证数据库更新 → 验证前端 UI 更新
- [ ] 9.4 准备部署文档和回滚计划