コード例 #1
0
        /// <summary>
        /// 获取历史聊天记录
        /// </summary>
        /// <param name="roomHashid">房间哈希ID</param>
        /// <param name="entryTime">进入房间的时间</param>
        /// <param name="startIndex">此次获取的聊天历史记录的开始序号</param>
        /// <returns>表示获取聊天记录的任务</returns>
        public async Task <List <ChatHistoryDto> > GetChatHistoryAsync(string roomHashid, long entryTime, int startIndex)
        {
            var roomId = HashidsHelper.Decode(roomHashid);
            var query  = await _dbContext.ChatHistory
                         .Where(msg => msg.RoomId == roomId &&
                                msg.UnixTimeMilliseconds < entryTime)
                         .OrderByDescending(msg => msg.UnixTimeMilliseconds)
                         .Skip(startIndex)
                         .Take(20)
                         .ToListAsync();

            var history = new List <ChatHistoryDto>();

            foreach (var msg in query)
            {
                var data = msg.Message;
                if (msg.IsPicture.Value)
                {
                    var path = Path.Combine(_picturesDirectory, msg.RoomId.ToString(), "thumbnails",
                                            $"{msg.UserId}_{msg.UnixTimeMilliseconds}.jpg");
                    data = Convert.ToBase64String(await File.ReadAllBytesAsync(path));
                }
                history.Add(new ChatHistoryDto
                {
                    UserId    = HashidsHelper.Encode(msg.UserId),
                    Username  = msg.Username,
                    Data      = data,
                    Timestamp = msg.UnixTimeMilliseconds,
                    IsPicture = msg.IsPicture.Value
                });
            }
            return(history);
        }
コード例 #2
0
        public ActionResult GetCallsAsJson(string contactId)
        {
            var calls = _callService.GetAllByContactIdWithUsers(HashidsHelper.Decrypt(contactId));
            var vm    = calls.Select(x => x.MapTo <CallVm>());

            return(Json(new { success = true, data = vm }));
        }
コード例 #3
0
        /// <summary>
        /// 获取用户上一次登录时所在的房间ID
        /// </summary>
        /// <param name="uid">用户ID</param>
        /// <returns>表示获取用户上一次登录时所在的房间ID的任务</returns>
        public async Task <string> GetPreviousRoomIdAsync(int uid)
        {
            var connection = await _dbContext.Connection
                             .Where(conn => conn.UserId == uid && !conn.IsDeleted.Value)
                             .FirstOrDefaultAsync();


            // 连接信息没找到
            if (connection == null)
            {
                return(null);
            }

            var room = await _dbContext.ChatRoom.FindAsync(connection.RoomId);

            if (room == null)
            {
                // 如果房间没找到,但连接信息却找到了,说明之前的数据有问题
                // 删除连接信息
                _dbContext.Remove(connection);
                await _dbContext.SaveChangesAsync();

                return(null);
            }
            return(HashidsHelper.Encode(connection.RoomId));
        }
コード例 #4
0
        public ActionResult GetCallAsJson(string id)
        {
            var call = _callService.GetById(HashidsHelper.Decrypt(id));
            var vm   = call.MapTo <CallVm>();

            return(Json(new { success = true, data = vm }));
        }
コード例 #5
0
        public ActionResult Profile(string id)
        {
            var user = _userService.GetByIdWithRoles(HashidsHelper.Decrypt(id));
            var vm   = user.MapTo <UserVm>();

            return(View(vm));
        }
コード例 #6
0
        public async Task DeleteRoomAsync(string roomHashid)
        {
            var roomId = HashidsHelper.Decode(roomHashid);

            var room = await _dbContext.ChatRoom.FindAsync(roomId);

            // 当前房间已经被删除则不做处理
            if (room == null)
            {
                return;
            }

            Roles userRole = (Roles)Convert.ToInt32(
                Context.User.FindFirst(ClaimTypes.Role).Value);
            int userId = HashidsHelper.Decode(Context.User.FindFirst("uid").Value);

            // 为了安全做一次判断
            // 如果不是房主或者管理员则无权进行删除处理
            if (userId != room.OwnerId && userRole != Roles.Admin)
            {
                return;
            }

            using (var tran = await _dbContext.Database.BeginTransactionAsync())
            {
                try
                {
                    // 删除房间
                    _dbContext.ChatRoom.Remove(room);

                    await _dbContext.SaveChangesAsync();

                    // 删除历史聊天信息
                    await _dbContext.Database.ExecuteSqlCommandAsync(
                        $"delete from chat_history where room_id = {room.Id}");

                    // 删除已经离线的用户的连接信息
                    await _dbContext.Database.ExecuteSqlCommandAsync(
                        $"delete from connection where room_id = {room.Id} and is_online = false");

                    // 删除存放聊天图片的文件夹
                    var path = Path.Combine(_picturesDirectory, roomId.ToString());
                    if (Directory.Exists(path))
                    {
                        Directory.Delete(path, true);
                    }

                    tran.Commit();

                    await Clients.Group(roomHashid).InvokeAsync("onRoomDeleted", _msg.GetMessage("E008"));
                }
                catch
                {
                    tran.Rollback();
                }
            }
        }
コード例 #7
0
        public async Task <JsonResult> RefreshTokenAsync()
        {
            string hashid = User.FindFirst("uid").Value;

            return(Json(new
            {
                AccessToken = await _tokenAuthService.RefreshTokenAsync(HashidsHelper.Decode(hashid))
            }));
        }
コード例 #8
0
        /// <summary>
        /// 创建房间
        /// </summary>
        /// <param name="uid">用户ID</param>
        /// <param name="roomDto">用户输入的用于创建房间的信息</param>
        /// <returns>表示异步创建房间的任务,如果创建失败则返回错误信息</returns>
        public async Task <ChatRoomCreateResponseDto> CreateRoomAsync(int uid, ChatRoomDto roomDto)
        {
            // 防止用户打开多个窗口创建房间
            var error = await ApplyForCreatingRoomAsync(uid);

            if (!string.IsNullOrEmpty(error))
            {
                return(new ChatRoomCreateResponseDto
                {
                    Error = error,
                    CloseModalIfError = true
                });
            }
            try
            {
                var room = new ChatRoom
                {
                    OwnerId     = uid,
                    Name        = roomDto.Name,
                    MaxUsers    = roomDto.MaxUsers,
                    IsEncrypted = roomDto.IsEncrypted,
                    IsPermanent = roomDto.IsPermanent,
                    IsHidden    = roomDto.IsHidden,
                    AllowGuest  = roomDto.AllowGuest
                };

                // 如果房间被加密
                if (roomDto.IsEncrypted)
                {
                    Guid salt = Guid.NewGuid();
                    room.Salt         = salt.ToString();
                    room.PasswordHash = PasswordHelper.GeneratePasswordHash(roomDto.Password, room.Salt);
                }

                _dbContext.ChatRoom.Add(room);
                await _dbContext.SaveChangesAsync();

                return(new ChatRoomCreateResponseDto
                {
                    RoomId = HashidsHelper.Encode(room.Id)
                });
            }
            catch (Exception)
            {
                // 因为是多线程,任然可能发生异常
                // 房间名重复
                return(new ChatRoomCreateResponseDto
                {
                    Error = _msg.GetMessage("E003", "房间名"),
                    CloseModalIfError = false
                });
            }
        }
コード例 #9
0
            public CallVm MapToCallVm(Call call)
            {
                // Call sınıfında User virtual bir property olduğu için database sorgusunda açıkca include edilmezse
                // call nesnesinde bu property tanımı olmaz ve buna erişmeye çalışıldığında hata fırlatır.
                string userFullName;

                try { userFullName = call.User?.Fullname; }
                catch { userFullName = null; }

                return(call == null ? null : new CallVm {
                    Id = HashidsHelper.Encrypt(call.Id), ContactId = HashidsHelper.Encrypt(call.ContactId), UserId = HashidsHelper.Encrypt(call.UserId), UserFullname = userFullName, IsAccess = call.IsAccess, Date = call.Date
                });
            }
コード例 #10
0
        /// <summary>
        /// 作为游客登录
        /// </summary>
        /// <returns>异步获取Token的任务</returns>
        public async Task <AccessTokenResponseDto> LoginAsGuestAsync()
        {
            var guestId  = Convert.ToInt32(DateTime.Now.ToString("ddHHmmss") + new Random().Next(0, 9));
            var username = $"游客{HttpUtility.UrlDecode(HashidsHelper.Encode(guestId))}";

            var user = new User
            {
                Id       = guestId,
                Username = username,
                RoleId   = (int)Roles.Guest
            };

            return(new AccessTokenResponseDto
            {
                AccessToken = await _tokenAuthService.GenerateAccessTokenAsync(user),
                RefreshToken = await _tokenAuthService.GenerateRefreshTokenAsync(user)
            });
        }
コード例 #11
0
        public async Task RemoveMemberAsync(string roomHashid, string memberHashid)
        {
            int roomId = HashidsHelper.Decode(roomHashid);
            int userId = HashidsHelper.Decode(Context.User.FindFirst("uid").Value);

            // 判断用户是否为房主,只有房主才有删除成员的权限
            var count = await _dbContext.ChatRoom
                        .CountAsync(room => room.Id == roomId && room.OwnerId == userId);

            if (count == 0)
            {
                return;
            }

            int memberId = HashidsHelper.Decode(memberHashid);

            var connection = await _dbContext.Connection
                             .FirstOrDefaultAsync(conn => conn.UserId == memberId);

            // 从数据库中删除
            _dbContext.Remove(connection);

            await _dbContext.SaveChangesAsync();

            // 通知被删除的用户
            await Clients.Client(connection.ConnectionId)
            .InvokeAsync("onRemoved",
                         _msg.GetMessage("I005", "你"));

            // 通知房间内其他用户
            await Clients.Group(roomHashid)
            .InvokeAsync("receiveSystemMessage",
                         _msg.GetMessage("I005", connection.Username));

            // 刷新成员列表
            await RefreshMemberListAsync(roomHashid, roomId);

            await Groups.RemoveAsync(connection.ConnectionId, roomHashid);
        }
コード例 #12
0
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="roomHashid">房间哈希ID</param>
        /// <param name="message">消息</param>
        /// <returns>表示发送消息的任务</returns>
        public async Task SendMessageAsync(string roomHashid, string message)
        {
            var userHashId           = Context.User.FindFirst("uid").Value;
            var username             = HttpUtility.UrlDecode(Context.User.Identity.Name);
            var unixTimeMilliseconds = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds();

            await Clients.Group(roomHashid)
            .InvokeAsync("receiveMessage", userHashId, username, message, unixTimeMilliseconds, false);

            // 将消息保存到数据库
            var history = new ChatHistory
            {
                RoomId = HashidsHelper.Decode(roomHashid),
                UserId = HashidsHelper.Decode(userHashId),
                UnixTimeMilliseconds = unixTimeMilliseconds,
                Username             = username,
                Message = message
            };

            _dbContext.ChatHistory.Add(history);
            await _dbContext.SaveChangesAsync();
        }
コード例 #13
0
        /// <summary>
        /// 离开房间
        /// </summary>
        /// <param name="roomHashid">房间哈希ID</param>
        /// <returns>表示离开房间的任务</returns>
        public async Task LeaveRoomAsync(string roomHashid)
        {
            var roomId = HashidsHelper.Decode(roomHashid);
            int userId = HashidsHelper.Decode(Context.User.FindFirst("uid").Value);

            var count = await _dbContext
                        .Connection
                        .CountAsync(conn => conn.RoomId == roomId &&
                                    conn.UserId == userId &&
                                    conn.ConnectionId == Context.ConnectionId);

            // 如果用户开了多个窗口的话,这边可能会出问题
            // 或者用户被移出房间
            if (count == 0)
            {
                return;
            }

            // 通知同一房间里其他人该用户已经离开房间
            await Clients.Group(roomHashid).InvokeAsync(
                "receiveSystemMessage",
                _msg.GetMessage("I004",
                                HttpUtility.UrlDecode(Context.User.Identity.Name)));

            var room = await _dbContext.ChatRoom.FirstOrDefaultAsync(x => x.Id == roomId);

            // 如果该房间已经被删除的话
            if (room == null)
            {
                return;
            }

            // 如果不是永久房,则房主离开,就意味着房间要被解散
            if (room.OwnerId == userId && !room.IsPermanent.Value)
            {
                await DeleteRoomAsync(roomHashid);
            }
        }
コード例 #14
0
        /// <summary>
        /// 刷新房间成员列表信息
        /// </summary>
        /// <param name="roomHashid">房间哈希ID</param>
        /// <param name="roomId">房间ID</param>
        /// <returns>表示刷新房间成员列表信息的任务</returns>
        private async Task RefreshMemberListAsync(string roomHashid, int roomId)
        {
            var ownerId = await _dbContext.ChatRoom
                          .Where(room => room.Id == roomId)
                          .Select(room => room.OwnerId).FirstOrDefaultAsync();

            // order by is_owner desc, is_online desc, create_time
            var list = await _dbContext.Connection
                       .Where(conn => conn.RoomId == roomId)
                       .OrderBy(conn => conn.CreateTime)
                       .Select(conn => new ChatRoomMemberDto
            {
                UserId   = HashidsHelper.Encode(conn.UserId),
                Username = conn.Username,
                IsOnline = conn.IsOnline.Value,
                IsOwner  = conn.UserId == ownerId
            })
                       .OrderByDescending(x => x.IsOwner)
                       .ThenByDescending(x => x.IsOnline)
                       .ToListAsync();

            await Clients.Group(roomHashid).InvokeAsync("refreshMemberList", list);
        }
コード例 #15
0
 public ContactVm MapToContactVm(Contact contact)
 {
     return(contact == null ? null : new ContactVm {
         Id = HashidsHelper.Encrypt(contact.Id), Phone = contact.Phone, CallReason = contact.CallReason, CallResult = contact.CallResult, Date = contact.Date
     });
 }
コード例 #16
0
        /// <summary>
        /// 获取房间列表
        /// </summary>
        /// <param name="keyword">关键词</param>
        /// <param name="page">页码</param>
        /// <param name="uid">用户ID</param>
        /// <param name="role">用户角色</param>
        /// <returns>表示异步获取房间列表的任务,如果创建失败则返回错误信息</returns>
        public async Task <ChatRoomSearchResponseDto> GetRoomList(string keyword, int page, int uid, Roles role)
        {
            // 房主和管理员可以像正常房间一样检索隐藏房间
            IQueryable <ChatRoom> query = null;

            if (string.IsNullOrEmpty(keyword))
            {
                query = from room in _dbContext.ChatRoom
                        where !room.IsHidden.Value ||
                        room.OwnerId == uid || role == Roles.Admin
                        select room;
            }
            else if (role == Roles.Admin)
            {
                query = from room in _dbContext.ChatRoom
                        where room.Name.Contains(keyword) ||
                        room.Owner.Username.Contains(keyword)
                        select room;
            }
            else
            {
                // 因为用三元表达式会报错,所以这里暂时这么写
                query = from room in _dbContext.ChatRoom
                        where
                        ((room.IsHidden.Value && room.OwnerId != uid) && room.Name == keyword)
                        ||
                        ((!room.IsHidden.Value || room.OwnerId == uid) &&
                         (room.Name.Contains(keyword) || room.Owner.Username.Contains(keyword)))
                        select room;
            }

            int count = await query.CountAsync();

            int totalPages = (int)Math.Ceiling(((decimal)count / 10));

            page = Math.Min(page, totalPages);

            ChatRoomSearchResponseDto chatRoomListDto = new ChatRoomSearchResponseDto();

            // 小于0的判断是为了防止不正当数据
            if (page <= 0)
            {
                return(chatRoomListDto);
            }

            chatRoomListDto.ChatRoomList = await query
                                           .OrderByDescending(room => room.CreateTime)
                                           .Skip((page - 1) * 10)
                                           .Take(10)
                                           .Select(room => new ChatRoomDto
            {
                Id           = HashidsHelper.Encode(room.Id),
                Name         = room.Name,
                MaxUsers     = room.MaxUsers,
                CurrentUsers = room.CurrentUsers,
                OwnerName    = room.Owner.Username,
                IsEncrypted  = room.IsEncrypted.Value,
                IsHidden     = room.IsHidden.Value,
                AllowGuest   = room.AllowGuest.Value,
                CreateTime   = new DateTimeOffset(room.CreateTime).ToUnixTimeMilliseconds()
            }).ToListAsync();

            chatRoomListDto.Pagination = new PaginationDto
            {
                CurrentPage = page,
                TotalPages  = totalPages,
                TotalItems  = count
            };

            return(chatRoomListDto);
        }
コード例 #17
0
        public async Task <string> GetRegistrationTimeAsync()
        {
            string hashid = User.FindFirst("uid").Value;

            return(await _userProfileService.GetRegistrationTimeAsync(HashidsHelper.Decode(hashid)));
        }
コード例 #18
0
 public Call MapToCall(CallVm vm)
 {
     return(vm == null ? null : new Call {
         Id = HashidsHelper.Decrypt(vm.Id), ContactId = HashidsHelper.Decrypt(vm.ContactId), Date = vm.Date, IsAccess = vm.IsAccess
     });
 }
コード例 #19
0
 public Contact MapToContact(ContactVm vm)
 {
     return(vm == null ? null : new Contact {
         Id = HashidsHelper.Decrypt(vm.Id), Phone = vm.Phone, CallReason = vm.CallReason, CallResult = vm.CallResult, Date = vm.Date
     });
 }
コード例 #20
0
        public ActionResult GetContactAsJson(string id)
        {
            var vm = _contactService.GetById(HashidsHelper.Decrypt(id)).MapTo <ContactVm>();

            return(Json(new { success = true, data = vm }, JsonRequestBehavior.AllowGet));
        }
コード例 #21
0
 public ActionResult Delete(string id)
 {
     _contactService.DeleteById(HashidsHelper.Decrypt(id));
     return(Json(new { success = true, message = C.Message.Success }));
 }
コード例 #22
0
        public ActionResult Create(ContactVm model)
        {
            var contact = _contactService.Add(model.MapTo <Contact>());

            return(Json(new { success = true, message = C.Message.Success, contactId = HashidsHelper.Encrypt(contact.Id) }));
        }
コード例 #23
0
        public async Task <AccessTokenResponseDto> UpdatePasswordAsync([FromBody] UserUpdatePasswordRequestDto passwordDto)
        {
            string hashid = User.FindFirst("uid").Value;

            return(await _userProfileService.UpdatePasswordAsync(HashidsHelper.Decode(hashid), passwordDto.NewPassword));
        }
コード例 #24
0
        /// <summary>
        /// 加入房间
        /// </summary>
        /// <param name="roomHashid">房间哈希ID</param>
        /// <returns>表示加入房间的任务,返回房间名和加入该房间的时间</returns>
        public async Task <ChatRoomInitialDisplayDto> JoinRoomAsync(string roomHashid)
        {
            var roomId   = HashidsHelper.Decode(roomHashid);
            var userId   = HashidsHelper.Decode(Context.User.FindFirst("uid").Value);
            var username = HttpUtility.UrlDecode(Context.User.Identity.Name);

            // 因为用户可能会尝试用一个账号同时登陆两个房间
            // 这里查询条件不加上房间ID,前一个房间内的用户会被提示在别处登录
            var connection = await _dbContext.Connection
                             .FirstOrDefaultAsync(x => x.UserId == userId);

            string msgId             = null;
            string connIdToBeRemoved = null;

            if (connection == null)
            {
                // 第一次进入该房间
                msgId = "I001";
                _dbContext.Add(new Connection
                {
                    RoomId       = roomId,
                    UserId       = userId,
                    Username     = username,
                    ConnectionId = Context.ConnectionId
                });
            }
            else
            {
                // 如果用户同时打开两个窗口
                if (connection.IsOnline.Value)
                {
                    connIdToBeRemoved = connection.ConnectionId;
                }
                // 重连的情况下
                msgId = "I003";
                connection.IsOnline     = true;
                connection.ConnectionId = Context.ConnectionId;
                // 只保留一条记录
                _dbContext.Update(connection);
            }

            await _dbContext.SaveChangesAsync();

            // 将用户添加到分组
            await Groups.AddAsync(Context.ConnectionId, roomHashid);

            // 只加载加入房间前的消息,避免消息重复显示
            long unixTimeMilliseconds = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds();

            // 显示欢迎用户加入房间的消息
            await Clients.Group(roomHashid).InvokeAsync(
                "receiveSystemMessage",
                _msg.GetMessage(msgId, username));

            if (!string.IsNullOrEmpty(connIdToBeRemoved))
            {
                // 前一个窗口显示消息,告知账号已经在其他地方登陆
                await Clients.Client(connIdToBeRemoved).InvokeAsync("onDuplicateLogin");

                await Groups.RemoveAsync(connIdToBeRemoved, roomHashid);
            }

            // 显示用户列表
            await RefreshMemberListAsync(roomHashid, roomId);

            var room = await _dbContext.ChatRoom
                       .Where(r => r.Id == roomId)
                       .Select(r => new { r.Name, r.OwnerId })
                       .FirstOrDefaultAsync();

            return(new ChatRoomInitialDisplayDto
            {
                OwnerId = HashidsHelper.Encode(room.OwnerId),
                RoomName = room.Name,
                EntryTime = unixTimeMilliseconds
            });
        }
コード例 #25
0
        /// <summary>
        /// 发送图片
        /// </summary>
        /// <param name="roomHashid">房间哈希ID</param>
        /// <param name="base64String">图片数据对应的base64String字符串</param>
        /// <returns>表示发送图片的任务</returns>
        public async Task SendPictureAsync(string roomHashid, string base64String)
        {
            var roomId               = HashidsHelper.Decode(roomHashid);
            var userHashId           = Context.User.FindFirst("uid").Value;
            var userId               = HashidsHelper.Decode(userHashId);
            var username             = HttpUtility.UrlDecode(Context.User.Identity.Name);
            var unixTimeMilliseconds = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds();

            string dirOriginal  = Path.Combine(_picturesDirectory, roomId.ToString(), "originals");
            string dirThumbnail = Path.Combine(_picturesDirectory, roomId.ToString(), "thumbnails");

            Directory.CreateDirectory(dirOriginal);
            Directory.CreateDirectory(dirThumbnail);

            // 获取图片数据
            var bytes = Convert.FromBase64String(base64String);
            var img   = Image.FromStream(new MemoryStream(bytes));

            var isGif     = _imageService.IsGif(img);
            var extension = isGif ? ".gif" : ".jpg";

            var pathOriginal  = Path.Combine(dirOriginal, $"{userId}_{unixTimeMilliseconds}{extension}");
            var pathThumbnail = Path.Combine(dirThumbnail, $"{userId}_{unixTimeMilliseconds}.jpg");

            // 保存图片
            if (isGif)
            {
                // 如果是gif则直接保存
                await File.WriteAllBytesAsync(pathOriginal, bytes);
            }
            else
            {
                // 不是gif则进行适当压缩后保存
                _imageService.SaveAsThumbnailImage(bytes, pathOriginal, 1920, 1080, 95);
            }

            // 保存缩略图
            _imageService.SaveAsThumbnailImage(bytes, pathThumbnail, 250, 250, 95);

            // 会自动进行base64的转换
            await Clients.Group(roomHashid)
            .InvokeAsync("receiveMessage", userHashId, username,
                         await File.ReadAllBytesAsync(pathThumbnail), unixTimeMilliseconds, true);

            // 将消息保存到数据库
            var history = new ChatHistory
            {
                RoomId = roomId,
                UserId = userId,
                UnixTimeMilliseconds = unixTimeMilliseconds,
                Username             = username,
                IsPicture            = true
            };

            _dbContext.ChatHistory.Add(history);
            await _dbContext.SaveChangesAsync();

            // 后台执行图片压缩任务
            // 不等待
            if (isGif)
            {
                Task noWarning = _imageService.TinifyAsync(pathThumbnail);
            }
            else
            {
                Task noWarning = _imageService.TinifyAsync(pathOriginal, pathThumbnail);
            }
        }
コード例 #26
0
        /// <summary>
        /// 失去连接
        /// </summary>
        /// <param name="exception">异常信息</param>
        /// <returns>表示处理失去连接的任务</returns>
        public async override Task OnDisconnectedAsync(Exception exception)
        {
            var connenction = await _dbContext
                              .Connection.FirstOrDefaultAsync(conn => conn.ConnectionId == Context.ConnectionId);

            // 如果用户开了多个窗口的话,这边可能会出问题
            // 或者用户被移出房间
            if (connenction == null)
            {
                return;
            }

            var roomHashid = HashidsHelper.Encode(connenction.RoomId);

            // 检索房主ID
            var ownerId = await _dbContext.ChatRoom
                          .Where(r => r.Id == connenction.RoomId)
                          .Select(r => r.OwnerId)
                          .FirstOrDefaultAsync();

            // 如果当前房间已经被删除,ownerId会得到0
            // 或者该连接被标记为删除,即用户退出聊天室
            if (ownerId == 0 || connenction.IsDeleted.Value)
            {
                // 删除连接信息
                _dbContext.Connection.Remove(connenction);
            }
            else
            {
                // 以下为用户失去连接的情况

                // 消息ID
                string msgId;
                Roles  userRole = (Roles)Convert.ToInt32(Context.User.FindFirst(ClaimTypes.Role).Value);
                // 如果是游客,直接删除链接信息
                if (userRole == Roles.Guest)
                {
                    // 游客直接通知离开房间
                    msgId = "I004";
                    _dbContext.Connection.Remove(connenction);
                }
                else
                {
                    // 通知同一房间里其他人该用户已经离线的ID
                    msgId = "I002";
                    connenction.IsOnline = false;
                    _dbContext.Update(connenction);
                }
                // 通知同一房间里其他人该用户已经离线或游客离开房间
                await Clients.Group(roomHashid).InvokeAsync(
                    "receiveSystemMessage",
                    _msg.GetMessage(msgId,
                                    HttpUtility.UrlDecode(Context.User.Identity.Name)));
            }

            await _dbContext.SaveChangesAsync();

            // 刷新用户列表
            await RefreshMemberListAsync(roomHashid, connenction.RoomId);

            // 从分组中删除当前连接
            // 注意 移除必须是在最后做,否则会报错
            await Groups.RemoveAsync(Context.ConnectionId, roomHashid);

            await base.OnDisconnectedAsync(exception);
        }