using Dpz.Core.Hangfire;
using Hangfire;

namespace Dpz.Core.WebApi.Controllers;

/// <summary>
/// 源码管理
/// </summary>
[ApiController, Route("api/[controller]")]
public class CodeController(ICodeFileSystemEntryService codeFileSystemEntryService) : ControllerBase
{
    /// <summary>
    /// 获取源码树节点
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ProducesResponseType<CodeNoteTree>(StatusCodes.Status200OK)]
    public async Task<IActionResult> GetCodeNotes([FromQuery] params string[] path)
    {
        var tree = await codeFileSystemEntryService.BuildCodeNoteTreeAsync(path);
        if (
            tree is { CodeContainer.CodeContent: not null, FileName: not null }
            && await codeFileSystemEntryService.ShouldAnalyzeAsync(
                tree.CurrentPaths.ToArray(),
                tree.FileName,
                tree.CodeContainer
            )
        )
        {
            var parentPath = tree.ParentPaths.ToArray();
            BackgroundJob.Enqueue<AnalyzeCodeActivator>(x =>
                x.AnalyzeAsync(tree.CodeContainer.CodeContent, parentPath, tree.FileName)
            );
        }
        return Ok(tree);
    }

    /// <summary>
    /// 搜索
    /// </summary>
    /// <param name="keyword"></param>
    /// <returns></returns>
    [HttpGet("search")]
    [ProducesResponseType<CodeNoteTree>(StatusCodes.Status200OK)]
    public async Task<IActionResult> Search([FromQuery][Required] string keyword)
    {
        if (string.IsNullOrWhiteSpace(keyword))
        {
            return Ok(CodeNoteTree.FromSearch([]));
        }

        var matchList = await codeFileSystemEntryService.SearchAsync(keyword);
        var model = CodeNoteTree.FromSearch(matchList);
        return Ok(model);
    }

    /// <summary>
    /// 保存源码说明
    /// </summary>
    /// <returns></returns>
    [HttpPost, Authorize(Policy = "System")]
    [ProducesResponseType<CodeNoteTree>(StatusCodes.Status204NoContent)]
    [ProducesResponseType<CodeNoteTree>(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> SaveNote([FromBody] CodeSaveDto model)
    {
        var saved = await codeFileSystemEntryService.SaveDescriptionAsync(
            model.Path,
            model.Name,
            model.Note
        );
        if (!saved)
        {
            return BadRequest("path not exists");
        }
        return NoContent();
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

下面以简洁明了的方式解释这段控制器代码的功能、各端点的行为及关键点(均为 C# ASP.NET Core WebAPI 代码):

总体说明

  • 这是一个用于“源码管理/源码说明”的 API 控制器,路由为 api/Code([ApiController], Route("api/[controller]"))。
  • 依赖注入了 ICodeFileSystemEntryService(构造器注入的简写语法:CodeController(ICodeFileSystemEntryService ...))。
  • 使用了异步方法(Task)并结合 Hangfire(BackgroundJob.Enqueue)来在后台队列分析源码内容。
  • 代码中带了 Swagger/响应类型注解([ProducesResponseType])和授权约束([Authorize(Policy = "System")])。

各个动作说明

  1. GetCodeNotes ([HttpGet])
  • 路径:GET api/Code

  • 参数:可变长度的 query 参数 path(params string[] path)。调用示例:GET api/Code?path=a&path=b 或 GET api/Code?path=/foo。

  • 行为:

    • 调用 codeFileSystemEntryService.BuildCodeNoteTreeAsync(path) 构建源码树/节点(返回 CodeNoteTree)。
    • 如果返回的 tree 满足:tree.CodeContainer.CodeContent 不为空 且 tree.FileName 不为空,并且 service 的 ShouldAnalyzeAsync(...) 返回 true,则将一次分析任务入队到 Hangfire:
      • BackgroundJob.Enqueue(x => x.AnalyzeAsync(tree.CodeContainer.CodeContent, parentPath, tree.FileName))
      • 也就是说把当前文件内容、父路径数组和文件名提交给 AnalyzeCodeActivator 的 AnalyzeAsync 在后台处理(异步、持久化的后台作业)。
    • 最后返回 Ok(tree)(200 + JSON)。
  • 关键点:

    • 通过 C# 的属性模式匹配判断 CodeContainer.CodeContent / FileName 是否为 null(现代写法)。
    • parentPath 使用 tree.ParentPaths.ToArray()(传递父路径数组给后台任务)。
    • Hangfire 的表达式入队要求方法签名和类型可被序列化调用,AnalyzeCodeActivator 需要是可解析的服务/类型并有公有方法 AnalyzeAsync。
  1. Search ([HttpGet("search")])
  • 路径:GET api/Code/search
  • 参数:[FromQuery] [Required] string keyword(必须在查询字符串中提供 keyword)。
  • 行为:
    • 如果 keyword 为空或只含空白,直接返回一个空的搜索结果 CodeNoteTree.FromSearch([])(Ok)。
    • 否则调用 codeFileSystemEntryService.SearchAsync(keyword) 得到匹配列表,然后用 CodeNoteTree.FromSearch(matchList) 构建模型并返回 Ok(model)。
  • 作用:对源码/说明内容进行关键字搜索并返回树/匹配结构。
  1. SaveNote ([HttpPost], [Authorize(Policy = "System")])
  • 路径:POST api/Code
  • 授权:只有满足名为 "System" 的策略的用户才可调用。
  • 请求体:[FromBody] CodeSaveDto model(包含 Path、Name、Note 等字段)。
  • 行为:
    • 调用 codeFileSystemEntryService.SaveDescriptionAsync(model.Path, model.Name, model.Note) 以保存源码说明/注释。
    • 如果返回 false(保存失败/路径不存在),返回 BadRequest("path not exists")。
    • 成功则返回 NoContent()(204)。
  • 作用:保存/更新源码项的说明文本。

额外说明 / 注意事项

  • Swagger/响应注解:[ProducesResponseType] 用于 API 文档,某些地方(比如将类型与 204 一起标注)语义上可能不严谨(204 无内容通常不带响应体)。
  • params string[] path 的绑定:调用时可重复传递 query 参数名 path 多次,或在客户端构造数组形式;需要注意前端如何传参。
  • Hangfire 后台任务:
    • 使用 BackgroundJob.Enqueue 会把任务放入队列并立即返回。要确保 AnalyzeCodeActivator 与 AnalyzeAsync 在应用范围内可被解析并且方法参数可以被序列化。
  • 并发/幂等性:GetCodeNotes 里会在满足条件时多次入队分析(如果前端频繁请求同一文件可能产生多次任务),若不希望重复分析应在 ShouldAnalyzeAsync 或任务端做去重/防护。

总结

  • 该控制器提供:获取源码树(并在必要时异步分析源码)、关键词搜索源码、保存源码说明的 API。配合 ICodeFileSystemEntryService 实现源码文件系统的构建、搜索与保存,并借助 Hangfire 做异步分析任务的执行。
评论加载中...