Ver código fonte

在线课程

denny 1 ano atrás
pai
commit
dd19be83c6

+ 3 - 0
AutoMapperProfile.cs

@@ -1,6 +1,7 @@
 using AutoMapper;
 using WingInterfaceLibrary.DTO.Courses;
 using WingInterfaceLibrary.DTO.Education;
+using WingInterfaceLibrary.Request.Courses;
 
 namespace WingEducationService
 {
@@ -15,6 +16,8 @@ namespace WingEducationService
             CreateMap<TeacherSetExaminationAnswerDTO, ExaminationAnswerDTO>().ReverseMap();
             CreateMap<StudentSetExaminationAnswerDTO, ExaminationAnswerDTO>().ReverseMap();
             CreateMap<BaseStudentExaminationDTO, StudentExaminationDTO>().ReverseMap();
+            CreateMap<CourseBrowsedRecordDTO, CourseBrowsedRecordRequest>().ReverseMap();
+            CreateMap<CourseBrowsedRecordRequest, CourseBrowsedRecordDTO>().ReverseMap();
         }
     }
 }

+ 1 - 1
Common/LiveHeartRateManager.cs

@@ -73,7 +73,7 @@ namespace WingEducationService.Common
         }
 
         /// <summary>
-        /// 会诊心跳有效期验证
+        /// 课程心跳有效期验证
         /// </summary>
         private void StartCheckClients()
         {

+ 141 - 0
Common/VisitorLiveHeartRateManager.cs

@@ -0,0 +1,141 @@
+
+using System;
+using System.Collections.Concurrent;
+using System.Linq;
+using System.Threading.Tasks;
+using WingServerCommon.Config;
+using WingServerCommon.Config.Parameters;
+using WingServerCommon.Log;
+
+namespace WingEducationService.Common
+{
+    /// <summary>
+    /// 访问者直播心跳管理
+    /// </summary>
+    public class VisitorLiveHeartRateManager
+    {
+        private int _rateSeconds => EnvironmentConfigManager.GetParammeter<IntParameter>("Course", "HeartRateSeconds").Value;//120秒
+        private int _networkErrTimes => EnvironmentConfigManager.GetParammeter<IntParameter>("LiveConsultation", "NetworkErrTimes").Value;//3次间隔无心跳,网络不佳
+        private int _leaveRoomTimes => EnvironmentConfigManager.GetParammeter<IntParameter>("LiveConsultation", "LeaveRoomTimes").Value;//10次间隔无心跳,离开房间
+        private ConcurrentDictionary<string, LiveHeartRateInfo> _clientTokens = new ConcurrentDictionary<string, LiveHeartRateInfo>();
+
+        private Action<string, string, string> OnClientJoined;
+        private Action<string, string, string> OnClientNetworkErr;
+        private Action<string, string, string> OnClientLeave;
+
+        public VisitorLiveHeartRateManager(Action<string, string, string> handleClientJoined, Action<string, string, string> handleClientNetworkErr, Action<string, string, string> handleClientLeave)
+        {
+            OnClientJoined = handleClientJoined;
+            OnClientNetworkErr = handleClientNetworkErr;
+            OnClientLeave = handleClientLeave;
+            StartCheckClients();
+        }
+
+        /// <summary>
+        /// 加入检测集合
+        /// </summary>
+        /// <param name="token"></param>
+        /// <param name="clientId"></param>
+        /// <param name="name"></param>
+        /// <param name="consultationCode"></param>
+        public void AddOrUpdate(string token, string clientId, string name, string liveCode)
+        {
+            var uniqueId = GetUniqueId(token, liveCode);
+            _clientTokens.AddOrUpdate(uniqueId, (k) =>
+            {
+                var liveHeartRateInfo = new LiveHeartRateInfo()
+                {
+                    Token = token, 
+                    ClientId = clientId, 
+                    Name = name, 
+                    LiveCode = liveCode
+                };
+                OnClientJoined.Invoke(liveCode, liveHeartRateInfo.ClientId, name);
+                return liveHeartRateInfo;
+            }, (k, v) =>
+            {
+                v.Activate();
+                OnClientJoined.Invoke(liveCode, v.ClientId, name);
+                return v;
+            });
+        }
+
+        /// <summary>
+        /// get uniquedId
+        /// </summary>
+        /// <param name="token"></param>
+        /// <param name="liveCode"></param>        
+        /// <returns>uniqueId</returns>
+        private string GetUniqueId(string token, string liveCode)
+        {
+            var uniqueId = $"{liveCode}_{token}";
+            return uniqueId;
+        }
+
+        /// <summary>
+        /// 尝试关闭
+        /// </summary>
+        /// <param name="key"></param>
+        public bool Close(string token, string clientId, string name, string liveCode)
+        {
+            var uniqueId = GetUniqueId(token, liveCode);
+            if (_clientTokens.ContainsKey(uniqueId))
+            {
+                var res = _clientTokens.TryRemove(uniqueId, out _);
+                return res;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// 课程心跳有效期验证
+        /// </summary>
+        private void StartCheckClients()
+        {
+            Task.Run(async () =>
+            {
+                Logger.WriteLineInfo($"VisitorLiveHeartRateManager start check clients");
+                try
+                {
+                    while (true)
+                    {
+                        await Task.Delay(1 * 1000 * _rateSeconds);
+                        foreach (var tokenInfo in _clientTokens.Values)
+                        {
+                            tokenInfo.DeActivate();
+                            var uniqueId = GetUniqueId(tokenInfo.Token, tokenInfo.LiveCode);
+                            try
+                            {
+                                if (tokenInfo.LivingTimes == _networkErrTimes)
+                                {
+                                    OnClientNetworkErr.Invoke(tokenInfo.LiveCode, tokenInfo.ClientId, tokenInfo.Name);
+                                }
+                                else if (tokenInfo.LivingTimes == _leaveRoomTimes)
+                                {
+                                    if (!_clientTokens.Values.Any(x => x.ClientId == tokenInfo.ClientId && x.LiveCode == tokenInfo.LiveCode && x.LivingTimes < _leaveRoomTimes))
+                                    {
+                                        OnClientLeave.Invoke(tokenInfo.LiveCode, tokenInfo.ClientId, tokenInfo.Name);
+                                    }
+                                    _clientTokens.TryRemove(uniqueId, out _);
+                                }
+                            }
+                            catch (Exception ex)
+                            {
+                                Logger.WriteLineWarn($"check user[{tokenInfo.Name}] token {tokenInfo.Token} err, {ex}");
+                            }
+                        }
+                    }
+                }
+                catch (Exception ex)
+                {
+                    Logger.WriteLineError($"VisitorLiveHeartRateManager err, ex:{ex}");
+                }
+                Logger.WriteLineInfo($"VisitorLiveHeartRateManager finished");
+            });
+        }
+
+    }
+}

+ 218 - 4
Service/EducationService.Course.cs

@@ -1762,7 +1762,8 @@ namespace WingEducationService.Service
         /// <returns>是否成功</returns>
         public async Task<bool> SaveCourseBrowsedRecordAsync(CourseBrowsedRecordRequest request)
         {
-            return false;
+            var res = await _educationDBService.SaveCourseBrowsedRecordByDBAsync(request);
+            return res;
         }
 
         /// <summary>
@@ -1771,7 +1772,94 @@ namespace WingEducationService.Service
         /// <returns>是否成功</returns>
         public async Task<List<CourseBrowsedRecordDTO>> QueryCourseBrowsedRecordsAsync(QueryBrowsedRecordRequest request)
         {
-            return null;
+            var list = await _educationDBService.QueryCourseBrowsedRecordsByDBAsync(request);
+            list = list ?? new List<CourseBrowsedRecordDTO>();
+
+            //拼接在线列表,顺序为老师,互动专家,助教
+            var courseDetailInfo = await _courseDBService.FindCourseByCodeAsync(new FindCourseByCodeDBRequest() { Code = request.RelevanceCode });
+            if (courseDetailInfo == null)
+            {
+                //课程未发现,则直接返回游客列表
+                return list;
+            }
+            var teacherCourseBrowsedRecordInfo = new CourseBrowsedRecordDTO()
+            {
+                RelevanceCode = request.RelevanceCode,
+                UserCode = courseDetailInfo.TeacherCode,                
+                DisplayName = courseDetailInfo.TeacherName,
+                BrowsedUserType = BrowsedUserTypeEnum.Teacher
+            };            
+            var userCodes = new List<string>() { courseDetailInfo.TeacherCode };
+            var expertrCourseBrowsedRecordList = new List<CourseBrowsedRecordDTO>();
+            if (courseDetailInfo.Experts?.Count > 0)
+            {
+                var expertUsers = courseDetailInfo.Experts.Select(c => c.Code).Distinct().ToList();
+                userCodes.AddRange(expertUsers);
+
+                expertrCourseBrowsedRecordList = courseDetailInfo.Experts.Select(c => new CourseBrowsedRecordDTO()
+                {
+                    RelevanceCode = request.RelevanceCode,
+                    UserCode = c.Code,                
+                    DisplayName = c.Name,
+                    BrowsedUserType = BrowsedUserTypeEnum.Guesting
+                }).ToList();
+            }
+            var assistantCourseBrowsedRecordList = new List<CourseBrowsedRecordDTO>();
+            if (courseDetailInfo.Assistants?.Count > 0)
+            {
+                var assistantsUsers = courseDetailInfo.Assistants.Select(c => c.Code).Distinct().ToList();
+                userCodes.AddRange(assistantsUsers);
+
+                assistantCourseBrowsedRecordList = courseDetailInfo.Assistants.Select(c => new CourseBrowsedRecordDTO()
+                {
+                    RelevanceCode = request.RelevanceCode,
+                    UserCode = c.Code,                
+                    DisplayName = c.Name,
+                    BrowsedUserType = BrowsedUserTypeEnum.Assistant
+                }).ToList();
+            }
+            var memberRequest = new GetLiveMemberStatusInfosRequest()
+            {
+                UserCodes = userCodes
+            };
+            var stautsInfo = await _rtcService.GetLiveMemberStatusInfosAsync(memberRequest);
+            if (stautsInfo?.Count > 0)
+            {
+                foreach(var item in stautsInfo)
+                {
+                    if (item.Code == courseDetailInfo.TeacherCode)
+                    {
+                        teacherCourseBrowsedRecordInfo.IsOnline = (item.Status == LiveMemberStatus.Joined ? true : false);
+                        teacherCourseBrowsedRecordInfo.FromPlatform = item.LoginSource.ToString();
+                    }
+                    var findExpertRecord = expertrCourseBrowsedRecordList.FirstOrDefault(c => c.UserCode == item.Code);
+                    if (!string.IsNullOrEmpty(findExpertRecord.UserCode))
+                    {
+                        findExpertRecord.IsOnline = (item.Status == LiveMemberStatus.Joined ? true : false);
+                        findExpertRecord.FromPlatform = item.LoginSource.ToString();
+                    }
+
+                    var findAssistantRecord = assistantCourseBrowsedRecordList.FirstOrDefault(c => c.UserCode == item.Code);
+                    if (!string.IsNullOrEmpty(findAssistantRecord.UserCode))
+                    {
+                        findAssistantRecord.IsOnline = (item.Status == LiveMemberStatus.Joined ? true : false);
+                        findAssistantRecord.FromPlatform = item.LoginSource.ToString();
+                    }
+                }
+            }
+            if (!string.IsNullOrEmpty(teacherCourseBrowsedRecordInfo.UserCode))
+            {
+                list.Add(teacherCourseBrowsedRecordInfo);
+            }
+            if (expertrCourseBrowsedRecordList?.Count > 0)
+            {
+                list.AddRange(expertrCourseBrowsedRecordList);
+            }
+            if (assistantCourseBrowsedRecordList?.Count > 0)
+            {
+                list.AddRange(assistantCourseBrowsedRecordList);
+            }
+            return list;
         }
 
         /// <summary>
@@ -1781,7 +1869,9 @@ namespace WingEducationService.Service
         /// <returns></returns> 
         public async Task<bool> VisitorHeartRateAsync(QueryBrowsedRecordRequest request)
         {
-            return false;
+            var userToken = await _authenticationService.GetTokenAsync(request);
+            _visitorLiveHeartRateManager.AddOrUpdate(userToken.Code, request.UserCode, request.DisplayName, request.RelevanceCode);
+            return await Task.FromResult<bool>(true);
         }
 
         /// <summary>
@@ -1791,7 +1881,131 @@ namespace WingEducationService.Service
         /// <returns></returns>
         public async Task<bool> VisitorLeaveLiveCourseAsync(QueryBrowsedRecordRequest request)
         {
-            return false;
+            var userToken = await _authenticationService.GetTokenAsync(request);
+            _visitorLiveHeartRateManager.Close(userToken.Code, request.UserCode, request.DisplayName, request.RelevanceCode);
+            VisitorHandleClientLeave(request.RelevanceCode, request.UserCode, request.DisplayName);
+            return await Task.FromResult<bool>(true);
+        }
+
+        /// <summary>
+        /// 设置课程禁言
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        public async Task<bool> SetCourseLiveProhibitionAsync(CourseLiveProhibitionRequest request)
+        {
+            var res = await _courseDBService.SetCourseLiveProhibitionByDBAsync(request);
+            return res;
+        }
+
+        #region private method  
+        /// <summary>
+        /// Visitor心跳检测进入房间
+        /// </summary>
+        /// <param name="roomId"></param>
+        /// <param name="clientId"></param>
+        /// <param name="name"></param>
+        /// <returns></returns> 
+        private async void VisitorHandleClientJoined(string roomId, string clientId, string name)
+        {
+            try
+            {
+                //获取记录缓存
+                var queryRequest = new QueryBrowsedRecordRequest()
+                {
+                    RelevanceCode = roomId,
+                    UserCode = clientId,
+                    DisplayName = name
+                };
+                var recordInfo = await _educationDBService.GetCourseBrowsedRecordByRelevanceCodeDBAsync(queryRequest);
+
+                //存在, 且不在线,则更新状态
+                if (!string.IsNullOrEmpty(recordInfo?.CourseBrowsedRecordCode) && !recordInfo.IsOnline)
+                {
+                    var updateEntity = recordInfo.MappingTo<CourseBrowsedRecordRequest>();
+                    updateEntity.IsOnline = true;
+                    var res = await _educationDBService.SaveCourseBrowsedRecordByDBAsync(updateEntity);                    
+                }
+                else if (string.IsNullOrEmpty(recordInfo?.CourseBrowsedRecordCode))
+                {
+                    //不存在,新增
+                    var insertEntity = new CourseBrowsedRecordRequest()
+                    {
+                        IsOnline = true,
+                        RelevanceCode = roomId,
+                        UserCode = clientId,
+                        DisplayName = name,
+                        BrowsedUserType = BrowsedUserTypeEnum.Visitor
+                    };
+                    var res = await _educationDBService.SaveCourseBrowsedRecordByDBAsync(insertEntity);           
+                }
+                else
+                {
+                    //存在,在线,则不处理
+                }
+            }
+            catch (Exception ex)
+            {
+                Logger.WriteLineWarn($"LiveService VisitorHandleClientJoined err, name:{name}, clientId:{clientId}, roomId:{roomId}, ex:{ex}");
+            }
+        }
+
+        /// <summary>
+        /// Visitor心跳检测网络异常
+        /// </summary>
+        /// <param name="roomId"></param>
+        /// <param name="clientId"></param>
+        /// <param name="name"></param>
+        /// <returns></returns> 
+        private async void VisitorHandleClientNetworkErr(string roomId, string clientId, string name)
+        {
+            try
+            {
+                //发送通知通知用户,网络异常,todo,暂时不做处理
+            }
+            catch (Exception ex)
+            {
+                Logger.WriteLineWarn($"LiveService VisitorHandleClientNetworkErr err, name:{name}, clientId:{clientId}, roomId:{roomId}, ex:{ex}");
+            }
+        }
+
+        /// <summary>
+        /// Visitor心跳检测离线
+        /// </summary>
+        /// <param name="roomId"></param>
+        /// <param name="clientId"></param>
+        /// <param name="name"></param>
+        /// <returns></returns> 
+        private async void VisitorHandleClientLeave(string roomId, string clientId, string name)
+        {
+            try
+            {
+                //获取记录缓存
+                var queryRequest = new QueryBrowsedRecordRequest()
+                {
+                    RelevanceCode = roomId,
+                    UserCode = clientId,
+                    DisplayName = name
+                };
+                var recordInfo = await _educationDBService.GetCourseBrowsedRecordByRelevanceCodeDBAsync(queryRequest);
+
+                //存在, 且在线,则更新为不在线
+                if (!string.IsNullOrEmpty(recordInfo?.CourseBrowsedRecordCode) && recordInfo.IsOnline)
+                {
+                    var updateEntity = recordInfo.MappingTo<CourseBrowsedRecordRequest>();
+                    updateEntity.IsOnline = false;
+                    var res = await _educationDBService.SaveCourseBrowsedRecordByDBAsync(updateEntity);                    
+                }
+                else
+                {
+                    //存在且不在线或者不存在,则不处理
+                }
+            }
+            catch (Exception ex)
+            {
+                Logger.WriteLineWarn($"LiveService VisitorHandleClientLeave err, name:{name}, clientId:{clientId}, roomId:{roomId}, ex:{ex}");
+            }
         }
+        #endregion 
     }
 }

+ 2 - 0
Service/EducationService.cs

@@ -45,6 +45,7 @@ namespace WingEducationService.Service
         private ILiveRoomDBService _liveRoomDBService;
         private IMasterInteractionCenterService _masterInteractionCenterService;
         private LiveHeartRateManager _liveHeartRateManager;
+        private VisitorLiveHeartRateManager _visitorLiveHeartRateManager;
         private IPaymentDBService _paymentDBService;
         private IPaymentService _paymentService;
         private INotificationService _notificationService;
@@ -68,6 +69,7 @@ namespace WingEducationService.Service
         public EducationService()
         {
             _liveHeartRateManager = new LiveHeartRateManager(HandleClientJoined, HandleClientNetworkErr, HandleClientLeave);
+            _visitorLiveHeartRateManager = new VisitorLiveHeartRateManager(VisitorHandleClientJoined, VisitorHandleClientNetworkErr, VisitorHandleClientLeave);
         }
 
         /// <summary>