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 JwtHelper _jwtHelper;
private readonly IConfiguration _configuration;
public AuthController(ISqlSugarClient sqlSugar, JwtHelper jwtHelper)
public AuthController(ISqlSugarClient sqlSugar, JwtHelper jwtHelper,
IConfiguration configuration)
{
_sqlSugar = sqlSugar;
_jwtHelper = jwtHelper;
this._configuration = configuration;
}
/// <summary>
@ -71,6 +74,8 @@ namespace WGShare.API.Controllers
btnAutn.Add(new Claim("perm", perms.Sum(x => x.PermValue).ToString()));
btnAutn.Add(new Claim("role", user.RoleId));
btnAutn.Add(new Claim("tenant", user.TenantId));
btnAutn.Add(new Claim("account", user.Account));
btnAutn.Add(new Claim("uname", user.UserName));
return Ok(new
{
@ -78,7 +83,8 @@ namespace WGShare.API.Controllers
token = _jwtHelper.CreateToken(user.Id, btnAutn),
roleId = user.RoleId,
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
{
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 SqlSugar;
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.Room;
using WGShare.Domain.Entities;
using WGShare.Domain.FriendlyException;
@ -18,21 +25,33 @@ namespace WGShare.API.Controllers.Frontend
public class HomeController : BasicController
{
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;
this._configuration = configuration;
this._httpClientFactory = httpClientFactory;
this._agoraHelper = agoraHelper;
this._logger = logger;
}
/// <summary>
/// 获取会议室列表
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpGet("room")]
public async Task<PagedResult<RoomOutputDTO>> GetRooms(PagedBaseDto dto)
public async Task<PagedResult<RoomOutputDTO>> GetRooms([FromQuery] PagedBaseDto dto)
{
RefAsync<int> total = 0;
@ -40,7 +59,19 @@ namespace WGShare.API.Controllers.Frontend
.Where(x => x.TenantId == TenantId && x.IsDelete == false)
.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>
@ -49,8 +80,13 @@ namespace WGShare.API.Controllers.Frontend
/// <param name="inputDTO"></param>
/// <returns></returns>
[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>();
entity.Id = YitIdHelper.NextId().ToString();
entity.TenantId = TenantId;
@ -63,5 +99,19 @@ namespace WGShare.API.Controllers.Frontend
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 Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.IdentityModel.Tokens;
using SqlSugar;
using System.CodeDom;
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.User;
@ -12,7 +17,6 @@ using WGShare.Domain.Entities;
using WGShare.Domain.FriendlyException;
using WGShare.Domain.GeneralModel;
using Yitter.IdGenerator;
using ZstdSharp.Unsafe;
namespace WGShare.API.Controllers.Frontend
{
@ -24,24 +28,36 @@ namespace WGShare.API.Controllers.Frontend
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;
public RoomController(ISqlSugarClient sqlSugar)
public RoomController(ISqlSugarClient sqlSugar,
IConfiguration configuration,
OssHelper ossHelper,
AgoraHelper agoraHelper,
IHubContext<SessionManageHub, IMessageClient> hubContext)
{
this._sqlSugar = sqlSugar;
this._configuration = configuration;
this._ossHelper = ossHelper;
this._agoraHelper = agoraHelper;
this._hubContext = hubContext;
}
/// <summary>
/// 获取会议室管理员
/// </summary>
/// <returns></returns>
[HttpGet("manager")]
public async Task<List<string>> GetRoomManager([FromQuery] string roomId)
{
return await _sqlSugar.Queryable<RoomManager>()
.Where(x => x.RoomId == roomId)
.Select(x => x.UserId)
.ToListAsync();
}
///// <summary>
///// 获取会议室管理员
///// </summary>
///// <returns></returns>
//[HttpGet("manager")]
//public async Task<List<string>> GetRoomManager([FromQuery] string roomId)
//{
// return await _sqlSugar.Queryable<RoomManager>()
// .Where(x => x.RoomId == roomId)
// .Select(x => x.UserId)
// .ToListAsync();
//}
/// <summary>
/// 设置房间管理员
@ -59,24 +75,51 @@ namespace WGShare.API.Controllers.Frontend
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>
/// <returns></returns>
[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);
if (uid.IsNullOrEmpty())
var data = await _agoraHelper.GetChannelUserList(roomNum);
if (data == null)
{
throw Oops.Oh("请输入需要查询的用户id,多id请用英文逗号,分割");
throw Oops.Oh("请求失败");
}
if (!data.channel_exist)
{
throw Oops.Oh("频道不存在");
}
var users = await _sqlSugar.Queryable<User>()
.Where(x => uid.Contains(x.Id))
.Where(x => data.users.Contains(x.Account))
.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>
@ -104,6 +147,53 @@ namespace WGShare.API.Controllers.Frontend
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>
@ -135,14 +225,17 @@ namespace WGShare.API.Controllers.Frontend
/// <summary>
/// 获取分享文件列表
/// </summary>
/// <param name="roomId">房间Id</param>
/// <param name="pagedBaseDto"></param>
/// <returns></returns>
[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;
var list = await _sqlSugar.Queryable<ShareFile>()
.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
{
Id = sf.Id,
@ -150,7 +243,7 @@ namespace WGShare.API.Controllers.Frontend
UserName = u.UserName,
FileName = sf.FileName,
FileUrl = sf.FileUrl,
RoomId = u.Id,
RoomId = sf.RoomId,
Size = sf.Size,
ModifyTime = sf.ModifyTime,
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")]
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;
var list = await _sqlSugar.Queryable<User>()
.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);
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>
/// <returns></returns>
[HttpPost]
public async Task<bool> AddUser([FromQuery] UserInputDTO inputDTO)
public async Task<bool> AddUser([FromBody] UserInputDTO inputDTO)
{
var user = inputDTO.Adapt<User>();
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 Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using SqlSugar;
using WGShare.API.BackgroudServices;
using WGShare.API.Helpers;
using WGShare.API.Hubs;
using WGShare.API.ServiceConfigs;
using Yitter.IdGenerator;
@ -18,6 +23,7 @@ namespace WGShare.API
// Add services to the container.
RedisHelper.Initialization(new FreeRedis.RedisClient(configuration["Redis:master"]));
builder.Services.AddControllers(options =>
{
// 全局异常捕获,无需在代码中 写 try catch
@ -37,8 +43,14 @@ namespace WGShare.API
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
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 AgoraHelper(configuration));
builder.Services.AddSingleton(new OssHelper(configuration));
builder.Services.AddHostedService<OssCleanWorker>();
builder.Services.AddAuth(configuration["Jwt:Issuer"],
configuration["Jwt:Audience"],
configuration["Jwt:SecretKey"]);
@ -57,7 +69,6 @@ namespace WGShare.API
IsAutoCloseConnection = true
});
YitIdHelper.SetIdGenerator(new IdGeneratorOptions(1));
RedisHelper.Initialization(new FreeRedis.RedisClient(configuration["Redis:master"]));
var app = builder.Build();
@ -74,13 +85,15 @@ namespace WGShare.API
});
app.UseCustomCors();
}
// Ìí¼ÓHangfire Dashboard
app.UseHangfireDashboard();
//调用中间件UseAuthentication认证必须在所有需要身份认证的中间件前调用比如 UseAuthorization授权
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHub<SessionManageHub>("/session-manage").RequireCors(q => q.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
app.Run();
}

View File

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

Binary file not shown.

View File

@ -36,6 +36,21 @@ namespace WGShare.API.ServiceConfigs
ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
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);
app.UseCors(options =>
{
options.WithOrigins("*") // 允许跨域请求的地址
options.AllowAnyOrigin() // 允许跨域请求的地址
.AllowAnyHeader() // 允许的请求标头
.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");
//w.IncludeXmlComments(xmlPath, true);
string xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WGShare.API.xml");
w.IncludeXmlComments(xmlPath, true);
});

View File

@ -5,9 +5,15 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>WGShare.API.xml</DocumentationFile>
</PropertyGroup>
<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" />
</ItemGroup>
@ -15,4 +21,19 @@
<ProjectReference Include="..\WGShare.Domain\WGShare.Domain.csproj" />
</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>

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;"
},
"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",
"Audience": "WGshareClient",
//
"Expires": 14400
"Expires": 86400
},
"ConnectionStrings": {
"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;"
},
"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>
public string FileUrl { get; set; }
/// <summary>
/// 文件大小 kb
/// 文件大小 字节数
///</summary>
public double Size { get; set; }
/// <summary>

View File

@ -20,5 +20,9 @@ namespace WGShare.Domain.DTOs.Room
/// 会议号
/// </summary>
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>
public string Pwd { get; set; }
public string? Pwd { get; set; }
/// <summary>
///
///</summary>
@ -30,6 +30,6 @@ namespace WGShare.Domain.DTOs.User
/// <summary>
/// 租户id
///</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 RoleName { get; set; }
/// <summary>
/// 是否管理员
/// </summary>
public bool IsManager { get; set; }
}
}

View File

@ -44,7 +44,7 @@ namespace WGShare.Domain.Entities
[SugarColumn(ColumnName = "user_id")]
public string UserId { get; set; }
/// <summary>
/// 文件大小 kb
/// 文件大小 字节数
///</summary>
[SugarColumn(ColumnName = "size")]
public double Size { get; set; }
@ -63,5 +63,10 @@ namespace WGShare.Domain.Entities
///</summary>
[SugarColumn(ColumnName = "download_count")]
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