201 lines
6.8 KiB
C#
201 lines
6.8 KiB
C#
using AgoraIO.Media;
|
||
using AgoraIO.Rtm;
|
||
using Mapster;
|
||
using Masuit.Tools;
|
||
using Microsoft.AspNetCore.Authorization;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.AspNetCore.Mvc.Filters;
|
||
using SharpCompress;
|
||
using SqlSugar;
|
||
using System.Collections.Concurrent;
|
||
using System.Diagnostics.CodeAnalysis;
|
||
using System.Net.Http;
|
||
using WGShare.API.Controllers.Basic;
|
||
using WGShare.API.Helpers;
|
||
using WGShare.Domain.AgoraApiResult;
|
||
using WGShare.Domain.Constant;
|
||
using WGShare.Domain.DTOs.AgoraCallback;
|
||
using WGShare.Domain.DTOs.Room;
|
||
using WGShare.Domain.Entities;
|
||
using WGShare.Domain.FriendlyException;
|
||
using WGShare.Domain.GeneralModel;
|
||
using Yitter.IdGenerator;
|
||
|
||
namespace WGShare.API.Controllers.Frontend
|
||
{
|
||
/// <summary>
|
||
/// Agora接口
|
||
/// </summary>
|
||
[ApiExplorerSettings(GroupName = "frontend")]
|
||
[Route("agora-cb"), AllowAnonymous]
|
||
public class AgoraCallbackController : BasicController
|
||
{
|
||
private readonly ILogger<AgoraCallbackController> _logger;
|
||
private readonly AgoraHelper _agoraHelper;
|
||
private readonly IConfiguration _configuration;
|
||
private readonly ISqlSugarClient _sqlSugarClient;
|
||
private readonly IHostEnvironment _hostEnvironment;
|
||
|
||
public AgoraCallbackController(
|
||
ILogger<AgoraCallbackController> logger,
|
||
AgoraHelper agoraHelper,
|
||
IConfiguration configuration,
|
||
ISqlSugarClient sqlSugarClient,
|
||
IHostEnvironment hostEnvironment)
|
||
{
|
||
this._logger = logger;
|
||
this._agoraHelper = agoraHelper;
|
||
this._configuration = configuration;
|
||
this._sqlSugarClient = sqlSugarClient;
|
||
this._hostEnvironment = hostEnvironment;
|
||
}
|
||
|
||
|
||
[HttpPost("event")]
|
||
public async Task Event()
|
||
{
|
||
using var reader = new StreamReader(Request.Body);
|
||
var bodyString = await reader.ReadToEndAsync();
|
||
|
||
//_logger.LogInformation($"Agora回调内容:{bodyString}");
|
||
|
||
// 校验请求体是否为空
|
||
if (string.IsNullOrWhiteSpace(bodyString))
|
||
{
|
||
await ExceptionNotice.SendAsync(new ArgumentNullException("Agora-Signature-V2 声网请求体为空"), "声网回调异常");
|
||
return;
|
||
}
|
||
|
||
var body = bodyString.FromJson<EventBody>();
|
||
if (body == null)
|
||
{
|
||
await ExceptionNotice.SendAsync(new InvalidDataException("Agora-Signature-V2 声网请求体反序列化失败"), "声网回调异常");
|
||
return;
|
||
}
|
||
|
||
// 声网健康检查回调,直接返回
|
||
if (body.payload.channelName == "test_webhook")
|
||
{
|
||
_logger.LogDebug($"测试回调,测试信息:{bodyString}");
|
||
return;
|
||
}
|
||
|
||
// 验证签名
|
||
var sig = Request.Headers["Agora-Signature-V2"].ToString();
|
||
if (!await _agoraHelper.CheckSignatureAsync(sig, bodyString))
|
||
{
|
||
_logger.LogWarning($"Agora回调签名验证失败,声网签名:{sig}");
|
||
return;
|
||
}
|
||
|
||
switch (body.eventType)
|
||
{
|
||
case Domain.Enums.EventType.channel_create:
|
||
await CreateChannel(body);
|
||
break;
|
||
case Domain.Enums.EventType.channel_destroy:
|
||
await DestroyChannel(body);
|
||
break;
|
||
case Domain.Enums.EventType.broadcaster_join_channel:
|
||
case Domain.Enums.EventType.audience_join_channel:
|
||
JoinChannelEvent(bodyString, body);
|
||
break;
|
||
case Domain.Enums.EventType.broadcaster_leave_channel:
|
||
case Domain.Enums.EventType.audience_leave_channel:
|
||
LeaveChannelEvent(bodyString);
|
||
break;
|
||
case Domain.Enums.EventType.client_role_change_to_broadcaster:
|
||
break;
|
||
case Domain.Enums.EventType.client_role_change__to_audience:
|
||
break;
|
||
default:
|
||
await ExceptionNotice.SendAsync(new Exception("声网事件未知类型"), "声网回调异常");
|
||
break;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加入频道
|
||
/// </summary>
|
||
[NonAction]
|
||
private void JoinChannelEvent(string bodyString, EventBody eventBody)
|
||
{
|
||
_logger.LogDebug($"Agora回调内容 加入频道:{bodyString}");
|
||
RedisHelper.Instance.LPush(RedisKeyConstant.PubSub.MeetingRecord, bodyString);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 离开频道
|
||
/// </summary>
|
||
[NonAction]
|
||
private void LeaveChannelEvent(string bodyString)
|
||
{
|
||
_logger.LogDebug($"Agora回调内容 离开频道:{bodyString}");
|
||
|
||
// 离会记录
|
||
RedisHelper.Instance.LPush(RedisKeyConstant.PubSub.MeetingRecord, bodyString);
|
||
}
|
||
|
||
|
||
static List<string> excludeChannel = new();
|
||
static ConcurrentDictionary<string, int> existsChannel = new();
|
||
/// <summary>
|
||
/// 创建频道
|
||
/// </summary>
|
||
[NonAction]
|
||
private async Task CreateChannel(EventBody eventBody)
|
||
{
|
||
if (_hostEnvironment.IsDevelopment())
|
||
{
|
||
_logger.LogDebug($"测试环境不通知创建频道");
|
||
return;
|
||
}
|
||
if (excludeChannel.IsNullOrEmpty())
|
||
{
|
||
// 缓存测试区域房间
|
||
excludeChannel = await _sqlSugarClient.Queryable<Room>().Where(x => x.TenantId == "559167236182085")
|
||
.Select(x => x.RoomNum).ToListAsync();
|
||
}
|
||
|
||
if (excludeChannel.Any(x => x == eventBody.payload.channelName))
|
||
{
|
||
_logger.LogDebug($"正式环境的测试区域不通知创建频道");
|
||
return;
|
||
}
|
||
|
||
if (existsChannel.TryAdd(eventBody.payload.channelName, 0))
|
||
{
|
||
var roomInfo = await _sqlSugarClient.Queryable<Room>()
|
||
.LeftJoin<Tenant>((r, t) => r.TenantId == t.Id)
|
||
.Where((r, t) => r.RoomNum == eventBody.payload.channelName)
|
||
.Select((r, t) => new Room
|
||
{
|
||
Id = r.Id.SelectAll(),
|
||
TenantName = t.TenantName
|
||
})
|
||
.FirstAsync();
|
||
ExceptionNotice.JoinAsync(eventBody, roomInfo);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 销毁频道
|
||
/// </summary>
|
||
[NonAction]
|
||
private async Task DestroyChannel(EventBody eventBody)
|
||
{
|
||
if (_hostEnvironment.IsDevelopment())
|
||
return;
|
||
existsChannel.TryRemove(eventBody.payload.channelName, out _);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
}
|