Beispiel #1
0
        public ActionResult GetReadState(string accesstoken, string msg_uids)
        {
            //先验证访问权限
            var userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            var readstate = new Dictionary <string, dynamic>();

            if (!String.IsNullOrEmpty(msg_uids))
            {
                var msg_uid_list  = msg_uids.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                var redis_msgpost = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGPOST_KEY);
                redis_msgpost.Exec(db => {
                    foreach (var msg_uid in msg_uid_list)
                    {
                        var val = db.SortedSetScore($"readtime_{msg_uid}", userid);
                        if (val.HasValue)
                        {
                            readstate.Add(msg_uid.ToString(), new { readstate = 1, readtime = val.Value });
                        }
                        else
                        {
                            readstate.Add(msg_uid.ToString(), new { readstate = 0 });
                        }
                    }
                });
            }

            return(JsonContent(readstate));
        }
Beispiel #2
0
        public ActionResult GetUserState(string accesstoken, string userid)
        {
            if (String.IsNullOrEmpty(userid))
            {
                throw new BusinessException("userid不能为空");
            }

            var isOnline       = false;
            var uiState        = "visible";
            var current_userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            var redis = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_SESSION_KEY);
            var task1 = FaceHand.Common.ThreadHelp.Start(() => {
                var imuser_usertoken = $"imuser_user_token_{userid}";
                var tokenList        = redis.Exec <RedisValue[]>(db => db.SetMembers(imuser_usertoken));
                if (tokenList != null && tokenList.Length > 0)
                {
                    //能在有效的token中找到client,只能说明在2小时内登陆过,还不能证明是当前在线的,但这是证明在线一个前提
                    var clientFlags = new List <string>()
                    {
                        ConstDefined.CLIENT_FLAG_WECHAT, ConstDefined.CLIENT_FLAG_MINIPROGRAM
                    };
                    var clientContain = false;

                    foreach (var token in tokenList)
                    {
                        var client = redis.Exec <string>(db => db.HashGet($"imuser_token_user_{token}", RedisFields.SESSION_TOKEN_CLIENTFLAG));
                        if (clientFlags.Contains(client))
                        {
                            clientContain = true;
                            break;
                        }
                    }

                    if (clientContain)
                    {
                        //在判断最后一次活动时间,如果最后一次活动在5分钟以内就表示在新,否则表示不在线
                        var lasttime = redis.Exec <RedisResult>(db => db.Execute("get", $"imuser_lastactivetime_{userid}")).AsInt();
                        if (lasttime != 0 &&
                            (DateTime.Now - lasttime.AsDateTimeFromUnixTimestamp()).TotalSeconds <= Entity.ConstDefined.LASTACTIVETIME_MAX_SECONDS * 2)
                        {
                            isOnline = true;
                        }
                    }
                }
            });
            var task2 = FaceHand.Common.ThreadHelp.Start(() => {
                var re  = redis.Exec <int>(db => db.Execute("get", $"imuser_user_clientstate_{userid}").AsInt());
                uiState = (re == 1 ? "visible" : "hidden");
            });

            System.Threading.Tasks.Task.WaitAll(task1, task2);

            return(JsonContent(new
            {
                online = isOnline,
                uistate = uiState
            }));
        }
Beispiel #3
0
        public ActionResult KeepAlive(string accesstoken)
        {
            var userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            var redis = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_SESSION_KEY);

            redis.Exec(db => db.Execute("set", $"imuser_lastactivetime_{userid}", DateTime.Now.AsUnixTimestamp()));

            return(JsonContent <bool>(true));
        }
Beispiel #4
0
        public ActionResult GetUserOnlineState(string accesstoken, string userid)
        {
            //var debuglog = new System.Text.StringBuilder();
            //debuglog.AppendLine($"datetime:{DateTime.Now.ToString()}");
            //debuglog.AppendLine($"accesstoken:{accesstoken}");
            //debuglog.AppendLine($"userid:{userid}");

            var isOnline       = false;
            var current_userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            //debuglog.AppendLine($"current_userid:{current_userid}");

            var imuser_usertoken = $"imuser_user_token_{userid}";
            var redis            = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_SESSION_KEY);
            var tokenList        = redis.Exec <RedisValue[]>(db => db.SetMembers(imuser_usertoken));

            if (tokenList != null && tokenList.Length > 0)
            {
                //debuglog.AppendLine($"tokenList:{String.Join(",", tokenList.Select(item => item.ToString()))}");

                //能在有效的token中找到client,只能说明在2小时内登陆过,还不能证明是当前在线的,但这是证明在线一个前提
                var clientFlags = new List <string>()
                {
                    ConstDefined.CLIENT_FLAG_WECHAT, ConstDefined.CLIENT_FLAG_MINIPROGRAM
                };
                var clientContain = false;

                foreach (var token in tokenList)
                {
                    var client = redis.Exec <string>(db => db.HashGet($"imuser_token_user_{token}", RedisFields.SESSION_TOKEN_CLIENTFLAG));
                    //debuglog.AppendLine($"{token}_client:{client}");
                    if (clientFlags.Contains(client))
                    {
                        //debuglog.AppendLine($"{token}_client_Contains:{client}");
                        clientContain = true;
                        break;
                    }
                }

                if (clientContain)
                {
                    //在判断最后一次活动时间,如果最后一次活动在5分钟以内就表示在新,否则表示不在线
                    var lasttime = redis.Exec <RedisResult>(db => db.Execute("get", $"imuser_lastactivetime_{userid}")).AsInt();
                    if (lasttime != 0 &&
                        (DateTime.Now - lasttime.AsDateTimeFromUnixTimestamp()).TotalSeconds <= Entity.ConstDefined.LASTACTIVETIME_MAX_SECONDS * 2)
                    {
                        isOnline = true;
                    }
                }
            }
            //System.IO.File.WriteAllText(Server.MapPath("~/1.txt"),debuglog.ToString());
            return(JsonContent(isOnline));
        }
Beispiel #5
0
        public ActionResult Send(string accesstoken, MsgType msgtype, string receivers, string msgbody, string extinfo)
        {
            //验证发送者
            var userid   = IMRedisDAL.GetUserIdByAccessToken(accesstoken);
            var userinfo = IMRedisDAL.GetUserInfo(userid);

            //接收人不能为空
            if (String.IsNullOrEmpty(receivers))
            {
                throw new BusinessException("消息接收人不能为空");
            }

            //验证消息体完整性
            ValidateMsgBody(msgtype, msgbody);

            //将消息放入消息缓存,放入消息缓存的目的是为了防止消息在发送失败时可以进行多次尝试
            var msg_uid = Guid.NewGuid().ToString();
            var redis   = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGCACHE_KEY);

            redis.Exec(db =>
            {
                var key = $"msg_{msg_uid}";
                db.HashSet(key, new HashEntry[] {
                    new HashEntry(RedisFields.MSG_UID, msg_uid),
                    new HashEntry(RedisFields.MSG_TYPE, (int)msgtype),
                    new HashEntry(RedisFields.MSG_SEND_USERTYPE, Convert.ToInt32(userinfo[RedisFields.USER_TYPE])),
                    new HashEntry(RedisFields.MSG_SEND_USERID, userid),
                    new HashEntry(RedisFields.MSG_RECEIVERS, receivers.NullDefault()),
                    new HashEntry(RedisFields.MSG_BODY, msgbody.NullDefault()),
                    new HashEntry(RedisFields.MSG_TRYCOUNT, 0),
                    new HashEntry(RedisFields.MSG_TRYTIME, 0),
                    new HashEntry(RedisFields.MSG_STATE, (byte)MsgSendState.Sending),
                    new HashEntry(RedisFields.MSG_SEND_TIME, DateTime.Now.AsUnixTimestamp()),
                    new HashEntry(RedisFields.USER_EXT_INFO, extinfo.NullDefault())
                });
            });

            //通知发送新消息
            redis = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGMQ_KEY);
            redis.Exec(db => {
                var batch = db.CreateBatch();
                //放入待发送队列
                batch.ListLeftPushAsync(MessageSendStateQueue.SENDING, msg_uid, When.Always, CommandFlags.FireAndForget);
                //通过订阅通知发送器异步处理发送
                batch.PublishAsync(RedisChannelName.NEW_MESSAGE_CHANNEL_TOPULLER, ConstDefined.PUBCMD_SEND, CommandFlags.FireAndForget);

                batch.Execute();
            });

            //返回消息ID
            return(JsonContent(new { msgid = msg_uid }));
        }
Beispiel #6
0
        public ActionResult GetNotReadList(string accesstoken, string msgtypes)
        {
            //先验证访问权限
            var userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            var notReadMsgList = new List <dynamic>();

            //从未读消息列表中取出未读消息的ID
            var notread_msg_uid_arr = FaceHand.Common.ResdisExecutor.ExecCommand <RedisValue[]>(
                db => db.SortedSetRangeByRank($"notreadlist_{userid}", 0, -1), RedisConfig.REDIS_MSGPOST_KEY);

            if (notread_msg_uid_arr != null && notread_msg_uid_arr.Length > 0)
            {
                var redis = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGCACHE_KEY);

                IEnumerable <int> msgtype_list = null;
                if (!String.IsNullOrEmpty(msgtypes))
                {
                    msgtype_list = msgtypes.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(item => {
                        int t;
                        return(int.TryParse(item, out t) ? t : (int)item.AsEnum <MsgType>());
                    });
                }


                foreach (var msg_uid in notread_msg_uid_arr)
                {
                    redis.Exec(db => {
                        var msg = db.HashGetAll($"msg_{msg_uid}");
                        if (msg == null || msg.Length == 0)
                        {
                            return;
                        }

                        var msg_dict = msg.AsDictionary();
                        if (msgtype_list == null)
                        {
                            notReadMsgList.Add(msg_dict.BuildMsg());
                        }
                        else
                        {
                            if (msgtype_list.Contains(msg_dict.GetStringValue(RedisFields.MSG_TYPE).AsInt()))
                            {
                                notReadMsgList.Add(msg_dict.BuildMsg());
                            }
                        }
                    });
                }
            }

            return(JsonContent(notReadMsgList));
        }
Beispiel #7
0
        public ActionResult Get(string userid)
        {
            var userinfo = IMRedisDAL.GetUserInfo(userid);

            return(JsonContent(new {
                userid = userinfo.GetStringValue(RedisFields.USER_ID),
                username = userinfo.GetStringValue(RedisFields.USER_NAME),
                usertype = userinfo.GetStringValue(RedisFields.USER_TYPE).AsInt(),
                //loginpwd = userinfo.GetStringValue(RedisFields.USER_LOGIN_PWD),
                sex = userinfo.GetStringValue(RedisFields.USER_SEX).AsInt(),
                faceurl = userinfo.GetStringValue(RedisFields.USER_FACE_URL),
                extinfo = userinfo.GetStringValue(RedisFields.USER_EXT_INFO)
            }));
        }
Beispiel #8
0
        public ActionResult GetMessage(string accesstoken, string msgid)
        {
            //先验证访问权限
            var userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            //获取消息
            var redis = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGCACHE_KEY);
            var dict  = redis.Exec <Dictionary <string, string> >(db => db.HashGetAll($"msg_{msgid}").AsDictionary());

            if (dict == null || dict.Count == 0)
            {
                throw new BusinessException("消息未找到");
            }

            return(JsonContent(dict.BuildMsg()));
        }
Beispiel #9
0
        public ActionResult GetChatNotReadCount(string accesstoken, string chatuserids)
        {
            //先验证访问权限
            var userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            if (String.IsNullOrEmpty(chatuserids))
            {
                throw new BusinessException("chatuserids不能为空");
            }

            var dict      = new Dictionary <string, int>();
            var user_list = chatuserids.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            if (user_list.Length > 0)
            {
                var redis_msgpost = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGPOST_KEY);
                foreach (var r in user_list)
                {
                    //我发送的
                    //var k1 = $"chatnotreadcount_{userid}_{r}";
                    //我接收的
                    var k2    = $"chatnotreadcount_{r}_{userid}";
                    var count = redis_msgpost.Exec <int>(db => {
                        var count1 = 0;
                        var count2 = 0;

                        //var k1v = db.Execute("get", k1);
                        //if (k1v.HasValue() && !k1v.IsNull && !k1v.IsEmpty())
                        //    count1 = Convert.ToInt32(k1v.ToString());

                        var k2v = db.Execute("get", k2);
                        if (k2v.HasValue() && !k2v.IsNull && !k2v.IsEmpty())
                        {
                            count2 = Convert.ToInt32(k2v.ToString());
                        }

                        return(Math.Max(0, count1 + count2));
                    });

                    dict.Add(r, count);
                }
            }

            return(JsonContent(dict));
        }
Beispiel #10
0
        public ActionResult GetNotReadCount(string accesstoken, string msgtypes)
        {
            //先验证访问权限
            var userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            var redis_msgpost = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGPOST_KEY);
            var count         = redis_msgpost.Exec <int>(db => {
                var lst = db.HashGetAll($"notreadcount_{userid}");
                if (lst == null || lst.Length == 0)
                {
                    return(0);
                }

                int re = 0;
                if (String.IsNullOrEmpty(msgtypes))
                {
                    foreach (HashEntry i in lst)
                    {
                        re += i.HasValue() ? Convert.ToInt32(i.Value) : 0;
                    }
                }
                else
                {
                    var msgtypes_lst = msgtypes.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                       .Select(item => {
                        int t;
                        return(int.TryParse(item, out t) ? t : (int)item.AsEnum <MsgType>());
                    });

                    foreach (HashEntry i in lst)
                    {
                        re += (i.HasValue() && msgtypes_lst.Contains(Convert.ToInt32(i.Name.ToString())))
                            ? Convert.ToInt32(i.Value) : 0;
                    }
                }

                return(re);
            });

            return(JsonContent <int>(Math.Max(count, 0)));
        }
Beispiel #11
0
        public ActionResult GetChatList(string accesstoken, string chatuserid, int pageIndex, int pageSize)
        {
            //先验证访问权限
            var userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            if (String.IsNullOrEmpty(chatuserid))
            {
                throw new BusinessException("chatuserid不能为空");
            }

            pageIndex = Math.Max(1, pageIndex);
            pageSize  = Math.Max(5, pageSize);

            var dt = IMMySqlDAL.Instance.SelectChatMessageList(userid, chatuserid, pageIndex, pageSize);
            var re = new List <dynamic>();

            foreach (DataRow row in dt.Rows)
            {
                re.Add(row.BuildMsg());
            }

            return(JsonContent(re));
        }
Beispiel #12
0
        public ActionResult GetSendState(string accesstoken, string msgid)
        {
            //先验证访问权限
            var userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            //获取消息
            var redis = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGCACHE_KEY);
            var dict  = redis.Exec <Dictionary <string, string> >(db => db.HashGetAll($"msg_{msgid}").AsDictionary());

            if (dict == null || dict.Count == 0)
            {
                throw new BusinessException("消息未找到");
            }

            return(JsonContent(new
            {
                state = dict.GetStringValue(RedisFields.MSG_STATE).AsInt(),
                errormsg = dict.GetStringValue(RedisFields.MSG_ERROR_MSG),
                trycount = dict.GetStringValue(RedisFields.MSG_TRYCOUNT).AsInt(),
                trytime = dict.GetStringValue(RedisFields.MSG_TRYTIME).AsLong(),
                receivers_success = dict.GetStringValue(RedisFields.MSG_SUCCESS_RECEIVERS),
                receivers_failed = dict.GetStringValue(RedisFields.MSG_FAILED_RECEIVERS)
            }));
        }
Beispiel #13
0
        public ActionResult Login(string userid, string pwd, string clientFlag)
        {
            var userinfo = IMRedisDAL.GetUserInfo(userid);

            //验证登录密码
            if (userinfo.GetStringValue(RedisFields.USER_LOGIN_PWD) == pwd.Trim().GetSHA1HashCode())
            {
                var tsp   = new TimeSpan(0, 2, 0, 0, 0);
                var redis = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_SESSION_KEY);

                //查找是否存在还未过期的token,如果存在就直接返回现在的token,就不创建新的token
                var imuser_usertoken = $"imuser_user_token_{userid}";
                var oldtoken         = redis.Exec <dynamic>(db => {
                    var tokenlist = db.SetMembers(imuser_usertoken);
                    if (tokenlist != null && tokenlist.Length != 0)
                    {
                        foreach (var item in tokenlist)
                        {
                            var item_token = db.HashGetAll($"imuser_token_user_{item}").AsDictionary();
                            if (item_token.GetStringValue(RedisFields.SESSION_TOKEN_CLIENTFLAG).Equals(clientFlag, StringComparison.OrdinalIgnoreCase))
                            {
                                return(new
                                {
                                    token = item.ToString(),
                                    expiry = item_token.GetStringValue(RedisFields.SESSION_TOKEN_CREATETIME).AsDateTimeFromUnixTimestamp().Add(tsp).AsUnixTimestamp()
                                });
                            }
                        }
                    }

                    return(null);
                });

                if (oldtoken != null)
                {
                    //更新一下最后活动时间
                    var imuser_lastactivetime_userid = $"imuser_lastactivetime_{userid}";
                    redis.Exec(db => db.Execute("set", imuser_lastactivetime_userid, DateTime.Now.AsUnixTimestamp()));

                    return(JsonContent(oldtoken));
                }
                else
                {
                    //创建新的token
                    var now = DateTime.Now;
                    var nowUnixTimestamp             = now.AsUnixTimestamp();
                    var expiry                       = now.Add(tsp);
                    var token                        = $"{userid}{nowUnixTimestamp}{clientFlag}".GetSHA1HashCode();
                    var imuser_tokenuser             = $"imuser_token_user_{token}";
                    var imuser_lastactivetime_userid = $"imuser_lastactivetime_{userid}";

                    redis.Exec(db => {
                        var batch = db.CreateBatch();

                        //保存token
                        batch.HashSetAsync(imuser_tokenuser, new HashEntry[] {
                            new HashEntry(RedisFields.SESSION_TOKEN_USERID, userid),
                            new HashEntry(RedisFields.SESSION_TOKEN_CLIENTFLAG, clientFlag),
                            new HashEntry(RedisFields.SESSION_TOKEN_CREATETIME, nowUnixTimestamp)
                        });
                        batch.KeyExpireAsync(imuser_tokenuser, expiry, CommandFlags.FireAndForget);

                        //将token添加到对应userid的集合里
                        batch.SetAddAsync(imuser_usertoken, token, CommandFlags.FireAndForget);

                        //lastactivetime
                        batch.ExecuteAsync("set", imuser_lastactivetime_userid, nowUnixTimestamp);

                        batch.Execute();
                    });

                    return(JsonContent(new { token = token, expiry = expiry.AsUnixTimestamp() }));
                }
            }

            throw new BusinessException("登录密码不正确", 10022);
        }
Beispiel #14
0
        public ActionResult GetUserId(string accesstoken)
        {
            var current_userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            return(JsonContent <string>(current_userid));
        }
Beispiel #15
0
        public ActionResult UpdateReadState(string accesstoken, string msg_uids)
        {
            //先验证访问权限
            var userid = IMRedisDAL.GetUserIdByAccessToken(accesstoken);

            if (!String.IsNullOrEmpty(msg_uids))
            {
                var msg_uid_list  = msg_uids.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                var redis_msgpost = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGPOST_KEY);
                redis_msgpost.Exec(db => {
                    var chatMsgList = new List <Dictionary <string, string> >();

                    //更新整体的未读消息数量
                    var batch = db.CreateBatch();
                    foreach (string msg_uid in msg_uid_list)
                    {
                        var readtime = db.SortedSetScore($"readtime_{msg_uid}", userid);
                        if (!readtime.HasValue)
                        {
                            //从未读消息列表中移除
                            batch.SortedSetRemoveAsync($"notreadlist_{userid}", msg_uid, CommandFlags.FireAndForget);

                            //更新未读消息统计计数
                            var msg         = IMRedisDAL.GetMsg(msg_uid);
                            var msgtype_str = msg.GetStringValue(RedisFields.MSG_TYPE);
                            if (!String.IsNullOrEmpty(msgtype_str))
                            {
                                var count_rv = db.HashGet($"notreadcount_{userid}", msgtype_str);
                                if (count_rv.HasValue && !count_rv.IsNullOrEmpty)
                                {
                                    long count_lng = 0;
                                    if (count_rv.TryParse(out count_lng))
                                    {
                                        if (count_lng > 0)
                                        {
                                            batch.HashDecrementAsync($"notreadcount_{userid}", msgtype_str, 1, CommandFlags.FireAndForget);
                                        }
                                        else
                                        {
                                            batch.HashSetAsync($"notreadcount_{userid}", msgtype_str, 0);
                                        }
                                    }
                                }

                                //如果是聊天消息,更新聊天消息的阅读情况
                                var msgtype_int = Convert.ToInt32(msgtype_str);
                                if (msgtype_int < 60)
                                {
                                    chatMsgList.Add(msg);
                                }
                            }

                            //更新阅读时间
                            batch.SortedSetAddAsync($"readtime_{msg_uid}",
                                                    new SortedSetEntry[] { new SortedSetEntry(userid, DateTime.Now.AsUnixTimestamp()) }, CommandFlags.FireAndForget);
                        }
                    }

                    batch.Execute();

                    //更新聊天消息和接收者之间的的未读数量
                    if (chatMsgList.Count > 0)
                    {
                        var batch2 = db.CreateBatch();
                        foreach (var msg in chatMsgList)
                        {
                            var msg_senderid = msg.GetStringValue(RedisFields.MSG_SEND_USERID);
                            var receivers    = msg.GetStringValue(RedisFields.MSG_SUCCESS_RECEIVERS);
                            if (!String.IsNullOrEmpty(receivers))
                            {
                                var receivers_list = receivers.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                                foreach (var receiver_userid in receivers_list)
                                {
                                    var k        = $"chatnotreadcount_{msg_senderid}_{receiver_userid}";
                                    var count_rv = db.Execute("get", k);
                                    if (count_rv.HasValue() && !count_rv.IsNull && !count_rv.IsEmpty())
                                    {
                                        //这个key必须大于0才执行递减操作
                                        //否则不变
                                        if (count_rv.AsInt() > 0)
                                        {
                                            batch2.ExecuteAsync("DECRBY", k, 1);
                                        }
                                        else
                                        {
                                            batch2.ExecuteAsync("set", k, 0);
                                        }
                                    }
                                    else
                                    {
                                        //这个key还不存在
                                        batch2.ExecuteAsync("set", k, 0);
                                    }
                                }
                            }
                        }
                        batch2.Execute();
                    }
                });
            }

            return(JsonContent(true));
        }
Beispiel #16
0
        //投送消息
        private static void SendMsg(string msg_uid, bool isRemoveFromMQ)
        {
            //获取消息以验证消息的有效性
            var msg = IMRedisDAL.GetMsg(msg_uid);

            if (msg == null || msg.Count == 0)
            {
                throw new BusinessException($"message {msg_uid} not found");
            }

            var state = msg.GetStringValue(RedisFields.MSG_STATE).AsEnum <MsgSendState>();

            if (state != MsgSendState.Sending)
            {
                //消息ID被取到,但是又不是正在发送的状态
                //说明消息已被其它进程处理,这时应该从消息队列中删除这个ID
                var redis_msmq = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGMQ_KEY);
                redis_msmq.Exec(db => db.ListRemove(MessageSendStateQueue.SENDING, msg_uid, 0, CommandFlags.FireAndForget));
            }
            else
            {
                //验证接收人,把有效的人和无效的人分离出来
                var receiver_valid   = new List <string>();
                var receiver_invalid = new List <string>();
                ValidateReceiver(msg[RedisFields.MSG_RECEIVERS], ref receiver_valid, ref receiver_invalid);

                //没有有效接收人,消息发送失败
                if (receiver_valid.Count == 0)
                {
                    //更新消息发送状态为失败
                    FaceHand.Common.ResdisExecutor.ExecCommand(db => db.HashSet($"msg_{msg_uid}", new HashEntry[] {
                        //更新发送失败的人
                        new HashEntry(RedisFields.MSG_FAILED_RECEIVERS, String.Join(",", receiver_invalid)),
                        //更新消息发送状态
                        new HashEntry(RedisFields.MSG_STATE, (int)MsgSendState.Failed),
                        //更新消息发送状态
                        new HashEntry(RedisFields.MSG_ERROR_MSG, "available receivers count is zero")
                    }, CommandFlags.FireAndForget), RedisConfig.REDIS_MSGCACHE_KEY);

                    //从队列中删除消息
                    if (isRemoveFromMQ)
                    {
                        var redis_msmq = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGMQ_KEY);
                        redis_msmq.Exec(db => db.ListRemove(MessageSendStateQueue.SENDING, msg_uid, 0, CommandFlags.FireAndForget));
                    }

                    //抛出错误,以便控制台能看到发送失败
                    throw new BusinessException($"available receivers count is zero");
                }
                else
                {
                    //投送消息
                    var redis_msgpost = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGPOST_KEY);
                    redis_msgpost.Exec(db => {
                        var batch           = db.CreateBatch();
                        var msg_sendtime    = msg.GetStringValue(RedisFields.MSG_SEND_TIME).AsLong();
                        var msg_senderid    = msg.GetStringValue(RedisFields.MSG_SEND_USERID);
                        var msg_type        = (int)msg.GetStringValue(RedisFields.MSG_TYPE).AsEnum <MsgType>();
                        var isChatMsg       = msg_type < 60;
                        var now             = DateTime.Now;
                        var nowUnixTimestmp = now.AsUnixTimestamp();
                        foreach (var receiver_userid in receiver_valid)
                        {
                            //更新未读消息数,未读消息数按消息类型分别存
                            batch.HashIncrementAsync($"notreadcount_{receiver_userid}", msg[RedisFields.MSG_TYPE], 1D, CommandFlags.FireAndForget);

                            //将消息ID放入对应人的未读消息清单
                            batch.SortedSetAddAsync($"notreadlist_{receiver_userid}", msg_uid, nowUnixTimestmp, When.Always, CommandFlags.FireAndForget);

                            //单独统计聊天消息的数量
                            if (isChatMsg)
                            {
                                var chatnotreadcount_k = $"chatnotreadcount_{msg_senderid}_{receiver_userid}";
                                var chatnotreadcount   = db.Execute("GET", chatnotreadcount_k).AsInt();

                                if (chatnotreadcount < 0)
                                {
                                    batch.ExecuteAsync("SET", 1);
                                }
                                else
                                {
                                    batch.ExecuteAsync("INCRBY", chatnotreadcount_k, 1D);
                                }
                            }
                        }

                        batch.Execute();
                    });

                    //更新消息发送状态
                    FaceHand.Common.ResdisExecutor.ExecCommand(db => db.HashSet($"msg_{msg_uid}", new HashEntry[] {
                        //更新发送成功的人
                        new HashEntry(RedisFields.MSG_SUCCESS_RECEIVERS, String.Join(",", receiver_valid)),

                        //更新发送失败的人
                        new HashEntry(RedisFields.MSG_FAILED_RECEIVERS, String.Join(",", receiver_invalid)),

                        //更新消息发送状态
                        new HashEntry(RedisFields.MSG_STATE, (int)MsgSendState.Success)
                    }, CommandFlags.FireAndForget), RedisConfig.REDIS_MSGCACHE_KEY);

                    var redis_msmq = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGMQ_KEY);

                    //从队列中删除消息
                    if (isRemoveFromMQ)
                    {
                        redis_msmq.Exec(db => db.ListRemove(MessageSendStateQueue.SENDING, msg_uid, 0, CommandFlags.FireAndForget));
                    }

                    //通过订阅器通知客户端有新消息
                    if (receiver_valid.Count > 0)
                    {
                        Task.Factory.StartNew(() =>
                        {
                            try
                            {
                                redis_msmq.Exec(db => db.Publish(
                                                    RedisChannelName.NEW_MESSAGE_CHANNEL_TOCLIENT,
                                                    $"{msg_uid},{String.Join(",", receiver_valid)}", //通知client manager
                                                    CommandFlags.FireAndForget));
                            }
                            catch (Exception ex)
                            {
                                WriteErrorLog($"notify {msg_uid} to client failed. {ex.Message}");
                            }
                        });
                    }

                    //将消息持久化到数据库
                    Task.Factory.StartNew(() =>
                    {
                        try
                        {
                            using (var context = new DataContext(MysqlDbConfig.IMCONN))
                            {
                                IMMySqlDAL.Instance.InsertMsg(msg, receiver_valid);
                            }
                        }
                        catch (Exception ex)
                        {
                            WriteErrorLog($"save message {msg_uid} to database failed. {ex.Message}");
                        }
                    });

                    //如果是应用消息,还要考虑是否通知到企业微信端
                    if (msg[RedisFields.MSG_TYPE].AsEnum <MsgType>() == MsgType.system_app)
                    {
                    }

                    WriteLog($"post message success。msg_uid:{msg_uid}");
                }
            }
        }