关注微信公众号推送消息
微信关注二维码的主要开发思路:
准备, 注册一个公从号、准备一个服务,在配置微信时用。
第一步,向用户展示要扫的二维码:通过提前申请的appid和秘钥去拿token,通过token再去微信服务上拿要关注的图片二维码,将二维码展示在你系统中。
第二步,用户扫码: 用户扫码时会向微信发信息,并且微信会回调你的自己的服务,需要你将关注的用户信息保存下来;
第三步,给用户发送信息:通过用户标识,找到第二步中保存下来的用户,给微信接口发指定的模版信息,关注的用户会接收到信息,并且会回调我们的服务。
准备:
注册一个公众号,配置相关的信息:
其中,服务器地址为你自己部署的服务器可访问地址,微信会调用此服务器地址验证token。也就是说会调用如下的不用授权的方法。
服务器验证token方法:
[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(""); } }
public static bool Check(string signature, string timestamp, string nonce, string token) { return signature == GetSignature(timestamp, nonce, token); }
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(); }
服务配置微信要用的参数:
一、获取微信关注二维码
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; } } }