WGShare.API/WGShare.API/Controllers/Frontend/RoomController.cs

603 lines
24 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AgoraIO.Media;
using Hangfire.MemoryStorage.Database;
using Mapster;
using Masuit.Tools;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.IdentityModel.Tokens;
using SqlSugar;
using SqlSugar.Extensions;
using System.Security.Principal;
using WGShare.API.Controllers.Basic;
using WGShare.API.Helpers;
using WGShare.API.Hubs;
using WGShare.Domain.Constant;
using WGShare.Domain.DTOs.File;
using WGShare.Domain.DTOs.Room;
using WGShare.Domain.DTOs.Tenant;
using WGShare.Domain.DTOs.User;
using WGShare.Domain.Entities;
using WGShare.Domain.Enums;
using WGShare.Domain.FriendlyException;
using WGShare.Domain.GeneralModel;
using Yitter.IdGenerator;
namespace WGShare.API.Controllers.Frontend
{
/// <summary>
/// 会议室接口
/// </summary>
[ApiExplorerSettings(GroupName = "frontend")]
[Route("room")]
public class RoomController : BasicController
{
private readonly ISqlSugarClient _sqlSugar;
private readonly IConfiguration _configuration;
private readonly OssHelper _ossHelper;
private readonly AgoraHelper _agoraHelper;
private readonly IHubContext<SessionManageHub, IMessageClient> _hubContext;
private readonly ILogger<RoomController> _logger;
public RoomController(ISqlSugarClient sqlSugar,
IConfiguration configuration,
OssHelper ossHelper,
AgoraHelper agoraHelper,
IHubContext<SessionManageHub, IMessageClient> hubContext,
ILogger<RoomController> logger)
{
this._sqlSugar = sqlSugar;
this._configuration = configuration;
this._ossHelper = ossHelper;
this._agoraHelper = agoraHelper;
this._hubContext = hubContext;
this._logger = logger;
}
/// <summary>
/// 设置房间管理员
/// </summary>
/// <returns></returns>
[HttpPost("manager")]
public async Task SetRoomManager([FromBody] RoomManagerInputDTO inputDTO)
{
var user = RedisHelper.Instance.HGet<ChannelUserInfo>(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, inputDTO.RoomNum), inputDTO.UserId);
if (user == null)
{
throw Oops.Oh("用户已不在房间内!");
}
//var entities = new RoomManager
//{
// RoomId = inputDTO.RoomId,
// UserId = inputDTO.UserId
//};
user.IsRoomManager = true;
//await _sqlSugar.Storageable(entities)
// .SplitInsert(x => !x.Any())
// .ToStorage().AsInsertable.ExecuteCommandAsync();
RedisHelper.Instance.HSet(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, inputDTO.RoomNum), inputDTO.UserId, user);
await _hubContext.Clients.Group(inputDTO.RoomNum).ManagerRefresh(user);
}
/// <summary>
/// 取消房间管理员
/// </summary>
/// <returns></returns>
[HttpDelete("manager")]
public async Task DelRoomManager([FromBody] RoomManagerInputDTO inputDTO)
{
var user = RedisHelper.Instance.HGet<ChannelUserInfo>(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, inputDTO.RoomNum), inputDTO.UserId);
if (user == null)
{
throw Oops.Oh("用户已不在房间内!");
}
//await _sqlSugar.Deleteable<RoomManager>()
// .Where(x => x.RoomId == inputDTO.RoomId && x.UserId == inputDTO.UserId)
// .ExecuteCommandAsync();
user.IsRoomManager = false;
RedisHelper.Instance.HSet(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, inputDTO.RoomNum), inputDTO.UserId, user);
await _hubContext.Clients.Group(inputDTO.RoomNum).ManagerRefresh(user);
}
/// <summary>
/// 查询房间中所有用户信息
/// </summary>
/// <returns></returns>
[HttpGet("user")]
public async Task<IEnumerable<ChannelUserInfo>> GetUsers([FromQuery] string roomNum)
{
var channelUsers = RedisHelper.Instance.HVals<ChannelUserInfo>(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum));
if (channelUsers.IsNullOrEmpty())
{
return new List<ChannelUserInfo>();
}
return channelUsers;
#region
//var data = await _agoraHelper.GetChannelUserList(roomNum);
//if (data == null)
//{
// throw Oops.Oh("请求失败");
//}
//if (!data.channel_exist)
//{
// throw Oops.Oh("频道不存在");
//}
//if (data.broadcasters.IsNullOrEmpty())
//{
// return new List<UserOutputDTO>();
//}
//var accounts = data.broadcasters.ConvertAll(x => x.ToString());
//var users = await _sqlSugar.Queryable<User>()
// .Where(x => accounts.Contains(x.Account))
// .ToListAsync();
//var managerIds = await _sqlSugar.Queryable<RoomManager>()
// .InnerJoin<Room>((rm, r) => r.Id == rm.RoomId)
// .Where((rm, r) => r.RoomNum == roomNum)
// .Select((rm, r) => rm.UserId)
// .ToListAsync();
//var result = users.Adapt<List<UserOutputDTO>>();
//result.ForEach(x => x.IsManager = managerIds.Contains(x.Id));
//return result;
#endregion
}
/// <summary>
/// 查询房间中的单个用户信息
/// </summary>
/// <returns></returns>
[HttpGet("user/{uid}")]
public async Task<ChannelUserInfo> GetUser([FromQuery] string roomNum, [FromRoute] string uid)
{
var channelUser = RedisHelper.Instance.HGet<ChannelUserInfo>(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum), uid);
if (channelUser == null)
{
throw Oops.Oh("用户不在房间内!");
}
return channelUser;
}
/// <summary>
/// 检验房间是否存在
/// </summary>
/// <returns></returns>
[HttpGet("checkout"), AllowAnonymous]
public async Task<bool> ExistsRoom([FromQuery] string roomNum)
{
return await _sqlSugar.Queryable<Room>().AnyAsync(x => x.RoomNum == roomNum && x.IsDelete == false);
}
/// <summary>
/// 获取单个会议室信息
/// </summary>
/// <returns></returns>
[HttpGet("{roomNum}")]
public async Task<RoomOutputDTO> GetRoom([FromRoute] string roomNum)
{
var room = await _sqlSugar.Queryable<Room>().FirstAsync(x => x.RoomNum == roomNum && x.IsDelete == false);
if (room == null)
{
throw Oops.Oh("会议号无效");
}
return room.Adapt<RoomOutputDTO>();
}
/// <summary>
/// 获取房间rtc token
/// </summary>
/// <returns></returns>
[HttpGet("tk/rtc")]
public async Task<string> GetRTCToken([FromQuery] string roomNum)
{
//var privilegeExpiredTs = _configuration["Agora:tokenExpireTimeInSecond"].ToInt32() + Utils.getTimestamp();
return new RtcTokenBuilder2().buildTokenWithUserAccount(
_configuration["Agora:appId"],
_configuration["Agora:appSecret"],
roomNum,
"*",
RtcTokenBuilder2.Role.ROLE_PUBLISHER,
_configuration["Agora:tokenExpireTimeInSecond"].ToInt32(),
_configuration["Agora:tokenExpireTimeInSecond"].ToInt32());
}
/// <summary>
/// 邀请用户
/// </summary>
/// <returns></returns>
[HttpPost("invite")]
public async Task InviteUser([FromQuery] string roomId, [FromBody] string[] inviteeUids)
{
var room = await _sqlSugar.Queryable<Room>().FirstAsync(x => x.Id == roomId);
//var connectIds = RedisHelper.Instance.HMGet(RedisKeyConstant.SessionManage.GetOnlineUserKey(TenantId), inviteeUids);
//connectIds.RemoveWhere(x => string.IsNullOrWhiteSpace(x));
//await _hubContext.Clients.Clients(connectIds).Invitation(room.RoomNum, room.RoomName, UserName);
await _hubContext.Clients.Users(inviteeUids).Invitation(room.RoomNum, room.RoomName, UserName);
}
/// <summary>
/// 踢出房间
/// </summary>
/// <returns></returns>
[HttpGet("kickout")]
public async Task KickOut([FromQuery] string roomNum, [FromQuery] string kickUid)
{
await _hubContext.Clients.User(kickUid).ForceExitRoom(roomNum);
}
/// <summary>
/// 全部人开闭麦
/// </summary>
/// <returns></returns>
[HttpGet("mute-all")]
public async Task MuteAll([FromQuery] string roomNum, [FromQuery] bool enableMicr)
{
// 全员静音
var allUsers = RedisHelper.Instance.HGetAll<ChannelUserInfo>(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum));
if (!allUsers.Any())
{
return;
}
allUsers.ForEach(x =>
{
// 排除自己
if (x.Key != UId)
{
x.Value.EnableMicr = enableMicr;
}
});
RedisHelper.Instance.HMSet(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum), allUsers);
// 通知其他人
await _hubContext.Clients.Group(roomNum).OperAllMicr(enableMicr, UId);
}
/// <summary>
/// 单用户开闭麦
/// </summary>
/// <returns></returns>
[HttpGet("oper-micr")]
public async Task Mute([FromQuery] string roomNum, [FromQuery] bool enableMicr, [FromQuery] string uid)
{
var userInfo = RedisHelper.Instance.HGet<ChannelUserInfo>(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum), uid);
if (userInfo == null || string.IsNullOrWhiteSpace(userInfo.ConnectId))
{
_logger.LogError($"闭麦操作,用户不存在频道rediskey:{RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum)} uid" + uid);
return;
}
userInfo.EnableMicr = enableMicr;
RedisHelper.Instance.HSet(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum), uid, userInfo);
_logger.LogInformation($@"开闭麦克分,推送一次,roomNum:{roomNum},enableMicr:{enableMicr},uid:{uid}");
// 通知所有人该用户麦克风状态
await _hubContext.Clients.Group(roomNum).OperMicr(userInfo, UId);
}
/// <summary>
/// 开关闭摄像头
/// </summary>
/// <returns></returns>
[HttpGet("oper-camera")]
public async Task CloseCamera([FromQuery] string roomNum, [FromQuery] bool enableCamera, [FromQuery] string uid)
{
var userInfo = RedisHelper.Instance.HGet<ChannelUserInfo>(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum), uid);
if (userInfo == null || string.IsNullOrWhiteSpace(userInfo.ConnectId))
{
_logger.LogError($"关闭摄像头操作,用户不存在频道rediskey:{RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum)} uid" + uid);
return;
}
userInfo.EnableCamera = enableCamera;
RedisHelper.Instance.HSet(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum), uid, userInfo);
// 通知所有人该用户摄像头状态
await _hubContext.Clients.Group(roomNum).OperCamera(userInfo, UId);
}
///// <summary>
///// 同步视图
///// </summary>
///// <param name="type"></param>
///// <returns></returns>
//[HttpGet("sync-view")]
//public async Task ChangeView([FromQuery] string roomNum, [FromQuery] string type)
//{
// await _hubContext.Clients.Groups(roomNum).RefreshView(type);
//}
/// <summary>
/// 全员观看
/// </summary>
/// <returns></returns>
[HttpGet("show-user")]
public async Task<string> GetShowUser([FromQuery] string roomNum)
{
// 获取全员观看用户
var showUserId = RedisHelper.Instance.HGet(RedisKeyConstant.SessionManage.GetChannelShowUserKey(TenantId), roomNum);
if (!string.IsNullOrWhiteSpace(showUserId))
{
return showUserId;
}
// 获取全部用户
var uids = RedisHelper.Instance.HKeys(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum));
if (uids.IsNullOrEmpty())
{
throw Oops.Oh("无效会议号!");
}
return uids.FirstOrDefault();
}
/// <summary>
/// 设置全员观看
/// </summary>
/// <returns></returns>
[HttpPost("show-user")]
public async Task SetShowUser([FromQuery] string roomNum, [FromQuery] string uid, [FromQuery] string uname)
{
// 设置房间全员观看用户
RedisHelper.Instance.HSet(RedisKeyConstant.SessionManage.GetChannelShowUserKey(TenantId), roomNum, uid);
//var connectId = RedisHelper.Instance.HGet(RedisKeyConstant.SessionManage.GetOnlineUserKey(TenantId), UId);
//if (!string.IsNullOrWhiteSpace(connectId))
//{
// await _hubContext.Clients.GroupExcept(roomNum, connectId).ShowUser();
//}
await _hubContext.Clients.Group(roomNum).ShowUser(uid, uname, UId, UserName);
}
/// <summary>
/// 加入频道
/// </summary>
/// <param name="roomNum"></param>
/// <param name="enableMicr"></param>
/// <param name="enableCamera"></param>
/// <returns></returns>
[HttpGet("join")]
public async Task JoinChannel([FromQuery] string roomNum, [FromQuery] bool enableMicr = false, [FromQuery] bool enableCamera = false)
{
//var isRoomManager = await _sqlSugar.Queryable<RoomManager>()
// .InnerJoin<Room>((rm, r) => r.Id == rm.RoomId)
// .Where((rm, r) => r.RoomNum == roomNum && rm.UserId == UId)
// .AnyAsync();
var userInfo = new ChannelUserInfo
{
UID = UId,
UserName = UserName,
EnableCamera = enableCamera,
EnableMicr = enableMicr,
ConnectId = ConnectionId,
Account = Account,
ScreenShareId = ScreenShareId,
RoleId = RoleId,
RoleName = ((RoleEnums)RoleId.ToInt32()).GetDescription(),
IsRoomManager = false
};
using (var pipe = RedisHelper.Instance.StartPipe())
{
//// 进房第一人,则设置全员看ta
//var script = $@"local exists = redis.call('HLEN', KEYS[1])
// if exists == 0 then redis.call('HSET', KEYS[2], ARGV[1], ARGV[2]) end
// return exists";
//pipe.Eval(script, [RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum), RedisKeyConstant.SessionManage.GetChannelShowUserKey(TenantId)], roomNum, UId);
// 记录频道得用户信息
pipe.HSet(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum), UId, userInfo);
// 记录用户已参与频道
pipe.Set(RedisKeyConstant.SessionManage.GetUserJoinChannelKey(UId), roomNum);
pipe.EndPipe();
}
await _hubContext.Groups.AddToGroupAsync(ConnectionId, roomNum);
await _hubContext.Clients.GroupExcept(roomNum, ConnectionId).UserJoined(userInfo);
}
/// <summary>
/// 单用户离开频道
/// </summary>
/// <param name="roomNum"></param>
/// <returns></returns>
[HttpGet("leave")]
public async Task LevelChannel([FromQuery] string roomNum)
{
using (var pipe = RedisHelper.Instance.StartPipe())
{
// 判断如果有全员看ta则删除
var script = $@"local value = redis.call('HGET', KEYS[1], ARGV[1])
if value == ARGV[2] or value == ARGV[3] then
return redis.call('HDEL', KEYS[1], ARGV[1])
else return -1 end";
// 执行 eval 命令
pipe.Eval(script, [RedisKeyConstant.SessionManage.GetChannelShowUserKey(TenantId)], roomNum, UId, ScreenShareId);
// 删除频道中得该用户
pipe.HDel(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum), UId);
// 删除用户已参与频道
pipe.Del(RedisKeyConstant.SessionManage.GetUserJoinChannelKey(UId));
pipe.EndPipe();
}
await _hubContext.Clients.GroupExcept(roomNum, ConnectionId).UserLeave(UId);
await _hubContext.Groups.RemoveFromGroupAsync(ConnectionId, roomNum);
}
/// <summary>
/// 全部离开频道
/// </summary>
/// <param name="roomNum"></param>
/// <returns></returns>
[HttpGet("leave-all")]
public async Task AllLevelChannel([FromQuery] string roomNum)
{
var uids = RedisHelper.Instance.HKeys(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum)).ToList();
using (var pipe = RedisHelper.Instance.StartPipe())
{
// 删除大家都看ta
pipe.HDel(RedisKeyConstant.SessionManage.GetChannelShowUserKey(TenantId), roomNum);
// 删除频道里得用户
pipe.Del(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum));
if (!uids.IsNullOrEmpty())
{
var keys = uids.ConvertAll(uid => RedisKeyConstant.SessionManage.GetUserJoinChannelKey(uid));
pipe.Del(keys.ToArray());
}
pipe.EndPipe();
}
await _hubContext.Clients.Group(roomNum).AllLeave();
}
/// <summary>
/// 获取监控的轮询用户
/// </summary>
/// <returns></returns>
[HttpGet("polling")]
public async Task<IEnumerable<ChannelUserInfo>> GetPollingUser([FromQuery] string roomNum, [FromQuery] int count)
{
// 获取当前房间所有人id
var channelUserInfos = RedisHelper.Instance.HVals<ChannelUserInfo>(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, roomNum));
var polledUserIds = RedisHelper.Instance.SMembers(RedisKeyConstant.Data.GetPolledUserId(TenantId, roomNum));
// 排除已轮询的用户
var userInfos = channelUserInfos.Where(x => !polledUserIds.Contains(x.UID)).Take(count).ToList();
if (userInfos.Count < count)
{
// 数量不足,则从全部用户中取足
var takeCount = count - userInfos.Count < 0 ? 0 : count - userInfos.Count;
userInfos.AddRange(channelUserInfos.Take(takeCount));
// 删除记录,重新记录
RedisHelper.Instance.Del(RedisKeyConstant.Data.GetPolledUserId(TenantId, roomNum));
}
RedisHelper.Instance.SAdd(RedisKeyConstant.Data.GetPolledUserId(TenantId, roomNum), userInfos.Select(x => x.UID).ToArray());
return userInfos;
}
/// <summary>
/// 申请发言
/// </summary>
/// <returns></returns>
[HttpGet("apply-speak")]
public async Task ApplyToSpeak([FromQuery] string roomNum)
{
}
#region
/// <summary>
/// 分享上传文件
/// </summary>
/// <param name="inputDTO"></param>
/// <returns></returns>
[HttpPost("file")]
public async Task<bool> AddFile([FromBody] ShareFileInputDTO inputDTO)
{
var entity = inputDTO.Adapt<ShareFile>();
entity.Id = YitIdHelper.NextId().ToString();
entity.UserId = UId;
return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0;
}
/// <summary>
/// 删除文件
/// </summary>
/// <returns></returns>
[HttpDelete("file")]
public async Task<bool> DeleteFile([FromBody] List<string> ids)
{
return await _sqlSugar.Updateable<ShareFile>()
.SetColumns(x => x.IsDelete == true)
.Where(x => ids.Contains(x.Id))
.ExecuteCommandHasChangeAsync();
}
/// <summary>
/// 获取分享文件列表
/// </summary>
/// <param name="roomId">房间Id</param>
/// <param name="pagedBaseDto"></param>
/// <returns></returns>
[HttpGet("file")]
public async Task<PagedResult<ShareFileOutputDTO>> GetFilesList([FromQuery] string roomId, [FromQuery] string? keyword, [FromQuery] PagedBaseDto pagedBaseDto)
{
RefAsync<int> total = 0;
var list = await _sqlSugar.Queryable<ShareFile>()
.LeftJoin<User>((sf, u) => sf.UserId == u.Id)
.Where((sf, u) => sf.IsDelete == false && sf.RoomId == roomId)
.WhereIF(!string.IsNullOrWhiteSpace(keyword), (sf, u) => sf.FileName.Contains(keyword))
.Select((sf, u) => new ShareFileOutputDTO
{
Id = sf.Id,
UserId = u.Id,
UserName = u.UserName,
FileName = sf.FileName,
FileUrl = sf.FileUrl,
RoomId = sf.RoomId,
Size = sf.Size,
ModifyTime = sf.ModifyTime,
DownloadCount = sf.DownloadCount,
}).ToPageListAsync(pagedBaseDto.PageIndex, pagedBaseDto.PageSize, total);
return PagedResult<ShareFileOutputDTO>.Create(list, total.Value);
}
/// <summary>
/// 获取文件上传url
/// </summary>
/// <returns></returns>
[HttpGet("up-fileurl")]
public async Task<IActionResult> GetUploadUrl([FromQuery] string roomNum, [FromQuery] string fileSuffix)
{
return Ok(_ossHelper.GetUploadUrl($@"share_file/{TenantId}/{roomNum}", Guid.NewGuid().ToString("N") + "." + fileSuffix));
}
/// <summary>
/// 获取文件下载地址
/// </summary>
/// <param name="fileUrl"></param>
/// <param name="fileId">文件Id</param>
/// <returns></returns>
[HttpGet("file-dw-url")]
public async Task<string> GetDownloadUrl([FromQuery] string fileUrl, [FromQuery] string fileId)
{
await _sqlSugar.Updateable<ShareFile>()
.SetColumns(x => x.DownloadCount == x.DownloadCount + 1)
.Where(x => x.Id == fileId).ExecuteCommandAsync();
return _ossHelper.GetAccessFileUrl(fileUrl);
}
#endregion
}
}