namespace Dpz.Core.Service.Mediator.Features.Code.Commands;
/// <summary>
/// 处理代码文档兼容:将历史备注和 AI 分析结果回填到文件树节点。
/// </summary>
public class CodeCompatibleHandler(
IRepository<CodeFileSystemEntry> codeFileSystemEntryRepository,
IRepository<CodeNote> codeNoteRepository
) : IRequestHandler<CodeCompatibleRequest>
{
/// <summary>
/// 按请求参数执行增量或全量兼容流程。
/// </summary>
public async ValueTask<Unit> Handle(
CodeCompatibleRequest request,
CancellationToken cancellationToken
)
{
if (request.NewEntries is { Count: > 0 })
{
await HandleIncrementalAsync(request.NewEntries, cancellationToken);
return Unit.Value;
}
await HandleFullAsync(cancellationToken);
return Unit.Value;
}
private async Task HandleIncrementalAsync(
IReadOnlyCollection<CodeCompatibleEntry> newEntries,
CancellationToken cancellationToken
)
{
var pathGroups = newEntries
.Select(x => BuildPathString(x.ParentPathSegments))
.Where(x => !string.IsNullOrWhiteSpace(x))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (pathGroups.Count == 0)
{
return;
}
var noteFilter = Builders<CodeNote>.Filter.In(x => x.Path, pathGroups);
var notes = await codeNoteRepository.SearchFor(noteFilter).ToListAsync(cancellationToken);
if (notes.Count == 0)
{
return;
}
var noteMap = notes.ToDictionary(
x => BuildNoteKey(x.Path, x.Name),
StringComparer.OrdinalIgnoreCase
);
var updateList = new List<CodeFileSystemEntry>();
foreach (var entry in newEntries)
{
var key = BuildNoteKey(BuildPathString(entry.ParentPathSegments), entry.Name);
if (!noteMap.TryGetValue(key, out var note))
{
continue;
}
var entryPathSegments = BuildPathSegments(entry.ParentPathSegments, entry.Name);
var entryFilter = Builders<CodeFileSystemEntry>.Filter.Eq(
x => x.PathSegments,
entryPathSegments
);
var updatedEntry = await codeFileSystemEntryRepository
.SearchFor(entryFilter)
.FirstOrDefaultAsync(cancellationToken);
if (updatedEntry == null)
{
continue;
}
var changed = false;
if (
string.IsNullOrWhiteSpace(updatedEntry.Description)
&& !string.IsNullOrWhiteSpace(note.Note)
)
{
updatedEntry.Description = note.Note;
changed = true;
}
if (
string.IsNullOrWhiteSpace(updatedEntry.AiAnalyzeResult)
&& !string.IsNullOrWhiteSpace(note.AiAnalyzeResult)
)
{
updatedEntry.AiAnalyzeResult = note.AiAnalyzeResult;
changed = true;
}
if (changed)
{
updateList.Add(updatedEntry);
}
}
if (updateList.Count > 0)
{
await codeFileSystemEntryRepository.UpdateAsync(updateList, cancellationToken);
}
}
private async Task HandleFullAsync(CancellationToken cancellationToken)
{
var notes = await codeNoteRepository.SearchFor(x => true).ToListAsync(cancellationToken);
if (notes.Count == 0)
{
return;
}
var entries = await codeFileSystemEntryRepository
.SearchFor(x => true)
.ToListAsync(cancellationToken);
if (entries.Count == 0)
{
return;
}
var entryMap = entries.ToDictionary(
x => BuildPathKey(x.PathSegments),
StringComparer.OrdinalIgnoreCase
);
var updateList = new List<CodeFileSystemEntry>();
foreach (var note in notes)
{
var pathSegments = BuildPathSegments(note.Path, note.Name);
if (pathSegments.Count == 0)
{
continue;
}
var key = BuildPathKey(pathSegments);
if (!entryMap.TryGetValue(key, out var entry))
{
continue;
}
var changed = false;
if (
string.IsNullOrWhiteSpace(entry.Description)
&& !string.IsNullOrWhiteSpace(note.Note)
)
{
entry.Description = note.Note;
changed = true;
}
if (
string.IsNullOrWhiteSpace(entry.AiAnalyzeResult)
&& !string.IsNullOrWhiteSpace(note.AiAnalyzeResult)
)
{
entry.AiAnalyzeResult = note.AiAnalyzeResult;
changed = true;
}
if (changed)
{
updateList.Add(entry);
}
}
if (updateList.Count > 0)
{
await codeFileSystemEntryRepository.UpdateAsync(updateList, cancellationToken);
}
}
private static List<string> BuildPathSegments(string? path, string name)
{
var segments = new List<string>();
if (!string.IsNullOrWhiteSpace(path))
{
segments.AddRange(
path.Split(
'/',
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries
)
);
}
if (!string.IsNullOrWhiteSpace(name))
{
segments.Add(name);
}
return segments;
}
private static string BuildPathKey(IEnumerable<string> segments)
{
return string.Join("/", segments);
}
private static string BuildPathString(IEnumerable<string> segments)
{
return string.Join("/", segments.Where(x => !string.IsNullOrWhiteSpace(x)));
}
private static string BuildNoteKey(string path, string name)
{
return string.Join("/", new[] { path, name }.Where(x => !string.IsNullOrWhiteSpace(x)));
}
private static List<string> BuildPathSegments(
IEnumerable<string> parentPathSegments,
string name
)
{
var segments = parentPathSegments.Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
if (!string.IsNullOrWhiteSpace(name))
{
segments.Add(name);
}
return segments;
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这是一个用于处理代码文档兼容性的命令处理器,主要功能是将历史备注和AI分析结果同步到文件系统树节点中。让我详细解释一下这个代码:
类的基本结构
public class CodeCompatibleHandler : IRequestHandler<CodeCompatibleRequest>
这是一个实现了MediatR模式的命令处理器,用于处理CodeCompatibleRequest请求。
构造函数依赖注入
public CodeCompatibleHandler(
IRepository<CodeFileSystemEntry> codeFileSystemEntryRepository,
IRepository<CodeNote> codeNoteRepository
)
注入了两个仓储:
codeFileSystemEntryRepository:管理文件系统条目codeNoteRepository:管理代码备注
主要处理逻辑
Handle方法
根据请求参数决定执行增量处理还是全量处理:
- 如果
request.NewEntries不为空,执行增量处理 - 否则执行全量处理
增量处理(HandleIncrementalAsync)
- 路径分组:从新条目中提取并去重父路径
- 查找匹配的备注:根据路径查找对应的CodeNote记录
- 构建备注映射:创建key-value映射,key为路径+名称的组合
- 更新文件系统条目:
- 遍历新条目,查找匹配的备注
- 如果文件系统条目的描述为空且备注不为空,则更新描述
- 如果文件系统条目的AI分析结果为空且备注的AI分析结果不为空,则更新
- 批量保存:将所有修改的条目批量更新到数据库
全量处理(HandleFullAsync)
- 加载所有数据:获取所有CodeNote和CodeFileSystemEntry记录
- 构建条目映射:以路径为key创建文件系统条目映射
- 同步数据:
- 遍历所有备注
- 查找对应的文件系统条目
- 按相同规则更新描述和AI分析结果
- 批量保存:更新修改的条目
辅助方法
路径处理方法
BuildPathSegments:将路径字符串和名称组合成路径段列表BuildPathKey:将路径段连接成完整路径字符串BuildPathString:过滤空值后连接路径段BuildNoteKey:构建备注的唯一键
核心业务逻辑
这个处理器的主要目的是数据同步:
- 确保文件系统条目具有对应的描述信息(从CodeNote.Note字段同步)
- 确保文件系统条目具有AI分析结果(从CodeNote.AiAnalyzeResult字段同步)
- 采用"只填充空值"的策略,不会覆盖已有数据
设计特点
- 增量与全量支持:可以针对特定新增条目进行增量处理,也可以进行全量同步
- 安全更新:只在目标字段为空时才进行更新,避免数据覆盖
- 批量操作:收集所有需要更新的条目后批量执行,提高性能
- 异步处理:全程使用异步操作,支持取消令牌
这个设计很适合在代码分析系统中,将历史的备注数据和AI分析结果迁移到新的文件系统结构中。
评论加载中...