關注微信公衆號推送消息

微信關注二維碼的主要開發思路:

第一步,向用戶展示要掃的二維碼:通過提前申請的appid和祕鑰去拿token,通過token再去微信服務上拿要關注的圖片二維碼,將二維碼展示在你係統中。

第二步,用戶掃碼: 用戶掃碼時會向微信發信息,並且微信會回調你的自己的服務,需要你將關注的用戶信息保存下來; 

第三步,給用戶發送信息:通過用戶標識,找到第二步中保存下來的用戶,給微信接口髮指定的模版信息,關注的用戶會接收到信息,並且會回調我們的服務。

一、獲取微信關注二維碼

1、在獲取二維碼之前要通過企業申請的appid和祕鑰拿到token

 /// <summary>
        /// 獲得微信AccessToken
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpGet("GetAccessToken")]
        public string GetAccessToken()
        {
            return _cache.GetOrCreate(CacheKeys.WeChatAccessToken, entry =>
            {
                string appId = HPDHttpContext.AppSettings.Value.WeChatOAConfig.AppID;
                string secret = HPDHttpContext.AppSettings.Value.WeChatOAConfig.AppSecret;
                string strUrl = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appId, secret);
                var token = HttpHelper.Get<WeChatToken>(strUrl);
                entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(token.expires_in - 180);
                return token.access_token;
            });
        }

2、在1中拿到token後用它獲取二維碼

 /// <summary>
        /// 獲得微信關注二維碼
        /// </summary>
        /// <returns>Base64字符串圖片</returns>
        [AllowAnonymous]
        [HttpPost("GetWeChatFollowQRCode")]
        public string GetWeChatFollowQRCode(WeChatQRCodeRequest request)
        {
            string token = GetAccessToken();
            //創建二維碼ticket
            string strUrl = string.Format("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={0}", token);
            var data = new
            {
                expire_seconds = 60,
                action_name = "QR_STR_SCENE",
                action_info = new
                {
                    scene = new
                    {
                        scene_str = JsonUtil.JsonSerializeObject(request)
                    }
                }
            };
            string result = HttpHelper.PostJson(strUrl, JsonUtil.JsonSerializeObject(data));
            var objResult = JsonConvert.DeserializeObject(result) as JObject;
            string ticket = (string)objResult["ticket"];
            //通過ticket換取二維碼
            string qrCodeUrl = string.Format("https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={0}", ticket);
            byte[] qrCode = HttpHelper.GetByte(qrCodeUrl);
            //using (FileStream fs = new FileStream("C:\\Users\\houzhipeng\\Desktop\\1.jpg", FileMode.Create, FileAccess.Write))
            //{
            //    fs.Write(qrCode, 0, qrCode.Length);
            //    fs.Flush();
            //    fs.Close();
            //}
            return Convert.ToBase64String(qrCode);
        }

3、獲取到二維碼圖片後在頁面上顯示

$('#img').attr('src', `data:;base64,${url}`);

 

二、用戶掃碼

1、用戶掃碼後會回調我們自己開發的接口,我們將用戶信息保存到自己開發的數據庫中

 /// <summary>
        /// 接收微信消息
        /// </summary>
        [AllowAnonymous]
        [HttpPost("WeChatMessage")]
        public ActionResult WeChatMessage(string msg_signature, string signature, string timestamp, string nonce, string token = null)
        {
            token = HPDHttpContext.AppSettings.Value.WeChatOAConfig.Token;
            if (!CheckSignature.Check(signature, timestamp, nonce, token))
            {
                return Content("參數錯誤!");
            }
            try
            {
                string encodingAESKey = HPDHttpContext.AppSettings.Value.WeChatOAConfig.EncodingAESKey;
                string appId = HPDHttpContext.AppSettings.Value.WeChatOAConfig.AppID;
                string content = "";

                using (StreamReader reader = new StreamReader(HttpContext.Request.Body))
                {
                    string s = reader.ReadToEndAsync().Result;
                    using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(s)))
                    {
                        content = Encoding.UTF8.GetString(memoryStream.ToArray());
                    }
                }
                LogUtil.WriteLog(content);
                XmlDocument document = new XmlDocument();
                if (!string.IsNullOrWhiteSpace(msg_signature)) // 消息加密模式
                {
                    string decryptMsg = string.Empty;
                    var wxBizMsgCrypt = new WXBizMsgCrypt(token, encodingAESKey, appId);
                    int decryptResult = wxBizMsgCrypt.DecryptMsg(msg_signature, timestamp, nonce, content, ref decryptMsg);
                    if (decryptResult == 0 && !string.IsNullOrWhiteSpace(decryptMsg))
                    {
                        LogUtil.WriteLog(decryptMsg);
                        document.LoadXml(decryptMsg);
                        var messageProcess = CommonMessageFactory.CreateMessageProcess(document);
                        string returnResult = messageProcess.ExecuteResult();
                        LogUtil.WriteLog(returnResult);
                        return Content(returnResult);
                    }
                }
                else // 消息未加密處理
                {
                    document.LoadXml(content);
                    var messageProcess = CommonMessageFactory.CreateMessageProcess(document);
                    string returnResult = messageProcess.ExecuteResult();
                    return Content(returnResult);
                }
            }
            catch (Exception ex)
            {
                LogUtil.WriteLog("接收消息並處理和返回相應結果異常:" + ex);
            }
            return Content("");
        }

在註冊微信公衆號時還會要求有一個可以調通的服務,從而驗證服務器配置是否正常,代碼如下:

 /// <summary>
        /// 服務器配置驗證
        /// </summary>
        /// <param name="signature"></param>
        /// <param name="timestamp"></param>
        /// <param name="nonce"></param>
        /// <param name="echostr"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpGet("WeChatMessage")]
        public ActionResult WeChatMessage(string signature, string timestamp, string nonce, string echostr)
        {
            string token = HPDHttpContext.AppSettings.Value.WeChatOAConfig.Token;
            if (CheckSignature.Check(signature, timestamp, nonce, token))
            {
                return Content(echostr); //返回隨機字符串則表示驗證通過
            }
            else
            {
                return Content("");
            }
        }

 

 

2、獲取微信消息的相關方法中的類

檢查簽名:

 public class CheckSignature
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="signature"></param>
        /// <param name="timestamp"></param>
        /// <param name="nonce"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static bool Check(string signature, string timestamp, string nonce, string token)
        {
            return signature == GetSignature(timestamp, nonce, token);
        }

        /// <summary>
        /// 返回正確的簽名
        /// </summary>
        /// <param name="timestamp"></param>
        /// <param name="nonce"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static string GetSignature(string timestamp, string nonce, string token)
        {
            var arr = new[] { token, timestamp, nonce }.OrderBy(z => z).ToArray();
            var arrString = string.Join("", arr);
            //var enText = FormsAuthentication.HashPasswordForStoringInConfigFile(arrString, "SHA1");//使用System.Web.Security程序集
            var sha1 = SHA1.Create();
            var sha1Arr = sha1.ComputeHash(Encoding.UTF8.GetBytes(arrString));
            StringBuilder enText = new StringBuilder();
            foreach (var b in sha1Arr)
            {
                enText.AppendFormat("{0:x2}", b);
            }
            return enText.ToString();
        }
    }

加密:

 public class WXBizMsgCrypt
    {
        string m_sToken;
        string m_sEncodingAESKey;
        string m_sAppID;
        enum WXBizMsgCryptErrorCode
        {
            WXBizMsgCrypt_OK = 0,
            WXBizMsgCrypt_ValidateSignature_Error = -40001,
            WXBizMsgCrypt_ParseXml_Error = -40002,
            WXBizMsgCrypt_ComputeSignature_Error = -40003,
            WXBizMsgCrypt_IllegalAesKey = -40004,
            WXBizMsgCrypt_ValidateAppid_Error = -40005,
            WXBizMsgCrypt_EncryptAES_Error = -40006,
            WXBizMsgCrypt_DecryptAES_Error = -40007,
            WXBizMsgCrypt_IllegalBuffer = -40008,
            WXBizMsgCrypt_EncodeBase64_Error = -40009,
            WXBizMsgCrypt_DecodeBase64_Error = -40010
        };

        //構造函數
        // @param sToken: 公衆平臺上,開發者設置的Token
        // @param sEncodingAESKey: 公衆平臺上,開發者設置的EncodingAESKey
        // @param sAppID: 公衆帳號的appid
        public WXBizMsgCrypt(string sToken, string sEncodingAESKey, string sAppID)
        {
            m_sToken = sToken;
            m_sAppID = sAppID;
            m_sEncodingAESKey = sEncodingAESKey;
        }


        // 檢驗消息的真實性,並且獲取解密後的明文
        // @param sMsgSignature: 簽名串,對應URL參數的msg_signature
        // @param sTimeStamp: 時間戳,對應URL參數的timestamp
        // @param sNonce: 隨機串,對應URL參數的nonce
        // @param sPostData: 密文,對應POST請求的數據
        // @param sMsg: 解密後的原文,當return返回0時有效
        // @return: 成功0,失敗返回對應的錯誤碼
        public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg)
        {
            if (m_sEncodingAESKey.Length!=43)
            {
                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;
            }
            XmlDocument doc = new XmlDocument();
            XmlNode root;
            string sEncryptMsg;
            try
            {
                doc.LoadXml(sPostData);
                root = doc.FirstChild;
                sEncryptMsg = root["Encrypt"].InnerText;
            }
            catch (Exception)
            {
                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ParseXml_Error;
            }
            //verify signature
            int ret = 0;
            ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature);
            if (ret != 0)
                return ret;
            //decrypt
            string cpid = "";
            try
            {
                sMsg = Cryptography.AES_decrypt(sEncryptMsg, m_sEncodingAESKey, ref cpid);
            }
            catch (FormatException)
            {
                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecodeBase64_Error;
            }
            catch (Exception)
            {
                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error;
            }
            if (cpid != m_sAppID)
                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateAppid_Error;
            return 0;
        }

        //將企業號回覆用戶的消息加密打包
        // @param sReplyMsg: 企業號待回覆用戶的消息,xml格式的字符串
        // @param sTimeStamp: 時間戳,可以自己生成,也可以用URL參數的timestamp
        // @param sNonce: 隨機串,可以自己生成,也可以用URL參數的nonce
        // @param sEncryptMsg: 加密後的可以直接回複用戶的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
        //                        當return返回0時有效
        // return:成功0,失敗返回對應的錯誤碼
        public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg)
        {
            if (m_sEncodingAESKey.Length!=43)
            {
                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;
            }
            string raw = "";
            try
            {
                raw = Cryptography.AES_encrypt(sReplyMsg, m_sEncodingAESKey, m_sAppID);
            }
            catch (Exception)
            {
                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_EncryptAES_Error;
            }
            string MsgSigature = "";
            int ret = 0;
            ret = GenarateSinature(m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature);
            if (0 != ret)
                return ret;
            sEncryptMsg = "";

            string EncryptLabelHead = "<Encrypt><![CDATA[";
            string EncryptLabelTail = "]]></Encrypt>";
            string MsgSigLabelHead = "<MsgSignature><![CDATA[";
            string MsgSigLabelTail = "]]></MsgSignature>";
            string TimeStampLabelHead = "<TimeStamp><![CDATA[";
            string TimeStampLabelTail = "]]></TimeStamp>";
            string NonceLabelHead = "<Nonce><![CDATA[";
            string NonceLabelTail = "]]></Nonce>";
            sEncryptMsg = sEncryptMsg + "<xml>" + EncryptLabelHead + raw + EncryptLabelTail;
            sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail;
            sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail;
            sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail;
            sEncryptMsg += "</xml>";
            return 0;
        }

        public class DictionarySort : System.Collections.IComparer
        {
            public int Compare(object oLeft, object oRight)
            {
                string sLeft = oLeft as string;
                string sRight = oRight as string;
                int iLeftLength = sLeft.Length;
                int iRightLength = sRight.Length;
                int index = 0;
                while (index < iLeftLength && index < iRightLength)
                {
                    if (sLeft[index] < sRight[index])
                        return -1;
                    else if (sLeft[index] > sRight[index])
                        return 1;
                    else
                        index++;
                }
                return iLeftLength - iRightLength;

            }
        }
        //Verify Signature
        private static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture)
        {
            string hash = "";
            int ret = 0;
            ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash);
            if (ret != 0)
                return ret;
            //System.Console.WriteLine(hash);
            if (hash == sSigture)
                return 0;
            else
            {
                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateSignature_Error;
            }
        }

        public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt ,ref string sMsgSignature)
        {
            ArrayList AL = new ArrayList();
            AL.Add(sToken);
            AL.Add(sTimeStamp);
            AL.Add(sNonce);
            AL.Add(sMsgEncrypt);
            AL.Sort(new DictionarySort());
            string raw = "";
            for (int i = 0; i < AL.Count; ++i)
            {
                raw += AL[i];
            }

            SHA1 sha;
            ASCIIEncoding enc;
            string hash = "";
            try
            {
                sha = new SHA1CryptoServiceProvider();
                enc = new ASCIIEncoding();
                byte[] dataToHash = enc.GetBytes(raw);
                byte[] dataHashed = sha.ComputeHash(dataToHash);
                hash = BitConverter.ToString(dataHashed).Replace("-", "");
                hash = hash.ToLower();
            }
            catch (Exception)
            {
                return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ComputeSignature_Error;
            }
            sMsgSignature = hash;
            return 0;
        }
    }

消息處理工廠類

public class CommonMessageFactory
    {
        public static ICommonMessageProcess CreateMessageProcess(XmlDocument document)
        {
            if (document == null)
                return null;
            ICommonMessageProcess messageProcess = null;
            XmlElement rootElement = document.DocumentElement;
            string msgType = rootElement.SelectSingleNode("MsgType").InnerText;
            switch (msgType)
            {
                case "text":
                    messageProcess = new TextMessageProcess();
                    break;
                case "image":
                    break;
                case "voice":
                    break;
                case "video":
                    break;
                case "shortvideo":
                    break;
                case "location":
                    break;
                case "link":
                    break;
                case "event":
                    switch (rootElement.SelectSingleNode("Event").InnerText)
                    {
                        case "subscribe":
                            messageProcess = new SubscribeMessageProcess(document.InnerXml);
                            break;
                        case "unsubscribe":
                            break;
                        case "SCAN":
                            messageProcess = new ScanMessageProcess(document.InnerXml);
                            break;
                        case "LOCATION":
                            break;
                        case "CLICK":
                            break;
                        case "VIEW":
                            break;
                        case "TEMPLATESENDJOBFINISH":
                            messageProcess = new TemplateSendResultMessageProcess(document.InnerXml);
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    //result = "沒有識別的類型消息:" + xmlElement.SelectSingleNode("MsgType").InnerText;
                    //WriteLog(result);
                    break;
             }
            return messageProcess;
        }
    }

在消息處理工廠類中有一個非常重要的子類就是掃碼後回調的信息處理類:ScanMessageProcess,在此類中可以看到,當我們掃碼後信息保存到數據庫中

public class ScanMessageProcess : ICommonMessageProcess
    {
        private SubscribeOrScanXmlModel _scanXmlModel;

        public ScanMessageProcess(string xmlString)
        {
            _scanXmlModel = XmlUtil.XmlDeserialize<SubscribeOrScanXmlModel>(xmlString);
        }

        public string ExecuteResult()
        {
            var qrCodeParam = JsonUtil.JsonDeserialize<WeChatQRCodeRequest>(_scanXmlModel.EventKey);

            string content = "";
            var msgInfo = ComUserUtil.SaveUserInfo(_scanXmlModel, qrCodeParam);
            if (msgInfo.State)
            {
                //content = "掃碼成功!場景值:" + _scanXmlModel.EventKey + ",OPEN_ID:" + _scanXmlModel.FromUserName;
                content = "掃碼成功!我們將及時爲您推送訂單,證照等消息。";
            }
            else
            {
                content = "掃碼失敗!場景值:" + _scanXmlModel.EventKey + "  " + msgInfo.Message;
            }

            TextXml xml = new TextXml();
            xml.FromUserName = _scanXmlModel.ToUserName;
            xml.ToUserName = _scanXmlModel.FromUserName;
            xml.CreateTime = DateTime.Now.Ticks;
            xml.Content = content;
            string xmlString = xml.ToXml();
            return xmlString;
        }
    }

另外,有一個發送信息後的回調類:TemplateSendResultMessageProcess

public class TemplateSendResultMessageProcess : ICommonMessageProcess
    {
        TemplateSendResultXmlModel _xmlModel = null;
        public TemplateSendResultMessageProcess(string xmlString)
        {
            _xmlModel = XmlUtil.XmlDeserialize<TemplateSendResultXmlModel>(xmlString);
        }

        public string ExecuteResult()
        {
            var msgInfo = ComMessageUtil.UpdateWeChatMPMessageState(_xmlModel.MsgID, _xmlModel.Status);
            //調用通知接口

            if (_xmlModel.Status == "success")
            {
                return "OK";
            }
            else 
            {
                
            }
            return "NO";
        }
    }
 [XmlRoot(ElementName = "xml")]
    public class TemplateSendResultXmlModel : BaseRequestXml
    {
        public string Event { get; set; }
        public string MsgID { get; set; }
        public string Status { get; set; }
    }

掃碼還可能回調的事件,對應的處理類是:SubscribeMessageProcess

/// <summary>
    /// 用戶掃碼關注消息處理
    /// </summary>
    public class SubscribeMessageProcess : ICommonMessageProcess
    {
        private SubscribeOrScanXmlModel _scanXmlModel;

        public SubscribeMessageProcess(string xmlString)
        {
            _scanXmlModel = XmlUtil.XmlDeserialize<SubscribeOrScanXmlModel>(xmlString);
        }

        public string ExecuteResult()
        {
            if (_scanXmlModel.EventKey.StartsWith("qrscene_"))
            {
                _scanXmlModel.EventKey = _scanXmlModel.EventKey.Substring(8);
            }
            var qrCodeParam = JsonUtil.JsonDeserialize<WeChatQRCodeRequest>(_scanXmlModel.EventKey);
            string content = "";
            var msgInfo = ComUserUtil.SaveUserInfo(_scanXmlModel, qrCodeParam);
            if (msgInfo.State)
            {
                //content = "關注成功!場景值:" + _scanXmlModel.EventKey;

                content = "感謝您的關注!我們將及時爲您推送訂單,證照等消息。";
            }
            else
            {
                content = "關注失敗!場景值:" + _scanXmlModel.EventKey + "  " + msgInfo.Message;
            }
            TextXml xml = new TextXml();
            xml.FromUserName = _scanXmlModel.ToUserName;
            xml.ToUserName = _scanXmlModel.FromUserName;
            xml.CreateTime = DateTime.Now.Ticks;
            xml.Content = content;
            string xmlString = xml.ToXml();
            return xmlString;
        }
    }

xml處理工具類:xmlUtil

 public class XmlUtil
    {
        /// <summary>
        /// 對象轉換爲XML
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string XmlSerialize<T>(T obj)
        {
            try
            {
                XmlSerializer ser = new XmlSerializer(obj.GetType());
                MemoryStream ms = new MemoryStream();
                ser.Serialize(ms, obj);
                ms.Close();
                return Encoding.UTF8.GetString(ms.ToArray());
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 轉換XML爲對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xmlString"></param>
        /// <returns></returns>
        public static T XmlDeserialize<T>(string xmlString)
        {
            try
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
                Stream xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlString));
                XmlReader xmlReader = XmlReader.Create(xmlStream);
                Object obj = xmlSerializer.Deserialize(xmlReader);

                xmlReader.Close();
                xmlStream.Close();
                return (T)obj;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }

json處理工具類:

 public static class JsonUtil
    {
        /// <summary>
        /// 使用Newtonsoft.Json序列化對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string JsonSerializeObject(object obj)
        {
            return Newtonsoft.Json.JsonConvert.SerializeObject(obj);
        }

        /// <summary>
        /// 使用Newtonsoft.Json反序列化對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="text"></param>
        /// <returns></returns>
        public static List<T> JsonDeserializeList<T>(string text)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(text);
        }
        /// <summary>
        /// 使用Newtonsoft.Json反序列化對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="text"></param>
        /// <returns></returns>
        public static T JsonDeserialize<T>(string text)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(text);
        }
        
        /// <summary>
        /// 讀取JSON文件
        /// </summary>
        /// <param name="key">JSON文件中的key值</param>
        /// <returns>JSON文件中的value值</returns>
        public static string ReadJsonFile(string jsonPath, string key)
        {
            if (!File.Exists(jsonPath))
                throw new Exception(jsonPath + "未找到文件!");
            using (StreamReader file = File.OpenText(jsonPath))
            {
                using (Newtonsoft.Json.JsonTextReader reader = new Newtonsoft.Json.JsonTextReader(file))
                {
                    Newtonsoft.Json.Linq.JObject o = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.Linq.JToken.ReadFrom(reader);
                    var value = o[key].ToString();
                    return value;
                }
            }
        }
        /// <summary>
        /// 讀取JSON文件
        /// </summary>
        /// <param name="jsonPath"></param>
        /// <returns></returns>
        public static string ReadJsonFile(string jsonPath)
        {
            if (!File.Exists(jsonPath))
                throw new Exception(jsonPath + "未找到文件!");
            string strJson = "";
            using (StreamReader file = File.OpenText(jsonPath))
            {
                strJson = file.ReadToEnd();
            }
            return strJson;
        }
    }

 

三、向用戶發送模版消息

/// <summary>
        /// 發送模板消息
        /// </summary>
        /// <param name="requestParam"></param>
        [HttpPost("SendWeChatMPTemplateMessage")]
        public string SendWeChatMPTemplateMessage(WeChatTemplateMessageRequest requestParam)
        {
            BaseResponse response = new BaseResponse();
            var msgInfo = RequestHelper.NotnullValidate<WeChatTemplateMessageRequest>(requestParam);
            if (!msgInfo.State)
            {
                response.ResultCode = "-1";
                response.ResultMessage = msgInfo.Message;
                return JsonUtil.JsonSerializeObject(response);
            }



            const string WeChatHttpUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}";
            string token = GetAccessToken();

            string templateId = string.Empty;
            if (!string.IsNullOrEmpty(requestParam.TemplateId))
            {
                templateId = requestParam.TemplateId;
            }
            else
            {
                templateId = GetTemplateId(requestParam.TemplateType);
            }

            string openId = requestParam.OpenId;
            if (string.IsNullOrEmpty(openId))
            {
                var dataAccess = JZTHttpContext.DataAccess;
                var user = dataAccess.GetByParam<COM_USER>(new { ORGAN_ID = requestParam.OrganId, USER_ID = requestParam.UserId });
                if (user == null)
                {
                    response.ResultCode = "-1";
                    response.ResultMessage = "無此用戶";
                    return JsonUtil.JsonSerializeObject(response);
                }
                openId = user.OPEN_ID;
            }

            Data data = new Data();
            data.first = new First() { value = requestParam.First };
            data.keyword1 = new Keyword1 { value = requestParam.Keyword1 };
            data.keyword2 = new Keyword2 { value = requestParam.Keyword2 };
            data.keyword3 = new Keyword3 { value = requestParam.Keyword3 };
            data.keyword4 = new Keyword4 { value = requestParam.Keyword4 };
            data.keyword5 = new Keyword5 { value = requestParam.Keyword5 };
            data.remark = new Remark { value = requestParam.Remark };

            TemplateMessageModel messageModel = new TemplateMessageModel();
            messageModel.touser = openId;
            messageModel.template_id = templateId;
            messageModel.url = requestParam.CallbackUrl;
            messageModel.data = data;

            string jsonData = JsonUtil.JsonSerializeObject(messageModel);
            string url = string.Format(WeChatHttpUrl, token);
            string returnResult = HttpHelper.PostJson(url, jsonData);

            LogUtil.WriteLog(returnResult);

            var tmResponse = JsonUtil.JsonDeserialize<TemplateMessageResponse>(returnResult);
            if (tmResponse.errcode == "0")
            {
                msgInfo = ComMessageUtil.SaveWeChatMPMessage(requestParam, tmResponse.msgid);
                if (!msgInfo.State)
                {
                    response.ResultCode = "-1";
                    response.ResultMessage = msgInfo.Message;
                }
            }
            else
            {
                response.ResultCode = "-1";
                response.ResultMessage = tmResponse.errmsg;
            }
            return JsonUtil.JsonSerializeObject(response);
        }
 private string GetTemplateId(int messageType)
        {
            string templateId = "";
            switch (messageType)
            {
                case (int)模板消息類型.關注消息:
                    templateId = HPDHttpContext.AppSettings.Value.WeChatOAConfig.GZTemplateId;
                    break;
                case (int)模板消息類型.掃碼消息:
                    templateId = HPDHttpContext.AppSettings.Value.WeChatOAConfig.SMTemplateId;
                    break;
                case (int)模板消息類型.證照到期提醒:
                    templateId = HPDHttpContext.AppSettings.Value.WeChatOAConfig.ZZDQTemplateId;
                    break;
                case (int)模板消息類型.訂單生成通知:
                    templateId = HPDHttpContext.AppSettings.Value.WeChatOAConfig.DDSCTemplateId;
                    break;
                case (int)模板消息類型.訂單確認通知:
                    templateId = HPDHttpContext.AppSettings.Value.WeChatOAConfig.DDQRTemplateId;
                    break;
                case (int)模板消息類型.單據審覈結果通知:
                    templateId = HPDHttpContext.AppSettings.Value.WeChatOAConfig.DDSHTemplateId;
                    break;
                case (int)模板消息類型.訂單配送提醒:
                    templateId = HPDHttpContext.AppSettings.Value.WeChatOAConfig.DDPSTemplateId;
                    break;
                default:
                    break;
            }
            return templateId;
        }

 

四、客戶端的使用

 1、獲取二維碼

 /// <summary>
        /// 獲取二維碼
        /// </summary>
        /// <returns></returns>
        public async Task<ResponseResult<string>> GetQrcode()
        {
         
            var json = (new
            {
                organId = "CX2023071900000001C",
                userId = CurrentUser.Id,
            }).ToJson();
            var recordRes = await _commonSpeedHttpClient.PostAsync($"{_appSettingConfig.WeixinServiceUrl}GetWeChatFollowQRCode", json);
            return ResponseResult<string>.Success("ok", recordRes);

        }

2、發送模版消息

 /// <summary>
        /// 發送模板消息
        /// </summary>
        /// <returns></returns>
        [RemoteService(IsMetadataEnabled = true)]
        [HttpPost]
        public async Task<ResponseResult> SendTemplateMsg(SendTemplateMsgInput param)
        {
            if (param == null)
            {
                return ResponseResult.Fail("參數不能爲空");
            }

     

            var templateId = EnumHelper.GetDisplayAttributeDescription(typeof(TemplateTypeEnum), param.TemplateType);
            if (string.IsNullOrEmpty(templateId))
            {
                _logger.LogInformation($"找不到對應的模板id");
                return ResponseResult<string>.Fail("找不到對應的模板id");
            }
            var messageId = Guid.NewGuid().ToString();
            var json = (new
            {
                OrganId = "CX2023071900000001C",
                TemplateId = templateId,
                UserId = param.UserId,
                MessageId = messageId,
                First = param.Data.ContainsKey("first") ? param.Data["first"] : string.Empty,
                Keyword1 = param.Data.ContainsKey("keyword1") ? param.Data["keyword1"] : string.Empty,
                Keyword2 = param.Data.ContainsKey("keyword2") ? param.Data["keyword2"] : string.Empty,
                Keyword3 = param.Data.ContainsKey("keyword3") ? param.Data["keyword3"] : string.Empty,
                Keyword4 = param.Data.ContainsKey("keyword4") ? param.Data["keyword4"] : string.Empty,
                Keyword5 = param.Data.ContainsKey("keyword5") ? param.Data["keyword5"] : string.Empty,
                Remark = param.Data.ContainsKey("remark") ? param.Data["remark"] : string.Empty,
            }).ToJson();

            _logger.LogInformation($"發送消息請求參數:{json}");

            var recordRes = await _commonSpeedHttpClient.PostAsync($"{_appSettingConfig.WeixinServiceUrl}SendWeChatMPTemplateMessage", json);
            _logger.LogInformation($"發送模板消息返回:{recordRes}");

            return ResponseResult.Success();

        }

模版枚舉類:

 public enum TemplateTypeEnum
    {
        /// <summary>
        /// 新訂單通知
        /// </summary>
        [Display(Name = "新訂單通知", Description = "M81T0LJjYvT94OzCzEBALxUVOTt2tkHD-iInhmclabg")]
        OrderAuth = 1,
        /// <summary>
        /// 訂單確認通知
        /// </summary>
        [Display(Name = "訂單確認通知", Description = "upxpP6h1g6KWcOJ6IErnt9CpudwTDoL6c58Dc57RnvQ")]
        OrderConfirm = 2,
        

    }

枚舉工具類:

/// <summary>
    /// 枚舉幫助類
    /// </summary>
    public class EnumHelper
    {
        public static List<Type> EnumTypes = null;

        static EnumHelper()
        {
            EnumTypes = Assembly.GetExecutingAssembly().GetTypes().Where(p => p.BaseType == typeof(System.Enum)).ToList(); 
        }


        /// <summary>
        /// 通過枚舉名稱獲取字典信息
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static List<EnumItem> GetEnumItemListByEnumName(string name)
        {
            var list = new List<EnumItem>();
            var lowerName = name.ToLower();
            if (!lowerName.EndsWith("enum"))
            {
                lowerName += "enum";
            }

            Type type = EnumTypes.FirstOrDefault(p => p.Name.ToLower() == lowerName || p.Name.ToLower() == name.ToLower());
            if (type == null)
            {
                throw new Exception("枚舉類型不存在");
            }
        
            var members = type.GetMembers();

            foreach (var member in members)
            {
                DisplayAttribute attribute = Attribute.GetCustomAttribute(member, typeof(DisplayAttribute)) as DisplayAttribute;

                if (attribute != null)
                {
                    var returnValues = new EnumItem
                    {
                        Key = member.Name,
                        Value = Convert.ToInt32(System.Enum.Parse(type, member.Name)).ToString()
                    };

                    if (attribute.Name != null)
                    {
                        returnValues.Desc = attribute.Name;
                    }
                    else if (attribute.Description != null)
                    {
                        returnValues.Desc = attribute.Description;
                    }
                    else
                    {
                        returnValues.Desc = member.Name;
                    }

                    list.Add(returnValues);

                }
            }

            return list;
        }

        /// <summary>
        /// 通過枚舉名稱獲取字典信息
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static Dictionary<string, string> GetEnumDataListByEnumName(string name)
        { 

            var lowerName = name.ToLower();
            if (!lowerName.EndsWith("enum"))
            {
                lowerName += "enum";
            }

            Type type = EnumTypes.FirstOrDefault(p => p.Name.ToLower() == lowerName || p.Name.ToLower() == name.ToLower());
            if (type == null)
            {
                throw new Exception("枚舉類型不存在");
            }
            var dic = GetCommonEnumMembers(type);

            return dic;
        }

        public static Dictionary<string, string> GetCommonEnumMembers(Type type)
        {
            var returnValues = new Dictionary<string, string>();
            var members = type.GetMembers();

            foreach (var member in members)
            {
                DisplayAttribute attribute = Attribute.GetCustomAttribute(member, typeof(DisplayAttribute)) as DisplayAttribute;

                if (attribute != null)
                {
                    if (attribute.Name != null)
                    {
                        returnValues.Add(Convert.ToInt32(System.Enum.Parse(type, member.Name)).ToString(), attribute.Name);
                    }
                    else if (attribute.Description != null)
                    {
                        returnValues.Add(Convert.ToInt32(System.Enum.Parse(type, member.Name)).ToString(), attribute.Description);
                    }
                    else
                    {
                        returnValues.Add(Convert.ToInt32(System.Enum.Parse(type, member.Name)).ToString(), member.Name);
                    }
                    
                }
            }

            return returnValues;
        }

        /// <summary>
        /// 獲取Display名
        /// </summary>
        /// <param name="type"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string GetDisplayAttributeName(Type type, object value)
        {

            string name = System.Enum.GetName(type, value);
            if (name == null)
            {
                return null;
            }
            FieldInfo field = type.GetField(name);
            DisplayAttribute attribute = Attribute.GetCustomAttribute(field, typeof(DisplayAttribute)) as DisplayAttribute;

            return attribute == null ? null : attribute.Name;
        }
        /// <summary>
        /// 獲取display描述
        /// </summary>
        /// <param name="type"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string GetDisplayAttributeDescription(Type type, object value)
        {
            string name = System.Enum.GetName(type, value);
            if (name == null)
            {
                return null;
            }
            FieldInfo field = type.GetField(name);
            DisplayAttribute attribute = Attribute.GetCustomAttribute(field, typeof(DisplayAttribute)) as DisplayAttribute;

            return attribute == null ? null : attribute.Description;
        }

        /// <summary>
        /// 獲取枚舉字典信息
        /// key 枚舉值 value 枚舉DisPlay
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        static public Dictionary<string, string> GetEnumMembers(Type type)
        {
            var returnValues = new Dictionary<string, string>();
            var members = type.GetMembers();

            foreach (var member in members)
            {
                DisplayAttribute attribute = Attribute.GetCustomAttribute(member, typeof(DisplayAttribute)) as DisplayAttribute;

                if (attribute != null)
                {
                    returnValues.Add(Convert.ToInt32(System.Enum.Parse(type, member.Name)).ToString(), attribute.Name);
                }
            }

            return returnValues;
        }

        /// <summary>
        /// 獲取枚舉字典信息
        /// key 枚舉值 value 枚舉DisPlay
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        static public List<KeyValuePair<string, string>> GetEnumMembersKeyValuePair(Type type)
        {
            var returnValues = new List<KeyValuePair<string, string>>();
            var members = type.GetMembers();

            foreach (var member in members)
            {
                DisplayAttribute attribute = Attribute.GetCustomAttribute(member, typeof(DisplayAttribute)) as DisplayAttribute;

                if (attribute != null)
                {
                    returnValues.Add(new KeyValuePair<string, string>(Convert.ToInt32(System.Enum.Parse(type, member.Name)).ToString(), attribute.Name));
                }
            }

            return returnValues;
        }

        /// <summary>
        /// 獲取枚舉字典信息
        /// key 枚舉值 value 枚舉DisPlay
        /// </summary>
        /// <param name="type"></param>
        /// <param name="isShowAll">0:返回枚舉類型,1:返回帶全部都枚舉類型</param>
        /// <returns></returns>
        static public Dictionary<string, string> GetEnumMembersList(Type type,int isShowAll=0)
        {
            var returnValues = new Dictionary<string, string>();
            if (isShowAll == 1)
            {
                returnValues.Add("0", "--請選擇--");
            }
            var members = type.GetMembers();
            foreach (var member in members)
            {
                DisplayAttribute attribute = Attribute.GetCustomAttribute(member, typeof(DisplayAttribute)) as DisplayAttribute;

                if (attribute != null)
                {
                    returnValues.Add(Convert.ToInt32(System.Enum.Parse(type, member.Name)).ToString(), attribute.Name);
                }
            }
            return returnValues;
        }

        /// <summary>
        /// 獲取枚舉字典信息
        /// key 枚舉屬性名 value 枚舉DisPlay
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        static public Dictionary<string, string> GetEnumKeyValuePair(Type type)
        {
            var returnValues = new Dictionary<string, string>();
            var members = type.GetMembers();

            foreach (var member in members)
            {
                DisplayAttribute attribute = Attribute.GetCustomAttribute(member, typeof(DisplayAttribute)) as DisplayAttribute;

                if (attribute != null)
                {
                    returnValues.Add(member.Name, attribute.Name);
                }
            }

            return returnValues;
        }

        /// <summary>
        /// 是否合法枚舉
        /// </summary>
        /// <returns></returns>
        public static bool IsValidEnumValue(Type type, int value)
        {
            var list = GetEnumMembers(type);
            if (list != null && list.Any())
            {
                return list.ContainsKey(value.ToString());
            }
            return false;
        }


        /// <summary>
        /// 將枚舉轉爲集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static List<EnumEntity> EnumToList<T>()
        {
            List<EnumEntity> list = new List<EnumEntity>();

            foreach (var e in System.Enum.GetValues(typeof(T)))
            {
                EnumEntity m = new EnumEntity();
                object[] objArr = e.GetType().GetField(e.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), true);
                if (objArr != null && objArr.Length > 0)
                {
                    DescriptionAttribute da = objArr[0] as DescriptionAttribute;
                    m.Desction = da.Description;
                }
                m.EnumValue = Convert.ToInt32(e);
                m.EnumName = e.ToString();
                list.Add(m);
            }
            return list;
        }


        /// <summary>
        /// 枚舉實體
        /// </summary>
        public class EnumEntity
        {
            /// <summary>  
            /// 枚舉的描述  
            /// </summary>  
            public string Desction { set; get; }

            /// <summary>  
            /// 枚舉名稱  
            /// </summary>  
            public string EnumName { set; get; }

            /// <summary>  
            /// 枚舉對象的值  
            /// </summary>  
            public int EnumValue { set; get; }
        }

    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章