using Dpz.Core.Public.ViewModel;
using Dpz.Core.Public.ViewModel.Response;

namespace Dpz.Core.Web.Models;

/// <summary>
/// 源码搜索结果视图模型(按父目录分组)
/// </summary>
public class CodeSearchResultsViewModel
{
    /// <summary>
    /// 搜索关键词
    /// </summary>
    public required string Keyword { get; set; }

    /// <summary>
    /// 匹配总数
    /// </summary>
    public int Total { get; set; }

    /// <summary>
    /// 按父目录分组的结果
    /// </summary>
    public List<CodeSearchResultGroup> Groups { get; set; } = [];

    /// <summary>
    /// 根据搜索匹配项构建分组结果
    /// </summary>
    public static CodeSearchResultsViewModel FromSearch(
        string keyword,
        IReadOnlyCollection<CodeFileSystemEntryResponse> entries
    )
    {
        var groups = entries
            .GroupBy(x => string.Join("/", x.ParentPathSegments))
            .OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase)
            .Select(g => new CodeSearchResultGroup
            {
                ParentPath = g.First().ParentPathSegments.ToList(),
                Items = g.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
                    .Select(ToChildrenTree)
                    .ToList(),
            })
            .ToList();

        return new CodeSearchResultsViewModel
        {
            Keyword = keyword,
            Total = entries.Count,
            Groups = groups,
        };
    }

    private static ChildrenTree ToChildrenTree(CodeFileSystemEntryResponse entry)
    {
        return new ChildrenTree
        {
            Name = entry.Name,
            LastUpdateTime = entry.LastWriteTime,
            CurrentPath = entry.PathSegments.ToList(),
            Note = entry.Description ?? "",
        };
    }
}

/// <summary>
/// 源码搜索结果分组(同一父目录下的匹配项)
/// </summary>
public class CodeSearchResultGroup
{
    /// <summary>
    /// 父目录路径分段(根目录为空集合)
    /// </summary>
    public List<string> ParentPath { get; set; } = [];

    /// <summary>
    /// 父目录名称(用于图标匹配,根目录为空)
    /// </summary>
    public string? ParentName => ParentPath.Count > 0 ? ParentPath[^1] : null;

    /// <summary>
    /// 该目录下的匹配项
    /// </summary>
    public List<ChildrenTree> Items { get; set; } = [];
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个用于展示源码搜索结果的视图模型(ViewModel)定义,主要用于ASP.NET Core Web应用中。代码定义了两个类来组织和展示搜索结果。

主要类说明

1. CodeSearchResultsViewModel - 搜索结果视图模型

这是主要的视图模型类,用于封装整个搜索结果的数据结构:

属性:

  • Keyword: 用户输入的搜索关键词
  • Total: 匹配到的总条目数
  • Groups: 按父目录分组后的搜索结果列表

核心方法 FromSearch

public static CodeSearchResultsViewModel FromSearch(
    string keyword,
    IReadOnlyCollection<CodeFileSystemEntryResponse> entries
)

这是一个静态工厂方法,负责将原始搜索结果转换为分组的视图模型:

  1. 分组逻辑:按照文件的父目录路径(ParentPathSegments)进行分组
  2. 排序
    • 目录级别:按父路径字母顺序(忽略大小写)
    • 文件级别:按文件名字母顺序(忽略大小写)
  3. 数据转换:将 CodeFileSystemEntryResponse 转换为 ChildrenTree 对象

辅助方法 ToChildrenTree

private static ChildrenTree ToChildrenTree(CodeFileSystemEntryResponse entry)

将文件系统条目响应对象转换为树形子节点对象,提取关键信息:

  • 文件名
  • 最后更新时间
  • 完整路径
  • 描述信息

2. CodeSearchResultGroup - 搜索结果分组

代表同一父目录下的所有匹配项:

属性:

  • ParentPath: 父目录的路径分段(例如:["src", "Controllers"]
  • ParentName: 计算属性,返回父目录的名称(路径的最后一段),根目录时返回 null
    • 使用索引 [^1] 获取列表最后一个元素(C# 8.0+ 语法)
  • Items: 该目录下所有匹配的文件/条目列表

使用场景示例

假设搜索关键词为 "UserService",找到以下文件:

  • /src/Services/UserService.cs
  • /src/Services/UserServiceTests.cs
  • /lib/Utils/UserServiceHelper.cs

处理后的结果结构:

Groups:
  - ParentPath: ["src", "Services"]
    ParentName: "Services"
    Items: [UserService.cs, UserServiceTests.cs]
    
  - ParentPath: ["lib", "Utils"]
    ParentName: "Utils"
    Items: [UserServiceHelper.cs]

技术特点

  1. C# 11+ 特性:使用 required 关键字强制初始化 Keyword 属性
  2. 集合表达式:使用 = [] 初始化空列表(C# 12+)
  3. LINQ 链式调用:优雅地实现分组、排序、转换
  4. 不可变性考虑:接受 IReadOnlyCollection 作为参数
  5. 关注点分离:将原始响应对象转换为适合UI展示的视图模型

这种设计模式使得前端可以更容易地按目录层次结构展示搜索结果,提升用户体验。

评论加载中...