This commit is contained in:
youngq 2024-07-10 16:33:59 +08:00
parent 50ee6aa7a2
commit 3f05dbefc0
31 changed files with 1344 additions and 51 deletions

View File

@ -0,0 +1,56 @@
using Hangfire;
using Hangfire.Server;
using Masuit.Tools;
using SqlSugar;
using WGShare.API.Helpers;
using WGShare.Domain.Entities;
namespace WGShare.API.BackgroudServices
{
public class OssCleanWorker : BackgroundService
{
private readonly ILogger<OssCleanWorker> _logger;
private readonly ISqlSugarClient _sugarClient;
private readonly OssHelper _ossHelper;
public OssCleanWorker(ILogger<OssCleanWorker> logger, ISqlSugarClient sugarClient, OssHelper ossHelper)
{
_logger = logger;
this._sugarClient = sugarClient;
this._ossHelper = ossHelper;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
RecurringJob.AddOrUpdate("DeleteOssFile", () => DeleteOssFile(), Cron.Daily());
}
public async Task DeleteOssFile()
{
await Console.Out.WriteLineAsync($@"开始清除过期文件,当前时间:{DateTime.Now}");
// 查找已删除未文件清理并过期180天的文件
var deleteFiles = await _sugarClient.Queryable<ShareFile>()
.Where(x => x.IsDelete == true && x.IsFileClean == false && SqlFunc.DateDiff(DateType.Day, x.ModifyTime, SqlFunc.GetDate()) > 180)
.ToListAsync();
if (deleteFiles.IsNullOrEmpty())
{
await Console.Out.WriteLineAsync($@"当前无可清除文件,退出本次操作,当前时间:{DateTime.Now}");
return;
}
await Console.Out.WriteLineAsync($@"本次清除文件数量:{deleteFiles.Count}");
await Console.Out.WriteLineAsync($@"本次清除文件路径:{string.Join(',', deleteFiles.Select(x => x.FileUrl))}");
if (_ossHelper.DeleteObjects(deleteFiles.Select(x => x.FileUrl).ToArray()))
{
await _sugarClient.Updateable<ShareFile>()
.SetColumns(x => x.IsFileClean == true)
.Where(x => deleteFiles.Select(a => a.Id).Contains(x.Id))
.ExecuteCommandAsync();
}
await Console.Out.WriteLineAsync($@"本次清除操作结束,当前时间:{DateTime.Now}");
}
}
}

View File

@ -18,11 +18,14 @@ namespace WGShare.API.Controllers
{ {
private readonly ISqlSugarClient _sqlSugar; private readonly ISqlSugarClient _sqlSugar;
private readonly JwtHelper _jwtHelper; private readonly JwtHelper _jwtHelper;
private readonly IConfiguration _configuration;
public AuthController(ISqlSugarClient sqlSugar, JwtHelper jwtHelper) public AuthController(ISqlSugarClient sqlSugar, JwtHelper jwtHelper,
IConfiguration configuration)
{ {
_sqlSugar = sqlSugar; _sqlSugar = sqlSugar;
_jwtHelper = jwtHelper; _jwtHelper = jwtHelper;
this._configuration = configuration;
} }
/// <summary> /// <summary>
@ -71,6 +74,8 @@ namespace WGShare.API.Controllers
btnAutn.Add(new Claim("perm", perms.Sum(x => x.PermValue).ToString())); btnAutn.Add(new Claim("perm", perms.Sum(x => x.PermValue).ToString()));
btnAutn.Add(new Claim("role", user.RoleId)); btnAutn.Add(new Claim("role", user.RoleId));
btnAutn.Add(new Claim("tenant", user.TenantId)); btnAutn.Add(new Claim("tenant", user.TenantId));
btnAutn.Add(new Claim("account", user.Account));
btnAutn.Add(new Claim("uname", user.UserName));
return Ok(new return Ok(new
{ {
@ -78,7 +83,8 @@ namespace WGShare.API.Controllers
token = _jwtHelper.CreateToken(user.Id, btnAutn), token = _jwtHelper.CreateToken(user.Id, btnAutn),
roleId = user.RoleId, roleId = user.RoleId,
userName = user.UserName, userName = user.UserName,
tenantName = tenant.TenantName tenantName = tenant.TenantName,
expire= _configuration["Jwt:Expires"].ToInt32()
}); });
} }

View File

@ -22,6 +22,33 @@ namespace WGShare.API.Controllers.Basic
} }
} }
public string Account
{
get
{
var account = HttpContext.User.Claims.FirstOrDefault(x => x.Type == "account").Value;
if (string.IsNullOrWhiteSpace(account))
{
throw Oops.Oh("用户信息有误,请重新登录");
}
return account;
}
}
public string UserName
{
get
{
var account = HttpContext.User.Claims.FirstOrDefault(x => x.Type == "uname").Value;
if (string.IsNullOrWhiteSpace(account))
{
throw Oops.Oh("用户信息有误,请重新登录");
}
return account;
}
}
public string RoleId public string RoleId
{ {
get get

View File

@ -1,7 +1,14 @@
using Mapster; using AgoraIO.Media;
using AgoraIO.Rtm;
using Mapster;
using Masuit.Tools;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SqlSugar; using SqlSugar;
using System.Net.Http;
using WGShare.API.Controllers.Basic; using WGShare.API.Controllers.Basic;
using WGShare.API.Helpers;
using WGShare.Domain.AgoraApiResult;
using WGShare.Domain.Constant;
using WGShare.Domain.DTOs.Room; using WGShare.Domain.DTOs.Room;
using WGShare.Domain.Entities; using WGShare.Domain.Entities;
using WGShare.Domain.FriendlyException; using WGShare.Domain.FriendlyException;
@ -18,21 +25,33 @@ namespace WGShare.API.Controllers.Frontend
public class HomeController : BasicController public class HomeController : BasicController
{ {
private readonly ISqlSugarClient _sqlSugar; private readonly ISqlSugarClient _sqlSugar;
private readonly IConfiguration _configuration;
private readonly IHttpClientFactory _httpClientFactory;
private readonly AgoraHelper _agoraHelper;
private readonly ILogger<HomeController> _logger;
public HomeController(ISqlSugarClient sqlSugar) public HomeController(
ISqlSugarClient sqlSugar,
IConfiguration configuration,
IHttpClientFactory httpClientFactory,
AgoraHelper agoraHelper,
ILogger<HomeController> logger)
{ {
_sqlSugar = sqlSugar; _sqlSugar = sqlSugar;
this._configuration = configuration;
this._httpClientFactory = httpClientFactory;
this._agoraHelper = agoraHelper;
this._logger = logger;
} }
/// <summary> /// <summary>
/// 获取会议室列表 /// 获取会议室列表
/// </summary> /// </summary>
/// <param name="dto"></param> /// <param name="dto"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("room")] [HttpGet("room")]
public async Task<PagedResult<RoomOutputDTO>> GetRooms(PagedBaseDto dto) public async Task<PagedResult<RoomOutputDTO>> GetRooms([FromQuery] PagedBaseDto dto)
{ {
RefAsync<int> total = 0; RefAsync<int> total = 0;
@ -40,7 +59,19 @@ namespace WGShare.API.Controllers.Frontend
.Where(x => x.TenantId == TenantId && x.IsDelete == false) .Where(x => x.TenantId == TenantId && x.IsDelete == false)
.ToPageListAsync(dto.PageIndex, dto.PageSize, total); .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
return PagedResult<RoomOutputDTO>.Create(list.Adapt<List<RoomOutputDTO>>(), total.Value); var result = list.Adapt<List<RoomOutputDTO>>();
if (!result.IsNullOrEmpty())
{
// 从Redis获取缓存在线人数 拼装数据
var userCounts = RedisHelper.Instance.HMGet<int>(RedisKeyConstant.SessionManage.GetChannelUserCountKey(TenantId),
result.Select(x => x.RoomNum).ToArray());
for (int i = 0; i < userCounts.Length; i++)
{
result[i].OnlineUserCount = userCounts[i];
}
}
return PagedResult<RoomOutputDTO>.Create(result, total.Value);
} }
/// <summary> /// <summary>
@ -51,6 +82,11 @@ namespace WGShare.API.Controllers.Frontend
[HttpPost("room")] [HttpPost("room")]
public async Task<bool> CreateRoom([FromBody] RoomInputDTO inputDTO) public async Task<bool> CreateRoom([FromBody] RoomInputDTO inputDTO)
{ {
if (inputDTO.RoomNum.Length != 8 || !int.TryParse(inputDTO.RoomNum, out _))
{
throw Oops.Oh("会议号必须为8位数字");
}
var entity = inputDTO.Adapt<Room>(); var entity = inputDTO.Adapt<Room>();
entity.Id = YitIdHelper.NextId().ToString(); entity.Id = YitIdHelper.NextId().ToString();
entity.TenantId = TenantId; entity.TenantId = TenantId;
@ -63,5 +99,19 @@ namespace WGShare.API.Controllers.Frontend
return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0; return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0;
} }
/// <summary>
/// 获取 rtm token
/// </summary>
/// <returns></returns>
[HttpGet("tk/rtm"),Obsolete]
public async Task<string> GetRTMToken()
{
uint privilegeExpiredTs = (uint)_configuration["tokenExpireTimeInSecond"].ToInt32() + (uint)Utils.getTimestamp();
return RtmTokenBuilder.buildToken(_configuration["Agora:appId"],
_configuration["Agora:appSecret"],
UId,
privilegeExpiredTs);
}
} }
} }

View File

@ -1,10 +1,15 @@
using Mapster; using AgoraIO.Media;
using Mapster;
using Masuit.Tools; using Masuit.Tools;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.IdentityModel.Tokens;
using SqlSugar; using SqlSugar;
using System.CodeDom;
using WGShare.API.Controllers.Basic; 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.File;
using WGShare.Domain.DTOs.Room; using WGShare.Domain.DTOs.Room;
using WGShare.Domain.DTOs.User; using WGShare.Domain.DTOs.User;
@ -12,7 +17,6 @@ using WGShare.Domain.Entities;
using WGShare.Domain.FriendlyException; using WGShare.Domain.FriendlyException;
using WGShare.Domain.GeneralModel; using WGShare.Domain.GeneralModel;
using Yitter.IdGenerator; using Yitter.IdGenerator;
using ZstdSharp.Unsafe;
namespace WGShare.API.Controllers.Frontend namespace WGShare.API.Controllers.Frontend
{ {
@ -24,24 +28,36 @@ namespace WGShare.API.Controllers.Frontend
public class RoomController : BasicController public class RoomController : BasicController
{ {
private readonly ISqlSugarClient _sqlSugar; private readonly ISqlSugarClient _sqlSugar;
private readonly IConfiguration _configuration;
private readonly OssHelper _ossHelper;
private readonly AgoraHelper _agoraHelper;
private readonly IHubContext<SessionManageHub, IMessageClient> _hubContext;
public RoomController(ISqlSugarClient sqlSugar) public RoomController(ISqlSugarClient sqlSugar,
IConfiguration configuration,
OssHelper ossHelper,
AgoraHelper agoraHelper,
IHubContext<SessionManageHub, IMessageClient> hubContext)
{ {
this._sqlSugar = sqlSugar; this._sqlSugar = sqlSugar;
this._configuration = configuration;
this._ossHelper = ossHelper;
this._agoraHelper = agoraHelper;
this._hubContext = hubContext;
} }
/// <summary> ///// <summary>
/// 获取会议室管理员 ///// 获取会议室管理员
/// </summary> ///// </summary>
/// <returns></returns> ///// <returns></returns>
[HttpGet("manager")] //[HttpGet("manager")]
public async Task<List<string>> GetRoomManager([FromQuery] string roomId) //public async Task<List<string>> GetRoomManager([FromQuery] string roomId)
{ //{
return await _sqlSugar.Queryable<RoomManager>() // return await _sqlSugar.Queryable<RoomManager>()
.Where(x => x.RoomId == roomId) // .Where(x => x.RoomId == roomId)
.Select(x => x.UserId) // .Select(x => x.UserId)
.ToListAsync(); // .ToListAsync();
} //}
/// <summary> /// <summary>
/// 设置房间管理员 /// 设置房间管理员
@ -59,24 +75,51 @@ namespace WGShare.API.Controllers.Frontend
return await _sqlSugar.Insertable(entities).ExecuteCommandAsync() > 0; return await _sqlSugar.Insertable(entities).ExecuteCommandAsync() > 0;
} }
/// <summary>
/// 取消房间管理员
/// </summary>
/// <returns></returns>
[HttpDelete("manager")]
public async Task<bool> DelRoomManager([FromQuery] string roomId, [FromBody] List<string> userIds)
{
return await _sqlSugar.Deleteable<RoomManager>()
.Where(x => x.RoomId == roomId && userIds.Contains(x.UserId))
.ExecuteCommandAsync() > 0;
}
/// <summary> /// <summary>
/// 查询用户信息 /// 查询用户信息
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet("user")] [HttpGet("user")]
public async Task<List<UserOutputDTO>> GetUsers([FromQuery] string uidStr) public async Task<List<UserOutputDTO>> GetUsers([FromQuery] string roomNum)
{ {
var uid = uidStr.Split(',', StringSplitOptions.RemoveEmptyEntries); var data = await _agoraHelper.GetChannelUserList(roomNum);
if (uid.IsNullOrEmpty()) if (data == null)
{ {
throw Oops.Oh("请输入需要查询的用户id,多id请用英文逗号,分割"); throw Oops.Oh("请求失败");
}
if (!data.channel_exist)
{
throw Oops.Oh("频道不存在");
} }
var users = await _sqlSugar.Queryable<User>() var users = await _sqlSugar.Queryable<User>()
.Where(x => uid.Contains(x.Id)) .Where(x => data.users.Contains(x.Account))
.ToListAsync(); .ToListAsync();
return users.Adapt<List<UserOutputDTO>>(); 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;
} }
/// <summary> /// <summary>
@ -104,6 +147,53 @@ namespace WGShare.API.Controllers.Frontend
return room.Adapt<RoomOutputDTO>(); 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,
Account,
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);
}
/// <summary>
/// 踢出房间
/// </summary>
/// <returns></returns>
[HttpGet("kickout")]
public async Task KickOut([FromQuery] string roomNum, [FromQuery] string kickUid)
{
var connectId = RedisHelper.Instance.HGet(RedisKeyConstant.SessionManage.GetOnlineUserKey(TenantId), kickUid);
await _hubContext.Clients.Client(connectId).ForceExitRoom(roomNum);
}
#region
/// <summary> /// <summary>
/// 分享上传文件 /// 分享上传文件
/// </summary> /// </summary>
@ -135,14 +225,17 @@ namespace WGShare.API.Controllers.Frontend
/// <summary> /// <summary>
/// 获取分享文件列表 /// 获取分享文件列表
/// </summary> /// </summary>
/// <param name="roomId">房间Id</param>
/// <param name="pagedBaseDto"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("file")] [HttpGet("file")]
public async Task<PagedResult<ShareFileOutputDTO>> GetFilesList([FromQuery] string roomId, [FromBody] PagedBaseDto pagedBaseDto) public async Task<PagedResult<ShareFileOutputDTO>> GetFilesList([FromQuery] string roomId, [FromQuery] string? keyword, [FromQuery] PagedBaseDto pagedBaseDto)
{ {
RefAsync<int> total = 0; RefAsync<int> total = 0;
var list = await _sqlSugar.Queryable<ShareFile>() var list = await _sqlSugar.Queryable<ShareFile>()
.LeftJoin<User>((sf, u) => sf.UserId == u.Id) .LeftJoin<User>((sf, u) => sf.UserId == u.Id)
.Where(x => x.IsDelete == false && x.RoomId == roomId) .Where((sf, u) => sf.IsDelete == false && sf.RoomId == roomId)
.WhereIF(!string.IsNullOrWhiteSpace(keyword), (sf, u) => sf.FileName.Contains(keyword))
.Select((sf, u) => new ShareFileOutputDTO .Select((sf, u) => new ShareFileOutputDTO
{ {
Id = sf.Id, Id = sf.Id,
@ -150,7 +243,7 @@ namespace WGShare.API.Controllers.Frontend
UserName = u.UserName, UserName = u.UserName,
FileName = sf.FileName, FileName = sf.FileName,
FileUrl = sf.FileUrl, FileUrl = sf.FileUrl,
RoomId = u.Id, RoomId = sf.RoomId,
Size = sf.Size, Size = sf.Size,
ModifyTime = sf.ModifyTime, ModifyTime = sf.ModifyTime,
DownloadCount = sf.DownloadCount, DownloadCount = sf.DownloadCount,
@ -160,5 +253,32 @@ namespace WGShare.API.Controllers.Frontend
} }
/// <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
} }
} }

View File

@ -22,17 +22,32 @@ namespace WGShare.API.Controllers.Frontend
} }
/// <summary>
/// 获取用户列表
/// </summary>
/// <param name="searchKeywod">用户名 或 账号</param>
/// <param name="pagedBaseDto">翻页信息</param>
/// <returns></returns>
[HttpGet("list")] [HttpGet("list")]
public async Task<List<UserOutputDTO>> GetUserList([FromQuery] string? searchKeywod, [FromQuery] PagedBaseDto pagedBaseDto) public async Task<PagedResult<UserOutputDTO>> GetUserList([FromQuery] string? searchKeywod, [FromQuery] PagedBaseDto pagedBaseDto)
{ {
RefAsync<int> total = 0; RefAsync<int> total = 0;
var list = await _sqlSugar.Queryable<User>() var list = await _sqlSugar.Queryable<User>()
.InnerJoin<Role>((u, r) => u.RoleId == r.Id) .InnerJoin<Role>((u, r) => u.RoleId == r.Id)
.WhereIF(!string.IsNullOrWhiteSpace(searchKeywod), u => u.UserName.Contains(searchKeywod) || u.Account.Contains(searchKeywod)) .Where((u, r) => u.IsDelete == false)
.WhereIF(!string.IsNullOrWhiteSpace(searchKeywod), (u, r) => u.UserName.Contains(searchKeywod) || u.Account.Contains(searchKeywod))
.Select((u, r) => new UserOutputDTO
{
Id = u.Id,
UserName = u.UserName,
Account = u.Account,
RoleId = r.Id,
RoleName = r.RoleName
})
.ToPageListAsync(pagedBaseDto.PageIndex, pagedBaseDto.PageSize, total); .ToPageListAsync(pagedBaseDto.PageIndex, pagedBaseDto.PageSize, total);
return list.Adapt<List<UserOutputDTO>>(); return PagedResult<UserOutputDTO>.Create(list, total.Value);
} }
@ -42,7 +57,7 @@ namespace WGShare.API.Controllers.Frontend
/// <param name="inputDTO"></param> /// <param name="inputDTO"></param>
/// <returns></returns> /// <returns></returns>
[HttpPost] [HttpPost]
public async Task<bool> AddUser([FromQuery] UserInputDTO inputDTO) public async Task<bool> AddUser([FromBody] UserInputDTO inputDTO)
{ {
var user = inputDTO.Adapt<User>(); var user = inputDTO.Adapt<User>();
user.Id = YitIdHelper.NextId().ToString(); user.Id = YitIdHelper.NextId().ToString();

View File

@ -0,0 +1,132 @@
using Masuit.Tools;
using System.Drawing.Printing;
using System.Net.Http;
using System.Text;
using WGShare.Domain.AgoraApiResult;
using WGShare.Domain.FriendlyException;
namespace WGShare.API.Helpers
{
public class AgoraHelper
{
public class Constant
{
/// <summary>
/// Redis 键hash ,每个频道用户数量
/// </summary>
public const string REDIS_CHANNEL_USERCOUNT = "channelUserCount";
}
private readonly IConfiguration _configuration;
public AgoraHelper(IConfiguration configuration)
{
this._configuration = configuration;
}
/// <summary>
/// 获取完整的声网API请求路径
/// </summary>
/// <param name="apiSuffixPath">api .com 后的路径后缀</param>
/// <returns></returns>
public string GetFullApiUrl(string apiSuffixPath)
{
var apiPrefix = _configuration["agora:apiPrefix"].Trim('/');
return apiPrefix + (apiSuffixPath.StartsWith('/') ? apiSuffixPath : ('/' + apiSuffixPath));
}
public string GetBasicCredential()
{
// APP ID
string customerKey = _configuration["Agora:clientId"];
// 客户密钥
string customerSecret = _configuration["Agora:clientSecret"];
// 拼接 APP ID 和APP密钥
string plainCredential = customerKey + ":" + customerSecret;
// 使用 base64 进行编码
var plainTextBytes = Encoding.UTF8.GetBytes(plainCredential);
string encodedCredential = Convert.ToBase64String(plainTextBytes);
// 创建 authorization header
return "Basic " + encodedCredential;
}
/// <summary>
/// 获取频道用户
/// </summary>
/// <param name="roomNum"></param>
/// <returns></returns>
public async Task<ChannelUser> GetChannelUserList(string roomNum)
{
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", GetBasicCredential());
var apiUrl = GetFullApiUrl($"dev/v1/channel/user/{_configuration["Agora:appId"]}/{roomNum}");
var responseMessage = await httpClient.GetAsync(apiUrl);
if (!responseMessage.IsSuccessStatusCode)
{
await Console.Out.WriteLineAsync($"声网接口请求失败!请求地址:{apiUrl} 请求结果:{await responseMessage.Content.ReadAsStringAsync()}");
return null;
}
var responseData = await responseMessage.Content.ReadFromJsonAsync<AgoraResponse<ChannelUser>>();
if (responseData == null)
{
await Console.Out.WriteLineAsync($"声网接口解析失败!请求地址:{apiUrl} 请求结果:{await responseMessage.Content.ReadAsStringAsync()}");
return null;
}
return responseData.Data;
}
/// <summary>
/// 更新每个频道用户在线数量
/// </summary>
/// <returns></returns>
public async Task RefreshChannelUserCount()
{
var pageSize = 500D;
var pageIndex = 1;
var totalCount = 0D;
do
{
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", GetBasicCredential());
var apiUrl = GetFullApiUrl($"dev/v1/channel/{_configuration["Agora:appId"]}?page_no={pageIndex}&page_size={pageSize}");
var responseMessage = await httpClient.GetAsync(apiUrl);
if (!responseMessage.IsSuccessStatusCode)
{
await Console.Out.WriteLineAsync($"声网接口请求失败!请求地址:{apiUrl} 请求结果:{await responseMessage.Content.ReadAsStringAsync()}");
return;
}
var responseData = await responseMessage.Content.ReadFromJsonAsync<AgoraResponse<Channel>>();
if (responseData == null)
{
await Console.Out.WriteLineAsync($"声网接口解析失败!请求地址:{apiUrl} 请求结果:{await responseMessage.Content.ReadAsStringAsync()}");
return;
}
totalCount = responseData.Data.total_size.ToDouble();
var ketValue = responseData.Data.channels.ToDictionary(x => x.channel_name, x => x.user_count);
if (!ketValue.IsNullOrEmpty())
{
RedisHelper.Instance.HSet(Constant.REDIS_CHANNEL_USERCOUNT, responseData.Data.channels.ToDictionary(x => x.channel_name, x => x.user_count));
}
// 总页数 = 总数/每页数
//var pageCount = (int)Math.Ceiling(Math.Round(totalCount / pageSize, 3));
} while (totalCount > 0 && (int)Math.Ceiling(Math.Round(totalCount / pageSize, 3)) > pageIndex);
}
}
}

View File

@ -0,0 +1,99 @@
using Aliyun.OSS;
using Masuit.Tools;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Extensions.Hosting;
using System.Security.Cryptography;
using System.Text;
using WGShare.Domain.FriendlyException;
namespace WGShare.API.Helpers
{
public class OssHelper
{
private readonly IConfiguration _configuration;
private readonly string _accessKeyId;
private readonly string _accessKeySecret;
private readonly string _bucketName;
private readonly string _endpoint;
private readonly OssClient _ossClient;
public OssHelper(IConfiguration configuration)
{
_configuration = configuration;
_accessKeyId = configuration["OSS:AccessKeyID"];
_accessKeySecret = configuration["OSS:AccessKeySecret"];
_bucketName = configuration["OSS:BucketName"];
_endpoint = configuration["OSS:Endpoint"];
this._ossClient = new OssClient("https://" + _endpoint, _accessKeyId, _accessKeySecret);
}
/// <summary>
/// 获取上传url
/// </summary>
/// <param name="path">bucket 路径</param>
/// <param name="fileName">文件名</param>
/// <param name="expireInSecond">过期时间 秒</param>
/// <returns></returns>
public Dictionary<string, string> GetUploadUrl(string path, string fileName, uint expireInSecond = 300)
{
//OssClient ossClient = new OssClient("httos://" + _endpoint, _accessKeyId, _accessKeySecret);
var policy = new PolicyConditions();
// 设置最大文件1000M
policy.AddConditionItem(PolicyConditions.CondContentLengthRange, 0, 1048576000);
policy.AddConditionItem(MatchMode.StartWith, PolicyConditions.CondKey, path.TrimEnd('/') + '/');
var postPolicy = _ossClient.GeneratePostPolicy(DateTimeOffset.Now.AddSeconds(expireInSecond).LocalDateTime, policy);
var policyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(postPolicy));
// 计算签名
var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(_accessKeySecret));
var bytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(policyBase64));
var sign = Convert.ToBase64String(bytes);
var host = $"https://{_bucketName}.{_endpoint}";
var key = $"{path.TrimEnd('/')}/{fileName}";
//var fullUrl = $"https://{_bucketName}.{_endpoint}/{key}";
return new Dictionary<string, string>
{
{ "OSSAccessKeyId", _configuration["OSS:AccessKeyID"]},
{ "Host",host },
{ "key",key},
{ "policy",policyBase64},
{ "Signature",sign},
{ "success_action_status","200"},
//{ "fullUrl",fullUrl },
{"expire",expireInSecond.ToString() },
{ "name",fileName}
};
}
public string GetAccessFileUrl(string path)
{
if (string.IsNullOrWhiteSpace(path) )
{
throw Oops.Oh("路径不能为空!");
}
var urlRequest = new GeneratePresignedUriRequest(_bucketName, path)
{
Expiration = DateTimeOffset.Now.AddMinutes(1).LocalDateTime
};
return _ossClient.GeneratePresignedUri(urlRequest).ToString();
}
public bool DeleteObjects(params string[] remotePathList)
{
if (remotePathList.IsNullOrEmpty())
{
return true;
}
var result = _ossClient.DeleteObjects(new DeleteObjectsRequest(_bucketName, remotePathList, true));
return result.HttpStatusCode == System.Net.HttpStatusCode.OK;
}
}
}

View File

@ -0,0 +1,33 @@
namespace WGShare.API.Hubs
{
/// <summary>
/// 客户端消息
/// </summary>
public interface IMessageClient
{
/// <summary>
/// 接受频道消息
/// </summary>
/// <param name="userName"></param>
/// <param name="message"></param>
/// <returns></returns>
Task ReceiveMessage(string userName, string message);
/// <summary>
/// 邀请进入会议室
/// </summary>
/// <param name="roomNum">会议号</param>
/// <param name="roomName">会议名称</param>
/// <param name="InviterName">邀请人名</param>
/// <returns></returns>
Task Invitation(string roomNum, string roomName, string InviterName);
/// <summary>
/// 强制退出房间
/// </summary>
/// <param name="roomNum">会议号</param>
/// <returns></returns>
Task ForceExitRoom(string roomNum);
}
}

View File

@ -0,0 +1,141 @@
using Masuit.Tools;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Text;
using WGShare.API.Helpers;
using WGShare.Domain.Constant;
namespace WGShare.API.Hubs
{
[Authorize]
public class SessionManageHub : Hub<IMessageClient>
{
public async override Task OnConnectedAsync()
{
var tenant = Context.User?.Claims.FirstOrDefault(x => x.Type == "tenant")?.Value;
var uid = Context.User?.Claims.FirstOrDefault(x => x.Type == "uid")?.Value;
await Console.Out.WriteLineAsync("连接成功 当前租户:" + tenant);
await Console.Out.WriteLineAsync("连接成功 uid" + uid);
await Console.Out.WriteLineAsync("连接成功 connectId" + Context.ConnectionId);
// 存储在线信息
RedisHelper.Instance.HSet(RedisKeyConstant.SessionManage.GetOnlineUserKey(tenant), uid, Context.ConnectionId);
}
public async override Task OnDisconnectedAsync(Exception? exception)
{
var tenant = Context.User?.Claims.FirstOrDefault(x => x.Type == "tenant")?.Value;
var uid = Context.User?.Claims.FirstOrDefault(x => x.Type == "uid")?.Value;
await Console.Out.WriteLineAsync("断开连接 当前租户:" + tenant);
await Console.Out.WriteLineAsync("断开连接 uid" + uid);
await Console.Out.WriteLineAsync("断开连接 connectId" + Context.ConnectionId);
// 获取用户参加得频道
var roomNums = RedisHelper.Instance.HKeys(RedisKeyConstant.SessionManage.GetUserJoinChannelKey(uid));
using (var pipe = RedisHelper.Instance.StartPipe())
{
// 移除在线信息
pipe.HDel(RedisKeyConstant.SessionManage.GetOnlineUserKey(tenant), uid);
if (!roomNums.IsNullOrEmpty())
{
await Console.Out.WriteLineAsync($@"uid:{uid} 退出以下频道:{string.Join(',', roomNums)}");
// 所有房间移除该用户
roomNums.ForEach(roomNum =>
{
pipe.HDel(RedisKeyConstant.SessionManage.GetChannelUserKey(tenant, roomNum), uid);
pipe.HIncrBy(RedisKeyConstant.SessionManage.GetChannelUserCountKey(tenant), roomNum, -1);
});
}
// 删除用户已加入频道信息
pipe.Del(RedisKeyConstant.SessionManage.GetUserJoinChannelKey(uid));
pipe.EndPipe();
}
}
/// <summary>
/// 加入频道
/// </summary>
/// <param name="roomNum"></param>
[HubMethodName("joinChannel")]
public async Task JoinChannel(string roomNum)
{
var tenant = Context.User?.Claims.FirstOrDefault(x => x.Type == "tenant")?.Value;
var uid = Context.User?.Claims.FirstOrDefault(x => x.Type == "uid")?.Value;
using (var pipe = RedisHelper.Instance.StartPipe())
{
pipe.HSet(RedisKeyConstant.SessionManage.GetChannelUserKey(tenant, roomNum), uid, Context.ConnectionId);
pipe.HIncrBy(RedisKeyConstant.SessionManage.GetChannelUserCountKey(tenant), roomNum, 1);
pipe.HSet(RedisKeyConstant.SessionManage.GetUserJoinChannelKey(uid), roomNum, 1);
pipe.EndPipe();
}
await Groups.AddToGroupAsync(Context.ConnectionId, roomNum);
}
/// <summary>
/// 离开频道
/// </summary>
/// <param name="roomNum"></param>
[HubMethodName("levelChannel")]
public async Task LevelChannel(string roomNum)
{
var tenant = Context.User?.Claims.FirstOrDefault(x => x.Type == "tenant")?.Value;
var uid = Context.User?.Claims.FirstOrDefault(x => x.Type == "uid")?.Value;
using (var pipe = RedisHelper.Instance.StartPipe())
{
pipe.HDel(RedisKeyConstant.SessionManage.GetChannelUserKey(tenant, roomNum), uid);
pipe.HDel(RedisKeyConstant.SessionManage.GetUserJoinChannelKey(uid), roomNum);
pipe.HIncrBy(RedisKeyConstant.SessionManage.GetChannelUserCountKey(tenant), roomNum, -1);
pipe.EndPipe();
}
await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomNum);
}
/// <summary>
/// 发送频道消息
/// </summary>
/// <param name="rooNum"></param>
/// <param name="userName"></param>
/// <param name="msg"></param>
[HubMethodName("sendChannelMsg")]
public async Task SenMessage(string rooNum, string msg)
{
var uname = Context.User?.Claims.FirstOrDefault(x => x.Type == "uname")?.Value;
await Clients.GroupExcept(rooNum, Context.ConnectionId).ReceiveMessage(uname, msg);
}
///// <summary>
///// 邀请呼叫
///// </summary>
///// <param name="inviteeUid">被邀请人id</param>
///// <param name="roomName">会议名称</param>
///// <param name="roomNum">会议号</param>
///// <returns></returns>
//[HubMethodName("call")]
//public async Task<(bool isSuccess, string msg)> CallUser(string inviteeUid, string roomName, string roomNum)
//{
// var tenant = Context.User?.Claims.FirstOrDefault(x => x.Type == "tenant")?.Value;
// var uname = Context.User?.Claims.FirstOrDefault(x => x.Type == "uname")?.Value;
// var connectId = RedisHelper.Instance.HGet(RedisKeyConstant.SessionManage.GetOnlineUserKey(tenant), inviteeUid);
// if (string.IsNullOrWhiteSpace(connectId))
// {
// return (false, "被邀请人不在线");
// }
// await Clients.Client(connectId).Invitation(roomNum, roomName, uname);
// return (true, "邀请成功");
//}
}
}

View File

@ -1,9 +1,14 @@
using Hangfire;
using Hangfire.MemoryStorage;
using Microsoft.AspNetCore.SignalR;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using SqlSugar; using SqlSugar;
using WGShare.API.BackgroudServices;
using WGShare.API.Helpers; using WGShare.API.Helpers;
using WGShare.API.Hubs;
using WGShare.API.ServiceConfigs; using WGShare.API.ServiceConfigs;
using Yitter.IdGenerator; using Yitter.IdGenerator;
@ -18,6 +23,7 @@ namespace WGShare.API
// Add services to the container. // Add services to the container.
RedisHelper.Initialization(new FreeRedis.RedisClient(configuration["Redis:master"]));
builder.Services.AddControllers(options => builder.Services.AddControllers(options =>
{ {
// 全局异常捕获,无需在代码中 写 try catch // 全局异常捕获,无需在代码中 写 try catch
@ -38,7 +44,13 @@ namespace WGShare.API
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwagger(); builder.Services.AddSwagger();
builder.Services.AddSignalR();
builder.Services.AddHttpClient();
builder.Services.ConfigureHangfire();
builder.Services.AddSingleton(new JwtHelper(configuration)); builder.Services.AddSingleton(new JwtHelper(configuration));
builder.Services.AddSingleton(new AgoraHelper(configuration));
builder.Services.AddSingleton(new OssHelper(configuration));
builder.Services.AddHostedService<OssCleanWorker>();
builder.Services.AddAuth(configuration["Jwt:Issuer"], builder.Services.AddAuth(configuration["Jwt:Issuer"],
configuration["Jwt:Audience"], configuration["Jwt:Audience"],
configuration["Jwt:SecretKey"]); configuration["Jwt:SecretKey"]);
@ -57,7 +69,6 @@ namespace WGShare.API
IsAutoCloseConnection = true IsAutoCloseConnection = true
}); });
YitIdHelper.SetIdGenerator(new IdGeneratorOptions(1)); YitIdHelper.SetIdGenerator(new IdGeneratorOptions(1));
RedisHelper.Initialization(new FreeRedis.RedisClient(configuration["Redis:master"]));
var app = builder.Build(); var app = builder.Build();
@ -74,13 +85,15 @@ namespace WGShare.API
}); });
app.UseCustomCors(); app.UseCustomCors();
} }
// Ìí¼ÓHangfire Dashboard
app.UseHangfireDashboard();
//调用中间件UseAuthentication认证必须在所有需要身份认证的中间件前调用比如 UseAuthorization授权 //调用中间件UseAuthentication认证必须在所有需要身份认证的中间件前调用比如 UseAuthorization授权
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.MapControllers(); app.MapControllers();
app.MapHub<SessionManageHub>("/session-manage").RequireCors(q => q.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
app.Run(); app.Run();
} }

View File

@ -14,7 +14,7 @@
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": false, "launchBrowser": false,
"launchUrl": "swagger", "launchUrl": "swagger",
"applicationUrl": "http://localhost:5192", "applicationUrl": "http://*:5192",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }

Binary file not shown.

View File

@ -36,6 +36,21 @@ namespace WGShare.API.ServiceConfigs
ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒) ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
RequireExpirationTime = true, RequireExpirationTime = true,
}; };
//// signalR 鉴权
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/session-manage")))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
}); });

View File

@ -18,7 +18,7 @@ namespace WGShare.API.ServiceConfigs
//var hosts = AppSetting.AllowedHosts.Split("|", StringSplitOptions.RemoveEmptyEntries); //var hosts = AppSetting.AllowedHosts.Split("|", StringSplitOptions.RemoveEmptyEntries);
app.UseCors(options => app.UseCors(options =>
{ {
options.WithOrigins("*") // 允许跨域请求的地址 options.AllowAnyOrigin() // 允许跨域请求的地址
.AllowAnyHeader() // 允许的请求标头 .AllowAnyHeader() // 允许的请求标头
.AllowAnyMethod(); // 允许跨域请求的类型 (GET,POST等) .AllowAnyMethod(); // 允许跨域请求的类型 (GET,POST等)

View File

@ -0,0 +1,41 @@
using Mapster;
using MapsterMapper;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.Reflection.Metadata;
using Microsoft.OpenApi.Models;
using Hangfire;
using Hangfire.MemoryStorage;
namespace WGShare.API.ServiceConfigs
{
public static class HangfireServiceExtensions
{
/// <summary>
/// 添加Hangfire
/// </summary>
/// <param name="services"> </param>
/// <returns></returns>
public static IServiceCollection ConfigureHangfire(this IServiceCollection services)
{
services.AddHangfire(x => x.UseMemoryStorage());
// Hangfire全局配置
GlobalConfiguration.Configuration
.UseColouredConsoleLogProvider()
.UseMemoryStorage()
.WithJobExpirationTimeout(TimeSpan.FromDays(7));
// Hangfire服务器配置
services.AddHangfireServer(options =>
{
options.HeartbeatInterval = TimeSpan.FromSeconds(10);
});
return services;
}
}
}

View File

@ -49,8 +49,8 @@ namespace WGShare.API.ServiceConfigs
} }
}); });
//string xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SwaggerGroup.xml"); string xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WGShare.API.xml");
//w.IncludeXmlComments(xmlPath, true); w.IncludeXmlComments(xmlPath, true);
}); });

View File

@ -5,9 +5,15 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile> <GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>WGShare.API.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="DotNetZip" Version="1.16.0" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
<PackageReference Include="Hangfire.Core" Version="1.8.14" />
<PackageReference Include="Hangfire.MemoryStorage" Version="1.8.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup> </ItemGroup>
@ -15,4 +21,19 @@
<ProjectReference Include="..\WGShare.Domain\WGShare.Domain.csproj" /> <ProjectReference Include="..\WGShare.Domain\WGShare.Domain.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="AgoraIO">
<HintPath>Reference\AgoraIO.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<None Update="Reference\AgoraIO.dll">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="WGShare.API.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>

381
WGShare.API/WGShare.API.xml Normal file
View File

@ -0,0 +1,381 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>WGShare.API</name>
</assembly>
<members>
<member name="M:WGShare.API.Controllers.AuthController.CheckUser(System.String)">
<summary>
检查用户名
</summary>
<param name="userName"></param>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.AuthController.Login(WGShare.Domain.DTOs.Login.UserLoginDTO)">
<summary>
正常账号登录
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.AuthController.Login(WGShare.Domain.DTOs.Login.AnonymousLoginDTO)">
<summary>
匿名登录,直接进入会议室
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.AuthController.Logout">
<summary>
登出(暂未处理任何业务逻辑)
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.AuthController.LoginForAdmin(WGShare.Domain.DTOs.Login.UserLoginDTO)">
<summary>
管理员登录
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.AuthController.GetAdminInfo">
<summary>
管理员信息
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Backend.PremController.GetPermValue">
<summary>
获取最新权限值
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Backend.UserController.EditPremissions(System.Collections.Generic.List{WGShare.Domain.DTOs.User.UserPremInputDTO})">
<summary>
权限修改
</summary>
<returns></returns>
</member>
<member name="T:WGShare.API.Controllers.Frontend.HomeController">
<summary>
首页接口
</summary>
</member>
<member name="M:WGShare.API.Controllers.Frontend.HomeController.GetRooms(WGShare.Domain.GeneralModel.PagedBaseDto)">
<summary>
获取会议室列表
</summary>
<param name="dto"></param>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.HomeController.CreateRoom(WGShare.Domain.DTOs.Room.RoomInputDTO)">
<summary>
创建会议室
</summary>
<param name="inputDTO"></param>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.HomeController.GetRTMToken">
<summary>
获取 rtm token
</summary>
<returns></returns>
</member>
<member name="T:WGShare.API.Controllers.Frontend.RoomController">
<summary>
会议室接口
</summary>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.SetRoomManager(System.String,System.Collections.Generic.List{System.String})">
<summary>
设置房间管理员
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.DelRoomManager(System.String,System.Collections.Generic.List{System.String})">
<summary>
取消房间管理员
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.GetUsers(System.String)">
<summary>
查询用户信息
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.ExistsRoom(System.String)">
<summary>
检验房间是否存在
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.GetRoom(System.String)">
<summary>
获取单个会议室信息
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.GetRTCToken(System.String)">
<summary>
获取房间rtc token
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.InviteUser(System.String,System.String[])">
<summary>
邀请用户
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.KickOut(System.String,System.String)">
<summary>
踢出房间
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.AddFile(WGShare.Domain.DTOs.File.ShareFileInputDTO)">
<summary>
分享上传文件
</summary>
<param name="inputDTO"></param>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.DeleteFile(System.Collections.Generic.List{System.String})">
<summary>
删除文件
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.GetFilesList(System.String,System.String,WGShare.Domain.GeneralModel.PagedBaseDto)">
<summary>
获取分享文件列表
</summary>
<param name="roomId">房间Id</param>
<param name="pagedBaseDto"></param>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.GetUploadUrl(System.String,System.String)">
<summary>
获取文件上传url
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.RoomController.GetDownloadUrl(System.String,System.String)">
<summary>
获取文件下载地址
</summary>
<param name="fileUrl"></param>
<param name="fileId">文件Id</param>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.UserController.GetUserList(System.String,WGShare.Domain.GeneralModel.PagedBaseDto)">
<summary>
获取用户列表
</summary>
<param name="searchKeywod">用户名 或 账号</param>
<param name="pagedBaseDto">翻页信息</param>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.UserController.AddUser(WGShare.Domain.DTOs.User.UserInputDTO)">
<summary>
新增用户
</summary>
<param name="inputDTO"></param>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.UserController.Modify(WGShare.Domain.DTOs.User.UserInputDTO)">
<summary>
修改用户信息
</summary>
<param name="inputDTO"></param>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.UserController.ModifyPassword(WGShare.Domain.DTOs.User.UserChangePwdDTO)">
<summary>
更改密码
</summary>
<param name="inputDTO"></param>
<returns></returns>
</member>
<member name="M:WGShare.API.Controllers.Frontend.UserController.Delete(System.String[])">
<summary>
删除用户
</summary>
<param name="ids"></param>
<returns></returns>
</member>
<member name="T:WGShare.API.Controllers.PublicController">
<summary>
前后端共用接口
</summary>
</member>
<member name="M:WGShare.API.Controllers.PublicController.GetDropDownList">
<summary>
角色列表下拉框
</summary>
<returns></returns>
</member>
<member name="F:WGShare.API.Helpers.AgoraHelper.Constant.REDIS_CHANNEL_USERCOUNT">
<summary>
Redis 键hash ,每个频道用户数量
</summary>
</member>
<member name="M:WGShare.API.Helpers.AgoraHelper.GetFullApiUrl(System.String)">
<summary>
获取完整的声网API请求路径
</summary>
<param name="apiSuffixPath">api .com 后的路径后缀</param>
<returns></returns>
</member>
<member name="M:WGShare.API.Helpers.AgoraHelper.GetChannelUserList(System.String)">
<summary>
获取频道用户
</summary>
<param name="roomNum"></param>
<returns></returns>
</member>
<member name="M:WGShare.API.Helpers.AgoraHelper.RefreshChannelUserCount">
<summary>
更新每个频道用户在线数量
</summary>
<returns></returns>
</member>
<member name="M:WGShare.API.Helpers.OssHelper.GetUploadUrl(System.String,System.String,System.UInt32)">
<summary>
获取上传url
</summary>
<param name="path">bucket 路径</param>
<param name="fileName">文件名</param>
<param name="expireInSecond">过期时间 秒</param>
<returns></returns>
</member>
<member name="T:WGShare.API.Helpers.RedisHelper">
<summary>
redis静态访问类
</summary>
</member>
<member name="P:WGShare.API.Helpers.RedisHelper.Instance">
<summary>
redis实例
</summary>
</member>
<member name="M:WGShare.API.Helpers.RedisHelper.Initialization(FreeRedis.RedisClient)">
<summary>
初始化redis静态访问类 RedisHelper.Initialization(new FreeRedis.RedisClient(\"127.0.0.1:6379,password=123,defaultDatabase=13,maxpoolsize=50,prefix=key前辍\"))
</summary>
<param name="redisClient"></param>
</member>
<member name="M:WGShare.API.Helpers.RedisHelper.RandomExpired(System.Int32,System.Int32)">
<summary>
随机秒防止所有key同一时间过期雪崩
</summary>
<param name="minTimeoutSeconds">最小秒数</param>
<param name="maxTimeoutSeconds">最大秒数</param>
<returns></returns>
</member>
<member name="T:WGShare.API.Hubs.IMessageClient">
<summary>
客户端消息
</summary>
</member>
<member name="M:WGShare.API.Hubs.IMessageClient.ReceiveMessage(System.String,System.String)">
<summary>
接受频道消息
</summary>
<param name="userName"></param>
<param name="message"></param>
<returns></returns>
</member>
<member name="M:WGShare.API.Hubs.IMessageClient.Invitation(System.String,System.String,System.String)">
<summary>
邀请进入会议室
</summary>
<param name="roomNum">会议号</param>
<param name="roomName">会议名称</param>
<param name="InviterName">邀请人名</param>
<returns></returns>
</member>
<member name="M:WGShare.API.Hubs.IMessageClient.ForceExitRoom(System.String)">
<summary>
强制退出房间
</summary>
<param name="roomNum">会议号</param>
<returns></returns>
</member>
<member name="M:WGShare.API.Hubs.SessionManageHub.JoinChannel(System.String)">
<summary>
加入频道
</summary>
<param name="roomNum"></param>
</member>
<member name="M:WGShare.API.Hubs.SessionManageHub.LevelChannel(System.String)">
<summary>
离开频道
</summary>
<param name="roomNum"></param>
</member>
<member name="M:WGShare.API.Hubs.SessionManageHub.SenMessage(System.String,System.String)">
<summary>
发送频道消息
</summary>
<param name="rooNum"></param>
<param name="userName"></param>
<param name="msg"></param>
</member>
<member name="M:WGShare.API.ServiceConfigs.AuthenticationServiceExtensions.AddAuth(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,System.String)">
<summary>
添加认证和授权
</summary>
<param name="services">服务集合</param>
<returns></returns>
</member>
<member name="T:WGShare.API.ServiceConfigs.CorsAppBuilderExtensions">
<summary>
跨域配置扩展服务
</summary>
</member>
<member name="M:WGShare.API.ServiceConfigs.CorsAppBuilderExtensions.UseCustomCors(Microsoft.AspNetCore.Builder.IApplicationBuilder)">
<summary>
允许所有跨域请求 (属于基础服务,若已调用 UseBasicServices 则无需调用)
</summary>
<param name="app"></param>
<returns></returns>
</member>
<member name="T:WGShare.API.ServiceConfigs.GlobalExceptionFilter">
<summary>
全局异常捕获
</summary>
</member>
<member name="M:WGShare.API.ServiceConfigs.HangfireServiceExtensions.ConfigureHangfire(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
添加Hangfire
</summary>
<param name="services"> </param>
<returns></returns>
</member>
<member name="M:WGShare.API.ServiceConfigs.ModelActionFilter.OnActionExecuted(Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext)">
<summary>
在Controller的Action执行后执行
</summary>
<param name="context"></param>
</member>
<member name="M:WGShare.API.ServiceConfigs.ModelActionFilter.OnActionExecuting(Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext)">
<summary>
在Controller的Action执行前执行
</summary>
<param name="context"></param>
</member>
<member name="M:WGShare.API.ServiceConfigs.SqlsugarServiceExtensions.AddSqlsugar(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,SqlSugar.ConnectionConfig[])">
<summary>
添加SqlSugar
</summary>
<param name="services">服务集合</param>
<returns></returns>
</member>
<member name="M:WGShare.API.ServiceConfigs.SwaggerServiceExtensions.AddSwagger(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
添加认证和授权
</summary>
<param name="services">服务集合</param>
<returns></returns>
</member>
</members>
</doc>

View File

@ -10,6 +10,6 @@
"usercenter": "Database=usercenter;Server=192.168.2.9;Port=3306;Uid=root;Pwd=qwe123!@#;AllowZeroDateTime=True;ConvertZeroDateTime=True;" "usercenter": "Database=usercenter;Server=192.168.2.9;Port=3306;Uid=root;Pwd=qwe123!@#;AllowZeroDateTime=True;ConvertZeroDateTime=True;"
}, },
"Redis": { "Redis": {
"master": "192.168.2.7:6379,password=qwe123!@#,defaultDatabase=12,name=wgshare,prefix=wgshare" "master": "192.168.2.7:6379,password=qwe123!@#,defaultDatabase=13,name=wgshare,prefix=wgshare"
} }
} }

View File

@ -11,13 +11,28 @@
"Issuer": "WGshareApi", "Issuer": "WGshareApi",
"Audience": "WGshareClient", "Audience": "WGshareClient",
// //
"Expires": 14400 "Expires": 86400
}, },
"ConnectionStrings": { "ConnectionStrings": {
"metting": "Database=metting;Server=192.168.2.9;Port=3306;Uid=root;Pwd=qwe123!@#;AllowZeroDateTime=True;ConvertZeroDateTime=True;", "metting": "Database=metting;Server=192.168.2.9;Port=3306;Uid=root;Pwd=qwe123!@#;AllowZeroDateTime=True;ConvertZeroDateTime=True;",
"usercenter": "Database=usercenter;Server=192.168.2.9;Port=3306;Uid=root;Pwd=qwe123!@#;AllowZeroDateTime=True;ConvertZeroDateTime=True;" "usercenter": "Database=usercenter;Server=192.168.2.9;Port=3306;Uid=root;Pwd=qwe123!@#;AllowZeroDateTime=True;ConvertZeroDateTime=True;"
}, },
"Redis": { "Redis": {
"master": "" "master": "172.29.33.83:16379,password=poiuyt)(*&^%,defaultDatabase=13,prefix=wgshare"
},
"Agora": {
"appId": "dcfc466a6ecb4a1f972630065dfb1e75",
"appSecret": "fc77000e329b4be7a0e26fa789e20d00",
"tokenExpireTimeInSecond": 7200,
"apiPrefix": "https://api.sd-rtn.com/",
"clientId": "80cdc24f7dfa4497a37d98da95a3c4a4",
"clientSecret": "8323581d4d464114b1f324b26cc62e09"
},
"OSS": {
"AccessKeyID": "LTAI5tQYVQHkkXxXTmjwiSDv",
"AccessKeySecret": "FKFNYRdS53FwA5ME2wM1585qX5eVEd",
"Endpoint": "oss-cn-chengdu.aliyuncs.com",
"Host": "https://wgshare.oss-cn-chengdu.aliyuncs.com/",
"BucketName": "wgshare"
} }
} }

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WGShare.Domain.AgoraApiResult
{
public class AgoraResponse<T>
{
public bool success { get; set; }
public T Data { get; set; }
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WGShare.Domain.AgoraApiResult
{
public class Channel
{
public List<ChannelUserCount> channels { get; set; }
public int total_size { get; set; }
}
public class ChannelUserCount
{
public string channel_name { get; set; }
public int user_count { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WGShare.Domain.AgoraApiResult
{
public class ChannelUser
{
/// <summary>
/// 频道是否存在
/// </summary>
public bool channel_exist { get; set; }
/// <summary>
/// 频道场景 1:通信场景 2:直播场景
/// </summary>
public int mode { get; set; }
/// <summary>
/// 频道总人数
/// </summary>
public int total { get; set; }
public List<string> users { get; set; }
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WGShare.Domain.Constant
{
/// <summary>
/// redis key 常量值
/// </summary>
public class RedisKeyConstant
{
/// <summary>
/// 会话管理
/// </summary>
public class SessionManage
{
/// <summary>
/// 在线人数
/// </summary>
public static string GetOnlineUserKey(string tenantId) => $@"te_{tenantId}:OnlieUser";
/// <summary>
/// 频道用户
/// </summary>
/// <param name="tenantId"></param>
/// <param name="roomNum"></param>
/// <returns></returns>
public static string GetChannelUserKey(string tenantId, string roomNum) => $@"te_{tenantId}:ch_{roomNum}";
/// <summary>
/// 用户参与频道
/// </summary>
/// <param name="uid"></param>
/// <returns></returns>
public static string GetUserJoinChannelKey(string uid) => $@"u_{uid}_join";
/// <summary>
/// 频道用户数
/// </summary>
/// <param name="tenantId"></param>
/// <returns></returns>
public static string GetChannelUserCountKey(string tenantId) => $@"te_{tenantId}:ChannelUserCount";
}
}
}

View File

@ -16,7 +16,7 @@ namespace WGShare.Domain.DTOs.File
///</summary> ///</summary>
public string FileUrl { get; set; } public string FileUrl { get; set; }
/// <summary> /// <summary>
/// 文件大小 kb /// 文件大小 字节数
///</summary> ///</summary>
public double Size { get; set; } public double Size { get; set; }
/// <summary> /// <summary>

View File

@ -20,5 +20,9 @@ namespace WGShare.Domain.DTOs.Room
/// 会议号 /// 会议号
/// </summary> /// </summary>
public string RoomNum { get; set; } public string RoomNum { get; set; }
/// <summary>
/// 在线人数
/// </summary>
public int OnlineUserCount { get; set; }
} }
} }

View File

@ -22,7 +22,7 @@ namespace WGShare.Domain.DTOs.User
/// <summary> /// <summary>
/// 密码 /// 密码
///</summary> ///</summary>
public string Pwd { get; set; } public string? Pwd { get; set; }
/// <summary> /// <summary>
/// ///
///</summary> ///</summary>
@ -30,6 +30,6 @@ namespace WGShare.Domain.DTOs.User
/// <summary> /// <summary>
/// 租户id /// 租户id
///</summary> ///</summary>
public string TenantId { get; set; } public string? TenantId { get; set; }
} }
} }

View File

@ -21,5 +21,10 @@ namespace WGShare.Domain.DTOs.User
public string RoleId { get; set; } public string RoleId { get; set; }
public string RoleName { get; set; } public string RoleName { get; set; }
/// <summary>
/// 是否管理员
/// </summary>
public bool IsManager { get; set; }
} }
} }

View File

@ -44,7 +44,7 @@ namespace WGShare.Domain.Entities
[SugarColumn(ColumnName = "user_id")] [SugarColumn(ColumnName = "user_id")]
public string UserId { get; set; } public string UserId { get; set; }
/// <summary> /// <summary>
/// 文件大小 kb /// 文件大小 字节数
///</summary> ///</summary>
[SugarColumn(ColumnName = "size")] [SugarColumn(ColumnName = "size")]
public double Size { get; set; } public double Size { get; set; }
@ -63,5 +63,10 @@ namespace WGShare.Domain.Entities
///</summary> ///</summary>
[SugarColumn(ColumnName = "download_count")] [SugarColumn(ColumnName = "download_count")]
public int DownloadCount { get; set; } public int DownloadCount { get; set; }
/// <summary>
/// 文件是否被删除
///</summary>
[SugarColumn(ColumnName = "is_fileclean")]
public bool IsFileClean { get; set; }
} }
} }

3
agora_key_and_secret.txt Normal file
View File

@ -0,0 +1,3 @@
证书在下载后将不再控制台保存或展示,请妥善保管证书
key80cdc24f7dfa4497a37d98da95a3c4a4
secret8323581d4d464114b1f324b26cc62e09