基础消息能力

环境:.NetCore、Senparc.Weixin.MP

被动回复用户消息

需求:通过后台配置,不同业务平台的公众号被关注时回复不同的内容;配置关键字对应的回复内容;

  1 /// <summary>
  2 /// 公众号消息回复
  3 /// </summary> 
  4 /// <returns></returns>
  5 public async Task<string> WeChatSendMsgAsync(string wechatBody)
  6 {
  7     WechatEventDto wechatEvent = XmlToJsonConverter.Convert<WechatEventDto>(wechatBody);
  8     if (wechatEvent is null)
  9     {
 10         Logger.LogWarning("xml解析失败");
 11         return "";
 12     }
 13     Logger.LogWarning($"xml解析内容{JsonConvert.SerializeObject(wechatEvent)}");
 14 
 15     var (success, msg, data) = await _weChatService.GetUnionInfoAsync(wechatEvent.FromUserName);
 16     Logger.LogWarning($"公众号消息回复获取UnionInfo结果:{JsonConvert.SerializeObject(data)}");
 17 
 18     string xmlStr = "";
 19     TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
 20     var createTime = Convert.ToInt64(ts.TotalSeconds).ToString();
 21     string fromUserName = wechatEvent.FromUserName;
 22     string toUserName = wechatEvent.ToUserName;
 23     //事件推送
 24     if (wechatEvent.MsgType == "event")
 25     {
 26         if (wechatEvent.Event == EventType.subscribe.ToString())
 27         {
 28             if (string.IsNullOrEmpty(wechatEvent.EventKey))  //普通关注
 29             {
 30                 var commonConfig = await _autoResponseConfigRepository.FindConfigByPlatformIdAsync(null);
 31                 if (commonConfig != null)
 32                 {
 33                     xmlStr = ReturnMsgXmlStr(commonConfig, fromUserName, toUserName);
 34                 }
 35             }
 36             else   //扫描关注
 37             {
 38                 var appId = wechatEvent.EventKey.Substring(0, wechatEvent.EventKey.LastIndexOf("&")).Replace(WeChatConstConfig.EventKeyPrefix, "");
 39                 var appInfo = await _systemAPPRepository.FirstOrDefaultAsync(x => x.AppId == appId);
 40                 if (appInfo is null)
 41                 {
 42                     Logger.LogWarning("未找到平台信息");
 43                     return "";
 44                 }
 45                 var autoResponseConfig = await _autoResponseConfigRepository.FindConfigByPlatformIdAsync(appInfo.Id);
 46                 if (autoResponseConfig != null)
 47                 {
 48                     xmlStr = ReturnMsgXmlStr(autoResponseConfig, fromUserName, toUserName);
 49                 }
 50             }
 51         }
 52 
 53         //模板消息事件推送
 54         if (wechatEvent.Event == EventType.TEMPLATESENDJOBFINISH.ToString())
 55         {
 56             //保存推送记录
 57             TemplateMessagePushRecord pushRecord = new TemplateMessagePushRecord()
 58             {
 59                 OpenId = data.openid,
 60                 UnionId = data.unionid,
 61                 MsgId = wechatEvent.MsgID,
 62                 WeChatReturnStr = wechatEvent.Status
 63             };
 64             await _pushRecordRepository.InsertAsync(pushRecord);
 65         }
 66     }
 67     //文本消息推送
 68     if (wechatEvent.MsgType == EnumMessageType.text.ToString())
 69     {
 70         AppAutoResponseConfig config = new AppAutoResponseConfig()
 71         {
 72             MessageType = EnumMessageType.text
 73         };
 74         var keywordConfig = await _keywordConfigRepository.FindAppKeywordConfigAsync(wechatEvent.Content);
 75         config.Content = keywordConfig?.ReplyContent;
 76         xmlStr = ReturnMsgXmlStr(config, fromUserName, toUserName);
 77     }
 78 
 79     return xmlStr;
 80 }
 81 
 82 /// <summary>
 83 /// 返回格式化后的回复消息
 84 /// </summary>
 85 /// <returns></returns>
 86 private string ReturnMsgXmlStr(AppAutoResponseConfig autoResponseConfig, string toUserName, string fromUserName)
 87 {
 88     string xmlStr = "";
 89     TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
 90     var createTime = Convert.ToInt64(ts.TotalSeconds).ToString();
 91 
 92     switch (autoResponseConfig.MessageType)
 93     {
 94         case EnumMessageType.text:
 95             TextMessageResponseDto textDto = new TextMessageResponseDto
 96             {
 97                 FromUserName = fromUserName,
 98                 ToUserName = toUserName,
 99                 CreateTime = createTime,
100                 Content = autoResponseConfig.Content
101             };
102             xmlStr = MessageResponse.TextMsgResponse(textDto);
103             break;
104         case EnumMessageType.news:
105             PicTextMessageResponseDto newsDto = new PicTextMessageResponseDto
106             {
107                 FromUserName = fromUserName,
108                 ToUserName = toUserName,
109                 CreateTime = createTime,
110                 Title = autoResponseConfig.Title,
111                 Description = autoResponseConfig.Description,
112                 PicUrl = autoResponseConfig.PicUrl,
113                 Url = autoResponseConfig.Url
114             };
115             xmlStr = MessageResponse.PicTextMsgResponse(newsDto);
116             break;
117     }
118     Logger.LogWarning($"xmlStr:{xmlStr}");
119     return xmlStr;
120 }
View Code
 1 public class XmlToJsonConverter
 2 {
 3     public static T Convert<T>(string xmlString) where T : new()
 4     {
 5         try
 6         {
 7             XDocument doc = XDocument.Parse(xmlString);
 8             return ProcessElement<T>(doc.Root);
 9         }
10         catch (Exception)
11         {
12             return default(T);
13            
14         }
15        
16     }
17     private static T ProcessElement<T>(XElement element) where T : new()
18     {
19         T result = new T();
20 
21         foreach (XElement childElement in element.Elements())
22         {
23             if (childElement.HasElements)
24             {
25                 var property = typeof(T).GetProperty(childElement.Name.LocalName);
26                 if (property != null)
27                 {
28                     property.SetValue(result, ProcessElement(property.PropertyType, childElement));
29                 }
30             }
31             else
32             {
33                 var property = typeof(T).GetProperty(childElement.Name.LocalName);
34                 if (property != null)
35                 {
36                     property.SetValue(result, childElement.Value);
37                 }
38             }
39         }
40         return result;
41     }
42     private static object ProcessElement(Type type, XElement element)
43     {
44         var method = typeof(XmlToJsonConverter).GetMethod("ProcessElement", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
45         var genericMethod = method.MakeGenericMethod(type);
46         return genericMethod.Invoke(null, new object[] { element });
47     }
48 }
View Code
 1 public class MessageResponse
 2 {
 3     /// <summary>
 4     /// 回复文本消息
 5     /// </summary>
 6     /// <param name="data"></param>
 7     /// <returns></returns>
 8     public static string TextMsgResponse(TextMessageResponseDto data)
 9     {
10         StringBuilder str = new StringBuilder();
11         str.Append("<xml>");
12         str.Append($"  <ToUserName><![CDATA[{data.ToUserName}]]></ToUserName>");
13         str.Append($"  <FromUserName><![CDATA[{data.FromUserName}]]></FromUserName>");
14         str.Append($"  <CreateTime>{data.CreateTime}</CreateTime>");
15         str.Append("  <MsgType><![CDATA[text]]></MsgType>");
16         str.Append($"  <Content>{data.Content}</Content>");
17         str.Append("</xml>");
18         return str.ToString();
19     }
20 
21     /// <summary>
22     /// 回复图文消息
23     /// </summary>
24     /// <param name="data"></param>
25     /// <returns></returns>
26     public static string PicTextMsgResponse(PicTextMessageResponseDto data)
27     {
28         StringBuilder str = new StringBuilder();
29         str.Append("<xml>");
30         str.Append("  <ToUserName><![CDATA[" + data.ToUserName + "]]></ToUserName>");
31         str.Append("  <FromUserName><![CDATA[" + data.FromUserName + "]]></FromUserName>");
32         str.Append("  <CreateTime>" + data.CreateTime + "</CreateTime>");
33         str.Append("  <MsgType><![CDATA[news]]></MsgType>");
34         str.Append("  <ArticleCount>1</ArticleCount>");
35         str.Append("  <Articles>");
36         str.Append("     <item>");
37         str.Append("       <Title><![CDATA[" + data.Title + "]]></Title>");
38         str.Append("       <Description><![CDATA[" + data.Description + "]]></Description>");
39         str.Append("       <PicUrl><![CDATA[" + data.PicUrl + "]]></PicUrl>");
40         str.Append("       <Url><![CDATA[" + data.Url + "]]></Url>");
41         str.Append("     </item>");
42         str.Append("  </Articles>");
43         str.Append("</xml>");
44         return str.ToString();
45     }
46 }
View Code

模板消息

需求:根据Excel模板导入学员信息发送消息提醒

 1 [HttpPost]
 2 [Route("import-msg")]
 3 public async Task<(bool success, string msg)> SendTempMsgAsync(IFormFile file)
 4 {
 5     if (file is null)
 6     {
 7         return (false, "未获取到文件信息");
 8     }
 9 
10     var fileExt = Path.GetExtension(file.FileName) ?? "";
11     if (fileExt != ".xlsx" && fileExt != ".xls")
12     {
13         return (false, "文件类型错误,请使用xlsx或xls后缀的文件");
14     }
15 
16     // 从Excel中获取数据
17     var excelData = ExcelExportHelper.ExcelToDataTable(file.OpenReadStream(), file.FileName, 3);
18     if (excelData == null)
19     {
20         return (false, "数据不可为空");
21     }
22 
23     var list = new List<ImportStudentsInput>();
24     foreach (DataRow row in excelData.Rows)
25     {
26         var model = new ImportStudentsInput()
27         {
28             WorkTypeName = row["岗位名称"].ToString(),
29             OpenId = row["OpenId"].ToString(),
30             UnionId = row["UnionId"].ToString()
31         };
32         list.Add(model);
33     }
34     var result = await _wcService.SendTempMsgAsync(list);
35     return (result, "操作完成");
36 }
37 
38 /// <summary>
39 /// 根据导入学员发送模板消息
40 /// </summary>
41 /// <param name="input"></param>
42 /// <returns></returns>
43 public async Task<bool> SendTempMsgAsync(List<ImportStudentsInput> input)
44 {
45     if (input != null)
46     {
47         //统一获取token
48         var (success, msg, accessToken) = await _weChatService.BuildWeChatTokenAsync();
49 
50         List<TemplateMessagePushRecord> recordList = new List<TemplateMessagePushRecord>();
51         var templateId = _configuration.GetSection("TemplateMsgPush:LearningProgressTempId").Value;
52         var first = "您有一条消息待确认!";
53         foreach (var item in input)
54         {
55             if (string.IsNullOrEmpty(item.OpenId))
56             {
57                 continue;
58             }
59             TemplateMessagePushRecord record = new TemplateMessagePushRecord();     //推送记录
60             record.OpenId = item.OpenId;
61             record.UnionId = item.UnionId;
62             record.MsgId = templateId;
63             record.PushStatus = 0;
64             try
65             {
66                 //推送内容
67                 var pushTemplate = new
68                 {
69                     first = new TemplateDataItem(first),
70                     keyword1 = new TemplateDataItem(item.WorkTypeName),
71                     keyword2 = new TemplateDataItem("2024年3月31日"),
72                     remark = new TemplateDataItem("请在规定时间内完成学习,如已完成请忽略")
73                 };
74                 var sendResult = TemplateApi.SendTemplateMessage(accessToken, item.OpenId, templateId, "", pushTemplate);
75                 record.WeChatReturnStr = sendResult.ToString();
76                 record.PushStatus = sendResult.ErrorCodeValue;
77             }
78             catch (Exception ex)
79             {
80                 record.WeChatReturnStr = ex.ToString();
81                 return false;
82             }
83             recordList.Add(record);
84         }
85         await _pushRecordRepository.InsertManyAsync(recordList);
86     }
87     return true;
88 }
View Code
  1 public class ExcelExportHelper
  2 {
  3     /// <summary>
  4     /// 将 Excel 文件流转换成 DataTable
  5     /// </summary>
  6     /// <param name="fileStream">Excel 文件流</param>
  7     /// <param name="fileName">Excel 文件名称</param>
  8     /// <param name="cellCount">读取的列数(从 0 开始)</param>
  9     /// <returns>转换后的 DataTable</returns>
 10     public static DataTable ExcelToDataTable(Stream fileStream, string fileName, int cellCount)
 11     {
 12         DataTable table = new DataTable();
 13 
 14         IWorkbook workbook = null;
 15         if (fileStream != null)
 16         {
 17             if (fileName.ToLower().EndsWith(".xlsx"))
 18             {
 19                 workbook = new XSSFWorkbook(fileStream);
 20             }
 21             else if (fileName.ToLower().EndsWith(".xls"))
 22             {
 23                 workbook = new HSSFWorkbook(fileStream);
 24             }
 25 
 26             if (workbook != null)
 27             {
 28                 ISheet sheet = workbook.GetSheetAt(0);
 29 
 30                 //添加表头,首行为说明文字,从第二行开始
 31                 IRow headerRow = sheet.GetRow(1);
 32                 for (int i = headerRow.FirstCellNum; i < cellCount; i++)
 33                 {
 34                     DataColumn column = new DataColumn(headerRow.GetCell(i).StringCellValue.Trim());
 35                     table.Columns.Add(column);
 36                 }
 37 
 38                 // 添加数据
 39                 int rowCount = sheet.LastRowNum;
 40                 for (int i = (sheet.FirstRowNum + 2); i <= rowCount; i++)
 41                 {
 42                     IRow row = sheet.GetRow(i);
 43                     DataRow dataRow = table.NewRow();
 44 
 45                     for (int j = row.FirstCellNum; j < cellCount; j++)
 46                     {
 47                         if (row.GetCell(j) != null)
 48                         {
 49                             dataRow[j] = row.GetCell(j).ToString().Trim();
 50                         }
 51                     }
 52 
 53                     table.Rows.Add(dataRow);
 54                 }
 55 
 56                 // 移除空行
 57                 for (var i = table.Rows.Count - 1; i >= 0; i--)
 58                 {
 59                     var isEmptyRow = true;
 60                     for (var j = 0; j < table.Columns.Count; j++)
 61                     {
 62                         if (!string.IsNullOrEmpty(table.Rows[i][j].ToString()))
 63                         {
 64                             isEmptyRow = false;
 65                             break;
 66                         }
 67                     }
 68                     if (isEmptyRow)
 69                     {
 70                         table.Rows.RemoveAt(i);
 71                     }
 72                 }
 73             }
 74         }
 75 
 76         return table;
 77     }
 78 
 79     /// <summary>
 80     /// 导出数据到Excel
 81     /// </summary>
 82     /// <typeparam name="T"></typeparam>
 83     /// <param name="dataList"></param>
 84     /// <param name="columnMapping"></param>
 85     /// <returns></returns>
 86     public static byte[] ExportToExcel<T>(IEnumerable<T> dataList, Dictionary<string, string> columnMapping)
 87     {
 88         // 创建Excel文档对象
 89         var workbook = new XSSFWorkbook();
 90         var sheet = workbook.CreateSheet("Sheet1");
 91 
 92         // 创建表头行并设置列名称
 93         var headerRow = sheet.CreateRow(0);
 94         int columnIndex = 0;
 95         foreach (var column in columnMapping)
 96         {
 97             headerRow.CreateCell(columnIndex).SetCellValue(column.Value);
 98             columnIndex++;
 99         }
100 
101         // 将数据填充到Excel文档中
102         int rowIndex = 1;
103         foreach (var data in dataList)
104         {
105             var dataRow = sheet.CreateRow(rowIndex);
106             columnIndex = 0;
107             foreach (var column in columnMapping)
108             {
109                 var property = typeof(T).GetProperty(column.Key);
110                 var value = property.GetValue(data);
111                 dataRow.CreateCell(columnIndex).SetCellValue(value?.ToString());
112                 columnIndex++;
113             }
114             rowIndex++;
115         }
116 
117         // 导出Excel文档
118         using (var stream = new MemoryStream())
119         {
120             workbook.Write(stream);
121             return stream.ToArray();
122         }
123     }
124 }
View Code

可能遇到的问题

微信公众号  throw exception when excuting local service: Common.IService.IWxMsgTemplateService.SendTemplate(openid,templateId,url,data)Senparc.Weixin.Exceptions.UnRegisterAppIdException: 尚无已经注册的AppId,请先使用AccessTokenContainer.Register完成注册(全局执行一次即可)!模块:WeChat_OfficialAccount

解决方案

微信公众号IP 白名单上加上部署的服务器IP 地址;在启动类中注册

 

posted @ 2024-02-19 11:02  智者见智  阅读(13)  评论(0编辑  收藏  举报