添加配置管理功能,包括获取和设置配置值的接口及实现
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 24s
Docker Build & Deploy / Deploy to Production (push) Successful in 7s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s

This commit is contained in:
孙诚
2026-01-05 15:21:13 +08:00
parent 5a824dac91
commit d44cceb6e4
8 changed files with 306 additions and 22 deletions

77
Service/ConfigService.cs Normal file
View File

@@ -0,0 +1,77 @@
namespace Service;
public interface IConfigService
{
/// <summary>
/// 根据Key获取配置值
/// </summary>
Task<T?> GetConfigByKeyAsync<T>(string key);
/// <summary>
/// 设置配置值
/// </summary>
Task<bool> SetConfigByKeyAsync<T>(string key, T value);
}
public class ConfigService(IConfigRepository configRepository) : IConfigService
{
public async Task<T?> GetConfigByKeyAsync<T>(string key)
{
var config = await configRepository.GetByKeyAsync(key);
if (config == null || string.IsNullOrEmpty(config.Value))
{
return default;
}
return config.Type switch
{
ConfigType.Boolean => (T)(object)bool.Parse(config.Value),
ConfigType.String => (T)(object)config.Value,
ConfigType.Number => (T)Convert.ChangeType(config.Value, typeof(T)),
ConfigType.Json => JsonSerializer.Deserialize<T>(config.Value) ?? default,
_ => default
};
}
public async Task<bool> SetConfigByKeyAsync<T>(string key, T value)
{
if (value == null)
{
return false;
}
var config = await configRepository.GetByKeyAsync(key);
var type = typeof(T) switch
{
Type t when t == typeof(bool) => ConfigType.Boolean,
Type t when t == typeof(int)
|| t == typeof(double)
|| t == typeof(float)
|| t == typeof(decimal) => ConfigType.Number,
Type t when t == typeof(string) => ConfigType.String,
_ => ConfigType.Json
};
var valueStr = type switch
{
ConfigType.Boolean => value.ToString()!.ToLower(),
ConfigType.Number => value.ToString()!,
ConfigType.String => value as string ?? string.Empty,
ConfigType.Json => JsonSerializer.Serialize(value),
_ => throw new InvalidOperationException("Unsupported config type")
};
if (config == null)
{
config = new ConfigEntity
{
Key = key,
Type = type,
};
return await configRepository.AddAsync(config);
}
config.Value = valueStr;
config.Type = type;
return await configRepository.UpdateAsync(config);
}
}

View File

@@ -14,7 +14,8 @@ public class SmartHandleService(
ITextSegmentService textSegmentService,
ILogger<SmartHandleService> logger,
ITransactionCategoryRepository categoryRepository,
IOpenAiService openAiService
IOpenAiService openAiService,
IConfigService configService
) : ISmartHandleService
{
public async Task SmartClassifyAsync(long[] transactionIds, Action<(string, string)> chunkAction)
@@ -258,9 +259,9 @@ public class SmartHandleService(
{
// 第一步使用AI生成聚合SQL查询
var now = DateTime.Now;
var sqlPrompt = $"""
当前日期:{now:yyyy年M月d日}{now:yyyy-MM-dd}
用户问题:{userInput}
var sqlPrompt = $$"""
当前日期:{{now:yyyy年M月d日}}{{now:yyyy-MM-dd}}
用户问题:{{userInput}}
数据库类型SQLite
数据库表名TransactionRecord
@@ -291,22 +292,27 @@ public class SmartHandleService(
- strftime('%Y-%m-%d', OccurredAt)
- 使 YEAR()MONTH()DAY() SQLite不支持
1
SELECT Classify, COUNT(*) as TransactionCount, SUM(ABS(Amount)) as TotalAmount, AVG(ABS(Amount)) as AvgAmount FROM TransactionRecord WHERE Type = 0 AND OccurredAt >= '2025-10-01' AND OccurredAt < '2026-01-01' AND (Classify LIKE '%%' OR Reason LIKE '%%' OR Reason LIKE '%%' OR Reason LIKE '%%') GROUP BY Classify ORDER BY TotalAmount DESC
2
SELECT strftime('%Y', OccurredAt) as Year, strftime('%m', OccurredAt) as Month, COUNT(*) as TransactionCount, SUM(ABS(Amount)) as TotalAmount FROM TransactionRecord WHERE Type = 0 AND OccurredAt >= '2025-06-01' GROUP BY strftime('%Y', OccurredAt), strftime('%m', OccurredAt) ORDER BY Year, Month
3
SELECT COUNT(*) as TransactionCount, SUM(ABS(Amount)) as TotalAmount, AVG(ABS(Amount)) as AvgAmount, MAX(ABS(Amount)) as MaxAmount FROM TransactionRecord WHERE Type = 0 AND OccurredAt >= '2025-12-01' AND OccurredAt < '2026-01-01'
4 - 使
1000
SELECT OccurredAt, Classify, Reason, ABS(Amount) as Amount FROM TransactionRecord WHERE Type = 0 AND ABS(Amount) > 1000 ORDER BY Amount DESC LIMIT 50
SQL会被一下DOTNET代码执行,
```C#
public async Task<List<dynamic>> ExecuteDynamicSqlAsync(string completeSql)
{
var dt = await FreeSql.Ado.ExecuteDataTableAsync(completeSql);
var result = new List<dynamic>();
foreach (System.Data.DataRow row in dt.Rows)
{
var expando = new System.Dynamic.ExpandoObject() as IDictionary<string, object>;
foreach (System.Data.DataColumn column in dt.Columns)
{
expando[column.ColumnName] = row[column];
}
result.Add(expando);
}
return result;
}
```
SQL语句
""";
@@ -344,10 +350,15 @@ public class SmartHandleService(
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
var userPromptExtra = await configService.GetConfigByKeyAsync<string>("BillAnalysisPrompt");
var dataPrompt = $"""
当前日期:{DateTime.Now:yyyy年M月d日}
用户问题:{userInput}
【用户要求(重要)】
{userInput}
查询结果数据JSON格式
{dataJson}
@@ -375,6 +386,9 @@ public class SmartHandleService(
13. 提供洞察分析:根据数据给出有价值的发现和趋势分析
14. 给出实用建议:基于数据提供合理的财务建议
15. 语言专业、清晰、简洁
【用户补充(重要)】
{userPromptExtra}
直接输出纯净的HTML内容不要markdown代码块标记。
""";