using Dpz.Core.Public.ViewModel;
namespace Dpz.Core.Service.RepositoryServiceImpl;
public class CodeNoteService(
IRepository<CodeNote> repository,
IMapper mapper,
ILogger<CodeNoteService> logger,
IConfiguration configuration
) : ICodeNoteService
{
public async Task<string> SaveNoteAsync(string[]? path, string name, string? note)
{
var codeNote = await BuildFluent(path, name, out var pathStr).FirstOrDefaultAsync();
if (codeNote == null)
{
var entity = new CodeNote
{
CreateTime = DateTime.Now,
LastUpdateTime = DateTime.Now,
Name = name,
Note = note,
Path = pathStr,
};
await repository.InsertAsync(entity);
return entity.Id.ToString();
}
codeNote.Note = note;
codeNote.LastUpdateTime = DateTime.Now;
await repository.UpdateAsync(codeNote);
return codeNote.Id.ToString();
}
public async Task<VmCodeNote?> FindAsync(string[]? path, string? name)
{
if (string.IsNullOrWhiteSpace(name))
{
return null;
}
var codeNote = await BuildFluent(path, name, out _).FirstOrDefaultAsync();
if (codeNote == null)
{
return null;
}
return mapper.Map<VmCodeNote>(codeNote);
}
private static string GetPathStr(string[]? path)
{
return path is not null && path.Length > 0 ? string.Join("/", path) : "";
}
private static FilterDefinition<CodeNote> BuildFilter(
string[]? path,
string? name,
out string pathStr
)
{
pathStr = GetPathStr(path);
return Builders<CodeNote>.Filter.And(
Builders<CodeNote>.Filter.Eq(x => x.Path, pathStr),
Builders<CodeNote>.Filter.Eq(x => x.Name, name)
);
}
private IFindFluent<CodeNote, CodeNote> BuildFluent(
string[]? path,
string name,
out string pathStr
)
{
var filter = BuildFilter(path, name, out pathStr);
return repository.SearchFor(
filter,
new FindOptions
{
Collation = new Collation(
locale: "en",
strength: new Optional<CollationStrength?>(CollationStrength.Secondary)
),
}
);
}
public async Task SaveAiAnalyzeResultAsync(string[]? path, string name, string analyzeResult)
{
var codeNote = await BuildFluent(path, name, out var pathStr).FirstOrDefaultAsync();
if (codeNote == null)
{
var entity = new CodeNote
{
CreateTime = DateTime.Now,
LastUpdateTime = DateTime.Now,
Name = name,
AiAnalyzeResult = analyzeResult,
Path = pathStr,
};
await repository.InsertAsync(entity);
return;
}
codeNote.AiAnalyzeResult = analyzeResult;
codeNote.LastUpdateTime = DateTime.Now;
await repository.UpdateAsync(codeNote);
return;
}
public async Task<bool> ShouldAnalyzeAsync(
string[]? path,
string? name,
CodeContainer? codeContainer
)
{
var useAiAnalyze = configuration.GetValue("CodeUseAIAnalyze", false);
if (!useAiAnalyze)
{
return false;
}
if (
codeContainer is not { Language: "csharp" }
|| string.IsNullOrWhiteSpace(codeContainer.CodeContent)
|| string.IsNullOrWhiteSpace(name)
)
{
return false;
}
var codeNote = await FindAsync(path, name);
if (string.IsNullOrWhiteSpace(codeContainer.AiAnalyzeResult))
{
return true;
}
if (codeNote == null)
{
return false;
}
return DateTime.Now - codeNote.LastUpdateTime > TimeSpan.FromDays(30);
}
public async Task<List<VmCodeNote>> GetNoteAsync(string[]? path)
{
#if DEBUG
var stopwatch = new Stopwatch();
stopwatch.Start();
#endif
var pathStr = GetPathStr(path);
var filter = Builders<CodeNote>.Filter.Eq(x => x.Path, pathStr);
var list = await repository
.SearchFor(
filter,
new FindOptions
{
Collation = new Collation(
locale: "en",
strength: new Optional<CollationStrength?>(CollationStrength.Secondary)
),
}
)
.ToListAsync();
#if DEBUG
stopwatch.Stop();
var queryTime = stopwatch.Elapsed;
logger.LogInformation("查询耗时:{QueryTime} ms", queryTime.TotalMilliseconds);
#endif
return mapper.Map<List<VmCodeNote>>(list);
}
public async Task<List<VmCodeNote>> GetAllNoteAsync()
{
var list = await repository.SearchFor(x => true).ToListAsync();
return mapper.Map<List<VmCodeNote>>(list);
}
public async Task DeleteAsync(string[]? path, string name)
{
var filter = BuildFilter(path, name, out _);
await repository.DeleteAsync(filter);
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这段代码定义了一个名为 CodeNoteService 的服务类(实现了 ICodeNoteService),主要用于在后端持久层(通过 IRepository
总体说明
- 实体:CodeNote(推断包含 Id、CreateTime、LastUpdateTime、Name、Note、Path、AiAnalyzeResult 等字段)。
- 存储:通过泛型仓储 IRepository
操作数据库(看上去像 MongoDB:使用 Builders、FilterDefinition、IFindFluent、Collation 等)。 - 映射:IMapper 用于将 CodeNote 映射为 VmCodeNote(返回给客户端的视图模型)。
- 路径处理:外部传入 string[]? path,内部通过 GetPathStr 将数组用 "/" 连接成一个路径字符串(空数组/null => "")。
方法详解
- SaveNoteAsync(string[]? path, string name, string? note)
- 尝试按 path + name 查找已存在的 CodeNote(使用 BuildFluent,带特定 collation)。
- 如果没找到:构建新的 CodeNote(设置 CreateTime、LastUpdateTime、Name、Note、Path),插入数据库,返回新记录的 Id 字符串。
- 如果找到:更新其 Note 与 LastUpdateTime,保存更新,返回已有记录 Id。
- FindAsync(string[]? path, string? name)
- 如果 name 空或空白则返回 null。
- 使用 BuildFluent 查找(path + name);找到就用 mapper 映射为 VmCodeNote 返回,否则返回 null。
- GetPathStr(string[]? path)
- 私有静态方法:把 path 数组 join 为 "a/b/c" 的形式,若 null 或空则返回空字符串 ""。
- BuildFilter(...)(静态)
- 构造 MongoDB 的 FilterDefinition:Path 等于 pathStr 且 Name 等于 name,用于 Delete/查找等。
- BuildFluent(string[]? path, string name, out string pathStr)
- 基于 BuildFilter 构建 IFindFluent<CodeNote, CodeNote> 查询,并传入 FindOptions 指定 Collation:
- locale = "en"
- strength = CollationStrength.Secondary
- 这意味着在比较时使用二级强度的排序规则(通常会忽略大小写差异,即实现 case-insensitive 比较,diacritics 处理根据 locale/strength 行为决定)。
- SaveAiAnalyzeResultAsync(string[]? path, string name, string analyzeResult)
- 与 SaveNoteAsync 相似,但保存/更新的是 AiAnalyzeResult 字段(同时更新 LastUpdateTime),若不存在则插入新记录。
- ShouldAnalyzeAsync(string[]? path, string? name, CodeContainer? codeContainer)
- 决定是否需要对某段代码调用 AI 分析,逻辑如下:
- 先读取配置键 "CodeUseAIAnalyze"(默认 false),若该配置为 false 则直接返回 false(不分析)。
- 如果 codeContainer 不存在或其 Language 不是 "csharp",或 CodeContent 为空,或 name 为空,返回 false。
- 如果 codeContainer.AiAnalyzeResult 为空(说明当前代码还没有 AI 结果),返回 true(需要分析)。
- 否则查找已有的 codeNote(FindAsync);如果没有对应的 codeNote,返回 false(已有 AI 结果但没有保存备注则不触发?——代码逻辑是这样)。
- 如果有 codeNote,则判断上次更新时间距今是否超过 30 天:超过 30 天则返回 true(需要重新分析),否则 false。
- GetNoteAsync(string[]? path)
- 查询指定 path 下的所有 CodeNote(Path 等于 pathStr),使用与 BuildFluent 相同的 Collation(case-insensitive),把结果列表映射为 List
返回。 - 在 DEBUG 模式下还会测量并记录查询耗时(logger.LogInformation)。
- GetAllNoteAsync()
- 返回仓库中所有 CodeNote(SearchFor(x => true)),并映射为 VmCodeNote 列表。
- DeleteAsync(string[]? path, string name)
- 构造 Path+Name 的 filter,然后调用 repository.DeleteAsync 删除对应记录。
一些实现细节与注意点
- Collation 的使用:BuildFluent 和 GetNoteAsync 在查询时都显式设置了 Collation 且 strength = Secondary,通常用于忽略大小写差异以做不区分大小写的匹配。注意插入/更新时没有显式设置 collation(只是查询时有效),插入时 Path 与 Name 的真实值(大小写)会被存储。
- 路径字符串构造:用 "/" 连接,注意如果 path 数组中包含 "/" 字符或空字符串,存储结果可能不是预期的分层表示。
- 时间字段:CreateTime 与 LastUpdateTime 都使用 DateTime.Now(本地时间);如果系统分布式/跨时区,考虑使用 UTC(DateTime.UtcNow)。
- 配置开关:是否使用 AI 分析由配置键 "CodeUseAIAnalyze" 控制,默认 false(代码中 GetValue("CodeUseAIAnalyze", false))。
- 代码对空值的保护:部分方法会检查 name 或 codeContainer.CodeContent 是否为 null/空;Find/Save 等查找依据 name 与 path 精确相等(但查询时使用了 collation)。
总体上该服务封装了对 CodeNote 的常见操作(增/查/改/删)、基于配置的 AI 分析触发决策以及将数据库实体映射为视图模型返回给调用方。若需要,我可以帮你:
- 说明某个方法的具体调用场景或接口签名;
- 建议改进(例如使用 UTC、处理 path 的规范化、增强并发/事务处理、添加索引建议等)。
评论加载中...