public void SendMsg(S_WF_InsTaskExec taskExec, string execComment, string nextUserIDs, string nextUserNames)
        {
            try
            {
                string msg = "";

                #region 生成消息
                string tmpl = this.MsgTmpl;
                Regex  reg  = new Regex("\\{[0-9a-zA-Z_\u4e00-\u9faf]*\\}");
                msg = reg.Replace(tmpl, (Match m) =>
                {
                    string value = m.Value.Trim('{', '}');

                    //先替换表单字段
                    if (taskExec.S_WF_InsFlow.FormDic.ContainsKey(value))
                    {
                        return(taskExec.S_WF_InsFlow.FormDic[value]);
                    }

                    switch (value)
                    {
                    case "TaskName":
                        return(taskExec.S_WF_InsTask.TaskName);

                    case "FlowName":
                        return(taskExec.S_WF_InsFlow.FlowName);

                    case "RoutingName":
                        return(this.Name);

                    case "TaskUserID":
                        return(taskExec.TaskUserID);

                    case "TaskUserName":
                        return(taskExec.TaskUserName);

                    case "ExecUserID":
                        return(taskExec.ExecUserID);

                    case "ExecUserName":
                        return(taskExec.ExecUserName);

                    case "ExecTime":
                        return(DateTime.Now.ToString());

                    case "ExecComment":
                        return(execComment);

                    case "NextUserIDs":
                        return(nextUserIDs);

                    case "NextUserNames":
                        return(nextUserNames);

                    case "NextStepCode":
                        return(this.S_WF_InsDefFlow.S_WF_InsDefStep.SingleOrDefault(c => c.ID == this.EndID).Code);

                    case "NextStepName":
                        return(this.S_WF_InsDefFlow.S_WF_InsDefStep.SingleOrDefault(c => c.ID == this.EndID).Name);

                    case "StepCode":
                        return(this.S_WF_InsDefStep.Code);

                    case "StepName":
                        return(this.S_WF_InsDefStep.Name);

                    default:
                        return(m.Value);
                    }
                });

                #endregion

                if (msg == "")
                {
                    return;
                }

                List <string> userIdList = new List <string>();

                var entities = FormulaHelper.GetEntities <WorkflowEntities>();
                var flow     = taskExec.S_WF_InsFlow;

                //给指定人发消息
                if (!string.IsNullOrEmpty(this.MsgUserIDs))
                {
                    userIdList.AddRange(this.MsgUserIDs.Split(','));
                }


                //环节任务执行人
                if (!string.IsNullOrEmpty(this.MsgUserIDsFromStepExec))
                {
                    foreach (string stepId in this.MsgUserIDsFromStepExec.Split(','))
                    {
                        var task = flow.S_WF_InsTask.LastOrDefault(c => c.InsDefStepID == stepId);
                        if (task == null)
                        {
                            throw new Exception("发送消息配置错误");
                        }
                        foreach (var item in task.S_WF_InsTaskExec)
                        {
                            userIdList.Add(item.ExecUserID);
                        }
                    }
                }
                //环节任务接收人
                if (!string.IsNullOrEmpty(this.MsgUserIDsFromStep))
                {
                    foreach (string stepId in this.MsgUserIDsFromStep.Split(','))
                    {
                        var task = flow.S_WF_InsTask.LastOrDefault(c => c.InsDefStepID == this.MsgUserIDsFromStep);
                        if (task == null)
                        {
                            throw new Exception("发送消息配置错误");
                        }
                        userIdList.AddRange(task.TaskUserIDs.Split(','));
                    }
                }
                //环节任务发送人
                if (!string.IsNullOrEmpty(this.MsgUserIDsFromStepSender))
                {
                    foreach (string stepId in this.MsgUserIDsFromStepSender.Split(','))
                    {
                        var task = flow.S_WF_InsTask.LastOrDefault(c => c.InsDefStepID == this.MsgUserIDsFromStepSender);
                        if (task == null)
                        {
                            throw new Exception("发送消息配置错误");
                        }
                        userIdList.AddRange(task.SendTaskUserIDs.Split(','));
                    }
                }
                //取自字段
                if (!string.IsNullOrEmpty(this.MsgUserIDsFromField))
                {
                    userIdList.AddRange(flow.FormDic[this.MsgUserIDsFromField].Split(','));
                }

                #region 指定组织、角色

                string orgIDs  = this.MsgOrgIDs;
                string roleIDs = this.MsgRoleIDs;

                if (string.IsNullOrEmpty(orgIDs) && !string.IsNullOrEmpty(this.MsgOrgIDsFromField))
                {
                    orgIDs = flow.FormDic[this.MsgOrgIDsFromField];
                }
                if (string.IsNullOrEmpty(roleIDs) && !string.IsNullOrEmpty(this.MsgRoleIDsFromField))
                {
                    roleIDs = flow.FormDic[this.MsgRoleIDsFromField];
                }

                if (!string.IsNullOrEmpty(this.MsgOrgIDFromUser) && string.IsNullOrEmpty(orgIDs))
                {
                    var user = FormulaHelper.GetUserInfo();
                    orgIDs = user.UserOrgID;
                }


                if (!string.IsNullOrEmpty(roleIDs))
                {
                    IRoleService roleService = FormulaHelper.GetService <IRoleService>();
                    string       userIDs     = roleService.GetUserIDsInRoles(roleIDs, orgIDs);

                    //2018-1-30 剥离项目角色选人功能
                    var prjRoleUser = PrjRoleExt.GetRoleUserIDs(roleIDs, orgIDs);
                    if (!string.IsNullOrEmpty(prjRoleUser))
                    {
                        userIDs = (prjRoleUser + "," + userIDs).Trim(',');
                    }

                    userIdList.AddRange(userIDs.Split(','));
                }
                else if (!string.IsNullOrEmpty(orgIDs))
                {
                    IOrgService orgService = FormulaHelper.GetService <IOrgService>();
                    string      userIDs    = orgService.GetUserIDsInOrgs(orgIDs);
                    userIdList.AddRange(userIDs.Split(','));
                }

                #endregion

                if (this.MsgSendToTaskUser == "1")
                {
                    userIdList.AddRange(nextUserIDs.Split(','));
                }


                IUserService    userService  = FormulaHelper.GetService <IUserService>();
                IMessageService msgService   = FormulaHelper.GetService <IMessageService>();
                string          resutUserIDs = string.Join(",", userIdList.Distinct());
                resutUserIDs = resutUserIDs.Trim(' ', ',');
                string resultUserNames = userService.GetUserNames(resutUserIDs);

                var msgType = Config.MsgType.Normal;
                if (!string.IsNullOrEmpty(this.MsgType))
                {
                    msgType = (MsgType)Enum.Parse(typeof(Config.MsgType), this.MsgType);
                }

                string url = this.S_WF_InsDefFlow.FormUrl;
                if (url.Contains("?"))
                {
                    url += "&";
                }
                else
                {
                    url += "?";
                }
                url += "FuncType=View&ID=" + taskExec.S_WF_InsFlow.FormInstanceID;

                msgService.SendMsg(msg, msg, url, "", resutUserIDs, resultUserNames, null, MsgReceiverType.UserType, msgType);
            }
            catch (Exception ex)
            {
                throw new FlowException(ex.Message);
            }
        }