296 lines
12 KiB
C#
296 lines
12 KiB
C#
using AgoraIO.Media;
|
|
using AgoraIO.Rtm;
|
|
using Mapster;
|
|
using Masuit.Tools;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using MiniExcelLibs;
|
|
using MiniExcelLibs.Attributes;
|
|
using MiniExcelLibs.OpenXml;
|
|
using SqlSugar;
|
|
using System.IO;
|
|
using System.Text.RegularExpressions;
|
|
using WGShare.API.Controllers.Basic;
|
|
using WGShare.API.Helpers;
|
|
using WGShare.Domain.Constant;
|
|
using WGShare.Domain.DTOs.Room;
|
|
using WGShare.Domain.Entities;
|
|
using WGShare.Domain.Enums;
|
|
using WGShare.Domain.FriendlyException;
|
|
using WGShare.Domain.GeneralModel;
|
|
using Yitter.IdGenerator;
|
|
using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration;
|
|
|
|
namespace WGShare.API.Controllers.Frontend
|
|
{
|
|
/// <summary>
|
|
/// 首页接口
|
|
/// </summary>
|
|
[ApiExplorerSettings(GroupName = "frontend")]
|
|
[Route("home")]
|
|
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;
|
|
private readonly OssHelper _ossHelper;
|
|
|
|
public HomeController(
|
|
ISqlSugarClient sqlSugar,
|
|
IConfiguration configuration,
|
|
IHttpClientFactory httpClientFactory,
|
|
AgoraHelper agoraHelper,
|
|
ILogger<HomeController> logger,
|
|
OssHelper ossHelper)
|
|
{
|
|
_sqlSugar = sqlSugar;
|
|
this._configuration = configuration;
|
|
this._httpClientFactory = httpClientFactory;
|
|
this._agoraHelper = agoraHelper;
|
|
this._logger = logger;
|
|
this._ossHelper = ossHelper;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 获取会议室列表
|
|
/// </summary>
|
|
/// <param name="dto"></param>
|
|
/// <returns></returns>
|
|
[HttpGet("room")]
|
|
public async Task<PagedResult<RoomOutputDTO>> GetRooms([FromQuery] PagedBaseDto dto)
|
|
{
|
|
RefAsync<int> total = 0;
|
|
|
|
var list = await _sqlSugar.Queryable<Room>()
|
|
.Where(x => x.TenantId == TenantId && x.IsDelete == false)
|
|
.WhereIF(Year > 0 && RoleId == RoleEnums.User, x => x.Year == Year || x.Year == 0)
|
|
.WhereIF(Subject > 0 && RoleId == RoleEnums.User, x => x.Subject == Subject || x.Subject == 0)
|
|
.OrderBy(x => x.Id, OrderByType.Desc)
|
|
.ToPageListAsync(dto.PageIndex, dto.PageSize, total);
|
|
|
|
var result = list.Adapt<List<RoomOutputDTO>>();
|
|
if (!result.IsNullOrEmpty())
|
|
{
|
|
// 从Redis获取缓存在线人数 拼装数据
|
|
result.ForEach(x => x.OnlineUserCount = RedisHelper.Instance.HLen(RedisKeyConstant.SessionManage.GetChannelUserKey(TenantId, x.RoomNum)));
|
|
//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>
|
|
/// <param name="inputDTO"></param>
|
|
/// <returns></returns>
|
|
[HttpPost("room")]
|
|
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;
|
|
|
|
if (await _sqlSugar.Queryable<Room>().AnyAsync(x => x.RoomNum == inputDTO.RoomNum))
|
|
{
|
|
throw Oops.Oh("无效会议号,请重新输入");
|
|
}
|
|
|
|
return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新会议室信息
|
|
/// </summary>
|
|
/// <param name="inputDTO"></param>
|
|
/// <returns></returns>
|
|
[HttpPut("room-info")]
|
|
public async Task<bool> ModifyRoom([FromBody] RoomInfoInputDTO inputDTO)
|
|
{
|
|
var entity = inputDTO.Adapt<Room>();
|
|
return await _sqlSugar.Updateable(entity)
|
|
.UpdateColumns(x => new { x.Year, x.Subject })
|
|
.ExecuteCommandHasChangeAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 删除会议室
|
|
/// </summary>
|
|
/// <param name="inputDTO"></param>
|
|
/// <returns></returns>
|
|
[HttpDelete("room")]
|
|
public async Task<bool> DeleteRoom([FromQuery] string roomId)
|
|
{
|
|
return await _sqlSugar.Updateable<Room>()
|
|
.SetColumns(x => x.IsDelete == true)
|
|
.Where(x => x.Id == roomId).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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取Agora配置
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[HttpGet("agora-conf")]
|
|
public string GetAgoraConfig()
|
|
{
|
|
return _configuration["Agora:appId"].ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取会议记录
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[HttpGet("record")]
|
|
public async Task<string> GetMeetingRecord([FromQuery] long beginTimestamp, [FromQuery] long endTimestamp, [FromQuery] string roomNum)
|
|
{
|
|
var room = await _sqlSugar.Queryable<Room>().Where(x => x.RoomNum == roomNum && x.IsDelete == false).FirstAsync();
|
|
if (room == null)
|
|
{
|
|
throw Oops.Oh("该会议室不存在!");
|
|
}
|
|
|
|
var recordList = await _sqlSugar.Queryable<MeetingRecord>()
|
|
.InnerJoin<User>((mr, u) => mr.uid == u.Id)
|
|
.InnerJoin<Role>((mr, u, r) => u.RoleId == r.Id)
|
|
.Where((mr, u, r) => mr.channelName == roomNum && mr.ts >= beginTimestamp && mr.ts <= endTimestamp)
|
|
.Select((mr, u, r) => new MeetingRecord
|
|
{
|
|
Id = mr.Id,
|
|
uid = u.Id,
|
|
ts = mr.ts,
|
|
account = mr.account,
|
|
channelName = mr.channelName,
|
|
clientSeq = mr.clientSeq,
|
|
CreateTime = mr.CreateTime,
|
|
duration = mr.duration,
|
|
EventType = mr.EventType,
|
|
platform = mr.platform,
|
|
reason = mr.reason,
|
|
userName = u.UserName,
|
|
UserAccount = u.Account,
|
|
RoleName = r.RoleName,
|
|
}).ToListAsync();
|
|
|
|
|
|
if (recordList.IsNullOrEmpty())
|
|
{
|
|
throw Oops.Oh("该时间段内没有记录!");
|
|
}
|
|
|
|
var groupByUser = recordList.GroupBy(x => x.uid).ToList();
|
|
|
|
var beginDatetime = DateTimeUtils.FromJavaScriptTimestampToLocal(beginTimestamp);
|
|
var endDaetime = DateTimeUtils.FromJavaScriptTimestampToLocal(endTimestamp);
|
|
var value = new RoomMettingRecordExportDTO
|
|
{
|
|
RoomName = room.RoomName,
|
|
RoomNum = room.RoomNum,
|
|
BeginTime = beginDatetime.ToString("yyyy-MM-dd HH:mm:ss"),
|
|
EndTime = endDaetime.ToString("yyyy-MM-dd HH:mm:ss"),
|
|
Users = new List<UserBehavior>(),
|
|
Signin = new List<SignInListRecordExcelDto>()
|
|
};
|
|
|
|
#region 计算入会次数、累计参会时长
|
|
foreach (var userRecord in groupByUser)
|
|
{
|
|
if (userRecord.IsNullOrEmpty())
|
|
{
|
|
continue;
|
|
}
|
|
// 按时间排序
|
|
var orderedRecord = userRecord.OrderBy(x => x.ts).ToList();
|
|
|
|
// 获取第一次进入房间的事件记录
|
|
var firstJoinTimeRecord = orderedRecord
|
|
.FirstOrDefault(x => x.EventType == EventType.broadcaster_join_channel
|
|
|| x.EventType == EventType.audience_join_channel);
|
|
string firstJoinTime = "暂无记录";
|
|
if (firstJoinTimeRecord != null)
|
|
{
|
|
firstJoinTime = DateTimeUtils.FromJavaScriptTimestampToLocal(firstJoinTimeRecord.ts).ToString("yyyy-MM-dd HH:mm:ss");
|
|
}
|
|
|
|
// 获取最后一次离开房间的事件记录
|
|
var lastLeaveTimeRecord = orderedRecord
|
|
.LastOrDefault(x => x.EventType == EventType.broadcaster_leave_channel
|
|
|| x.EventType == EventType.audience_leave_channel);
|
|
string lastLeaveTime = "暂无记录";
|
|
if (lastLeaveTimeRecord != null)
|
|
{
|
|
lastLeaveTime = DateTimeUtils.FromJavaScriptTimestampToLocal(lastLeaveTimeRecord.ts).ToString("yyyy-MM-dd HH:mm:ss");
|
|
}
|
|
|
|
|
|
// 计算入会次数
|
|
var joinCount = orderedRecord.Count(x => x.EventType == EventType.broadcaster_join_channel
|
|
|| x.EventType == EventType.audience_join_channel);
|
|
|
|
// 累计参会时长
|
|
var sumTime = orderedRecord.Sum(x => x.duration);
|
|
|
|
value.Users.Add(new UserBehavior
|
|
{
|
|
Account = userRecord.FirstOrDefault().UserAccount,
|
|
FirstJoinTime = firstJoinTime,
|
|
LastExitTime = lastLeaveTime,
|
|
JoinCount = joinCount,
|
|
Role = userRecord.FirstOrDefault().RoleName,
|
|
UserName = userRecord.FirstOrDefault().userName,
|
|
SumTime = sumTime,
|
|
});
|
|
}
|
|
#endregion
|
|
|
|
#region 计算签到次数
|
|
var signins = await _sqlSugar.Queryable<UserSignInRecord>()
|
|
.InnerJoin<User>((u, us) => u.UId == us.Id)
|
|
.Where((u, us) => u.RoomNum == roomNum && u.CreateTime >= beginDatetime && u.CreateTime <= endDaetime)
|
|
.Select((u, us) => new SignInListRecordExcelDto
|
|
{
|
|
Account = us.Account,
|
|
SignInTime = u.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
|
|
UserName = us.UserName,
|
|
SignInName = u.SignInName
|
|
})
|
|
.ToListAsync();
|
|
value.Signin.AddRange(signins);
|
|
#endregion
|
|
|
|
using var stream = new MemoryStream();
|
|
|
|
await MiniExcel.SaveAsByTemplateAsync(stream, $@"{AppDomain.CurrentDomain.BaseDirectory}meetingRecordTemplate.xlsx", value);
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
var fileName = $@"excel/{room.RoomName}-参会记录-{beginDatetime.ToString("yyyyMMddHHmmss")}至{endDaetime.ToString("yyyyMMddHHmmss")}.xlsx";
|
|
_ossHelper.UploadWithExpireTime(fileName, stream, 10);
|
|
return _ossHelper.GetAccessFileUrl(fileName, 5);
|
|
}
|
|
}
|
|
}
|