using JetBrains.Annotations;

namespace Dpz.Core.WebApi.Controllers;

/// <summary>
/// 弹幕管理
/// </summary>
[ApiController, Route("api/[controller]"), Authorize(Policy = "System")]
public class DanmakuController(IBarrageService barrageService, ILogger<DanmakuController> logger)
    : ControllerBase
{
    /// <summary>
    /// 获取弹幕
    /// </summary>
    /// <param name="query"></param>
    /// <returns></returns>
    [HttpGet]
    [ProducesResponseType<List<VmBarrage>>(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> GetDanmaku([FromQuery] DanmakuQueryDto query)
    {
        var list = await barrageService.GetPageAsync(
            query.PageIndex,
            query.PageSize,
            query.Text,
            query.Group
        );
        list.AddPaginationMetadata(Response.Headers);
        return Ok(list);
    }

    /// <summary>
    /// 获取弹幕分组
    /// </summary>
    /// <returns></returns>
    [HttpGet("group")]
    [ProducesResponseType<List<string>>(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> GetGroups()
    {
        var groups = await barrageService.GetGroupsAsync();
        return Ok(groups);
    }

    /// <summary>
    /// 删除弹幕
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpDelete]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> Delete([FromBody] [Required] string[] id)
    {
        await barrageService.DeleteAsync(id);
        return NoContent();
    }

    /// <summary>
    /// 导入A站弹幕
    /// </summary>
    /// <returns></returns>
    [HttpPost("import/acfun")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> ImportAcFun([FromForm] ImportDanmaku import)
    {
        var extensionName = Path.GetExtension(import.File.FileName);
        if (extensionName != ".json")
        {
            return NoContent();
        }

        List<AcFunDanmaku> danmakus;
        try
        {
            danmakus =
                await JsonSerializer.DeserializeAsync<List<AcFunDanmaku>>(
                    import.File.OpenReadStream(),
                    new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
                ) ?? [];
        }
        catch (Exception e)
        {
            logger.LogError(e, "import acfun danmaku fail");
            return BadRequest(e.Message);
        }

        if (danmakus.Count == 0)
        {
            return BadRequest("no danmaku");
        }

        var source = danmakus
            .Select(x => new VmBarrage
            {
                Group = import.Group,
                Color = x.Color.ToString(),
                Position = x.Mode == 5 ? 1 : 0,
                SendTime = x.GetCreateTime(),
                Size = x.Size,
                Text = x.Body,
                Time = x.Position / (decimal)1e3,
            })
            .ToArray();
        await barrageService.ImportAsync(source);
        return NoContent();
    }

    /// <summary>
    /// 导入B站弹幕
    /// </summary>
    /// <returns></returns>
    [HttpPost("import/bilibili")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> ImportBilibili([FromForm] ImportDanmaku import)
    {
        var extensionName = Path.GetExtension(import.File.FileName);
        if (extensionName != ".xml")
        {
            return NoContent();
        }

        try
        {
            var source = await XElement.LoadAsync(
                import.File.OpenReadStream(),
                LoadOptions.None,
                CancellationToken.None
            );
            var bilibiliDanmakus = (
                from x in source.Elements("d")
                let p = x.Attribute("p")
                where p != null && !string.IsNullOrEmpty(p.Value)
                let props = p.Value.Split(",")
                where props.Length >= 5
                let type = props[1] switch
                {
                    "1" => DanmakuType.Rolling,
                    "4" => DanmakuType.Bottom,
                    "5" => DanmakuType.Top,
                    _ => DanmakuType.Rolling,
                }
                select new VmBarrage
                {
                    Group = import.Group,
                    Color = props[3],
                    Position = (int)type,
                    SendTime = long.Parse(props[4]).ToDateTime(),
                    Size = int.Parse(props[2]),
                    Text = x.Value,
                    Time = decimal.Parse(props[0]),
                }
            ).ToArray();
            await barrageService.ImportAsync(bilibiliDanmakus);
            return NoContent();
        }
        catch (Exception e)
        {
            logger.LogError(e, "import bilibili danmaku fail");
            return BadRequest(e.Message);
        }
    }

    /// <summary>
    /// AcFun弹幕
    /// </summary>
    [UsedImplicitly]
    private record AcFunDanmaku(
        int Color,
        int Mode,
        long CreateTime,
        string? Body,
        int Position,
        int Size
    )
    {
        public DateTime GetCreateTime()
        {
            var time = CreateTime / 1000D;
            var utcTime = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).AddSeconds(time);
            return utcTime.LocalDateTime;
        }
    }

    private enum DanmakuType
    {
        Rolling = 0,
        Top = 1,
        Bottom = 2,
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一个ASP.NET Core Web API控制器,用于管理弹幕系统。让我详细解释这个代码的功能和结构:

控制器概述

DanmakuController 是一个弹幕管理控制器,具有以下特点:

  • 使用主构造函数注入 IBarrageServiceILogger
  • 需要 "System" 策略授权
  • API路由为 api/Danmaku

主要功能

1. 获取弹幕 (GET /api/Danmaku)

[HttpGet]
public async Task<IActionResult> GetDanmaku([FromQuery] DanmakuQueryDto query)
  • 支持分页查询弹幕
  • 可按文本内容和分组筛选
  • 返回弹幕列表并添加分页元数据到响应头

2. 获取弹幕分组 (GET /api/Danmaku/group)

[HttpGet("group")]
public async Task<IActionResult> GetGroups()
  • 返回所有可用的弹幕分组列表

3. 删除弹幕 (DELETE /api/Danmaku)

[HttpDelete]
public async Task<IActionResult> Delete([FromBody] [Required] string[] id)
  • 批量删除指定ID的弹幕
  • 接收ID数组作为请求体

4. 导入A站弹幕 (POST /api/Danmaku/import/acfun)

[HttpPost("import/acfun")]
public async Task<IActionResult> ImportAcFun([FromForm] ImportDanmaku import)
  • 导入AcFun平台的弹幕文件(JSON格式)
  • 解析AcFun特定的弹幕数据结构
  • 转换为通用的 VmBarrage 格式

5. 导入B站弹幕 (POST /api/Danmaku/import/bilibili)

[HttpPost("import/bilibili")]
public async Task<IActionResult> ImportBilibili([FromForm] ImportDanmaku import)
  • 导入哔哩哔哩平台的弹幕文件(XML格式)
  • 解析B站XML弹幕格式
  • 支持不同弹幕类型(滚动、顶部、底部)

内部类型定义

AcFunDanmaku Record

private record AcFunDanmaku(int Color, int Mode, long CreateTime, string? Body, int Position, int Size)
  • 表示AcFun弹幕的数据结构
  • 包含 GetCreateTime() 方法将时间戳转换为本地时间

DanmakuType 枚举

private enum DanmakuType { Rolling = 0, Top = 1, Bottom = 2 }
  • 定义弹幕显示位置类型

关键特性

  1. 错误处理: 所有导入操作都包含异常处理和日志记录
  2. 文件验证: 检查上传文件的扩展名
  3. 数据转换: 将不同平台的弹幕格式转换为统一的内部格式
  4. 异步操作: 所有数据库操作都是异步的
  5. RESTful设计: 遵循REST API设计规范
  6. 文档注释: 完整的XML文档注释用于API文档生成

这个控制器为弹幕系统提供了完整的CRUD操作以及多平台弹幕导入功能,是一个功能完整的弹幕管理API。

评论加载中...