chore: migrate remaining ECharts components to Chart.js
- Migrated 4 components from ECharts to Chart.js: * MonthlyExpenseCard.vue (折线图) * DailyTrendChart.vue (双系列折线图) * ExpenseCategoryCard.vue (环形图) * BudgetChartAnalysis.vue (仪表盘 + 多种图表) - Removed all ECharts imports and environment variable switches - Unified all charts to use BaseChart.vue component - Build verified: pnpm build success ✓ - No echarts imports remaining ✓ Refs: openspec/changes/migrate-remaining-echarts-to-chartjs
This commit is contained in:
94
Service/IconSearch/SearchKeywordGeneratorService.cs
Normal file
94
Service/IconSearch/SearchKeywordGeneratorService.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using Service.AI;
|
||||
|
||||
namespace Service.IconSearch;
|
||||
|
||||
public record SearchKeywordSettings
|
||||
{
|
||||
public string KeywordPromptTemplate { get; init; } =
|
||||
"为以下中文分类名称生成3-5个相关的英文搜索关键字,用于搜索图标:{categoryName}。" +
|
||||
"输出格式为JSON数组,例如:[\"food\", \"restaurant\", \"dining\"]。";
|
||||
}
|
||||
|
||||
public class SearchKeywordGeneratorService(
|
||||
IOpenAiService openAiService,
|
||||
IOptions<SearchKeywordSettings> settings,
|
||||
ILogger<SearchKeywordGeneratorService> logger
|
||||
) : ISearchKeywordGeneratorService
|
||||
{
|
||||
private readonly SearchKeywordSettings _settings = settings.Value;
|
||||
|
||||
public async Task<List<string>> GenerateKeywordsAsync(string categoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(categoryName))
|
||||
{
|
||||
logger.LogWarning("分类名称为空,无法生成搜索关键字");
|
||||
return [];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var prompt = _settings.KeywordPromptTemplate.Replace("{categoryName}", categoryName);
|
||||
var response = await openAiService.ChatAsync(prompt, timeoutSeconds: 15);
|
||||
|
||||
if (string.IsNullOrEmpty(response))
|
||||
{
|
||||
logger.LogWarning("AI未返回搜索关键字,分类:{CategoryName}", categoryName);
|
||||
return [];
|
||||
}
|
||||
|
||||
var keywords = ParseKeywordsFromResponse(response);
|
||||
logger.LogInformation("为分类 {CategoryName} 生成了 {Count} 个搜索关键字:{Keywords}",
|
||||
categoryName, keywords.Count, string.Join(", ", keywords));
|
||||
|
||||
return keywords;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "生成搜索关键字失败,分类:{CategoryName}", categoryName);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> ParseKeywordsFromResponse(string response)
|
||||
{
|
||||
try
|
||||
{
|
||||
var jsonNode = JsonNode.Parse(response);
|
||||
if (jsonNode is JsonArray arrayNode)
|
||||
{
|
||||
var keywords = new List<string>();
|
||||
foreach (var item in arrayNode)
|
||||
{
|
||||
if (item is JsonValue value && value.TryGetValue(out string keyword))
|
||||
{
|
||||
keywords.Add(keyword);
|
||||
}
|
||||
}
|
||||
return keywords;
|
||||
}
|
||||
else if (jsonNode is JsonObject jsonObject)
|
||||
{
|
||||
if (jsonObject.TryGetPropertyValue("keywords", out var keywordsNode) && keywordsNode is JsonArray arrayNode2)
|
||||
{
|
||||
var keywords = new List<string>();
|
||||
foreach (var item in arrayNode2)
|
||||
{
|
||||
if (item is JsonValue value && value.TryGetValue(out string keyword))
|
||||
{
|
||||
keywords.Add(keyword);
|
||||
}
|
||||
}
|
||||
return keywords;
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogWarning("无法解析AI响应为关键字数组:{Response}", response);
|
||||
return [];
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "解析AI响应失败:{Response}", response);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user