• .Net6+Furion+Sqlsugar+SenparcSdk开发微信公众号系列之六:普通消息处理


    一、回复图文消息

    表MessageReceive新增一条数据

    INSERT INTO "MessageReceive" VALUES (4, 2, '图片', 'https://pic.cnblogs.com/avatar/668465/20210318093258.png');

    IMessageService定义回复图文的接口

            /// <summary>
            /// 处理文字消息,包括回复图片
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            Task<IResponseMessageBase> OnReceiveDbRequestAsync(RequestMessageText requestMessage);

    实现接口,按理说图片和图文应该是分开来的,这里我仅仅是演示就直接把他们归为一类,实际肯定是要分开的。

            public async Task<IResponseMessageBase> OnReceiveDbRequestAsync(RequestMessageText requestMessage)
            {
                var receives = await DbContext.Db.Queryable<MessageReceive>().ToListAsync();//获取列表
                var receive = receives.Where(it => it.KeyWords == requestMessage.Content).FirstOrDefault();//查找关键字是否存在
                if (receive != null)
                {
    
                    switch (receive.ReceiveType)
                    {
                        case ReceiveType.文字:
                            var responseText = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
                            responseText.Content = receive.ReceiveString;
                            return responseText;
                        case ReceiveType.图片:
                            var responseImage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageNews>(requestMessage);
                            responseImage.Articles.Add(new Article()
                            {
                                Title = "欢迎",
                                Description = "这是一张图片消息",
                                PicUrl = receive.ReceiveString,
                                Url = "https://www.cnblogs.com/huguodong/"
                            });
                            return responseImage;
                        default:
                            var responseOther = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
                            responseOther.Content = receive.ReceiveString;
                            return responseOther;
                    }
                }
                else
                {
                    var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
                    //如果关键字搜不到,列出关键字
                    var result = new StringBuilder();
                    result.AppendFormat("听不懂你再说什么,可以试试下面的关键字\r\n");
                    for (int i = 0; i < receives.Count; i++)
                    {
                        result.AppendFormat($"{i + 1}:{receives[i].KeyWords}\r\n");
                    }
                    responseMessage.Content = result.ToString();
                    return responseMessage;
    
                }
            }

    修改CustomMessageHandler的OnTextRequestAsync

       var result = await _messageService.OnReceiveDbRequestAsync(requestMessage);

    发布到服务器,查看效果,没毛病

    二、图片消息处理与回复

     IMessageService定义接口

    
            /// <summary>
            /// 处理图片
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            Task<IResponseMessageBase> OnReceiveImageRequestAsync(RequestMessageImage requestMessage);

    实现接口,这里传什么图片我就返回什么图片

          public async Task<IResponseMessageBase> OnReceiveImageRequestAsync(RequestMessageImage requestMessage)
            {
                var responseImage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageImage>(requestMessage);
                responseImage.Image.MediaId = requestMessage.MediaId;
                return responseImage;
            }

    CustomMessageHandler重写OnImageRequestAsync方法

            /// <summary>
            /// 处理图片
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            public override async Task<IResponseMessageBase> OnImageRequestAsync(RequestMessageImage requestMessage)
            {
                var result = await _messageService.OnReceiveImageRequestAsync(requestMessage);
                return result;
            }

    发布到云服务器,看看效果,没毛病

    三、语音消息的处理与回复

    首先去公众号官网设置接口权限开通语音识别

    IMessageService定义接口

            /// <summary>
            /// 处理语音消息
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            Task<IResponseMessageBase> OnReceiveVoiceRequestAsync(RequestMessageVoice requestMessage);

    实现接口

         public async Task<IResponseMessageBase> OnReceiveVoiceRequestAsync(RequestMessageVoice requestMessage)
            {
    
                var recognition = requestMessage.Recognition;//文字识别结果
                Console.WriteLine($"文字识别结果:{recognition}");
                if (string.IsNullOrEmpty(recognition))
                {
                    var responseText = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
                    responseText.Content = "抱歉,听不清你在说啥!";
                    return responseText;
                }
                else
                {
                    var responseVoice = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageVoice>(requestMessage);
                    responseVoice.Voice.MediaId = requestMessage.MediaId;
                    return responseVoice;
                }
    
            }

    CustomMessageHandler重写OnVoiceRequestAsync

            /// <summary>
            /// 处理音频
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            public override async Task<IResponseMessageBase> OnVoiceRequestAsync(RequestMessageVoice requestMessage)
            {
                var result = await _messageService.OnReceiveVoiceRequestAsync(requestMessage);
                return result;
            }

    上传服务器,测试,没毛病

    四、视频消息的处理与回复

    IMessageService定义接口

            /// <summary>
            /// 处理视频消息
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            Task<IResponseMessageBase> OnReceiveVideoRequestAsync(RequestMessageVideo requestMessage);

    实现接口

         public async Task<IResponseMessageBase> OnReceiveVideoRequestAsync(RequestMessageVideo requestMessage)
            {
                var responseVideo = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageVideo>(requestMessage);
                responseVideo.Video.Title = "这是您刚才发送的视频";
                responseVideo.Video.Description = "这是一条视频消息";
                responseVideo.Video.MediaId = requestMessage.MediaId;
                return responseVideo;
            }

    CustomMessageHandler重写OnVideoRequestAsync方法

            /// <summary>
            /// 处理视频消息
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            public override async Task<IResponseMessageBase> OnVideoRequestAsync(RequestMessageVideo requestMessage)
            {
                var result = await _messageService.OnReceiveVideoRequestAsync(requestMessage);
                return result;
            }

    上传云服务器,查看效果

    好像直接返回视频会报错,上网查了下,要通过接口上传到素材库再转发给用户才行,这是盛派SDK的Demo中写的,他是上传到素材库然后通过客服消息发给用户

    修改MessageService里的代码

            public async Task<IResponseMessageBase> OnReceiveVideoRequestAsync(RequestMessageVideo requestMessage)
            {
                var responseVideo = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageVideo>(requestMessage);
                //responseVideo.Video.Title = "这是您刚才发送的视频";
                //responseVideo.Video.Description = "这是一条视频消息";
                //responseVideo.Video.MediaId = requestMessage.MediaId;
                //上传素材
                var dir = ServerUtility.ContentRootMapPath("~/App_Data/TempVideo/");
                var file = await MediaApi.GetAsync(appId, requestMessage.MediaId, dir);
                var uploadResult = await MediaApi.UploadTemporaryMediaAsync(appId, UploadMediaFileType.video, file, 5000);
                responseVideo.Video.Title = "这是您刚才发送的视频";
                responseVideo.Video.Description = "这是一条视频消息";
                responseVideo.Video.MediaId = uploadResult.media_id;
                return responseVideo;
            }

    上传云服务器,发现报错了,原来是没有access_token

    我们在startup.cs中注册一下方法

    继续发到服务器,发现又报错了

    触发了weixin异常日志,之前我们没有打印日志消息,所以看不到错误内容,现在我们改一下

            //当发生基于WeixinException的异常时触发
            WeixinTrace.OnWeixinExceptionFunc = async ex =>
            {
                //加入每次触发WeixinExceptionLog后需要执行的代码
                System.Console.WriteLine(ex.Message);
                System.Console.WriteLine(ex.InnerException);
            };

    重新发布,看到报错是没有加到白名单,不给上传

    设置白名单之后,发现报错了,原因可能是上传素材和回复视频不能同时操作,在demo中他是通过客服机器人回复的

    这里因为我是用的个人公众号没有客服功能,所以我试着先上传临时素材,拿到MediaId之后在接口里写死。首先在WeiXinApi.Application->Services文件夹下新建Api接口MaterialService

    这里考虑到MaterialService和WeiXinService都是动态API并且都要用到Appid等属性,所有就可以把通用的东西提取出来成为BaseService,然后service只要继承就行了,在services文件夹下新建BaseService

    namespace WeiXinApi.Application.Services
    {
        public class BaseService : IDynamicApiController
        {
            public static readonly string Token = Config.SenparcWeixinSetting.MpSetting.Token;//与微信公众账号后台的Token设置保持一致,区分大小写。
            public static readonly string EncodingAESKey = Config.SenparcWeixinSetting.MpSetting.EncodingAESKey;//与微信公众账号后台的EncodingAESKey设置保持一致,区分大小写。
            public static readonly string AppId = Config.SenparcWeixinSetting.MpSetting.WeixinAppId;//与微信公众账号后台的AppId设置保持一致,区分大小写。
    
    
        }
    }

    WeiXinService改成继承BaseService

    MaterialService也继承BaseService,并新增加上传临时文件接口

    namespace WeiXinApi.Application.Services
    {
        public class MaterialService : BaseService
        {
    
            /// <summary>
            /// 上传临时素材
            /// </summary>
            /// <param name="input"></param>
            /// <returns></returns>
            [HttpPost("/upload/temp")]
            public async Task<dynamic> UploadTemp([FromForm] MaterialAddInput input)
            {
    
                var file = await GetPath(input.File);
                var uploadResult = await MediaApi.UploadTemporaryMediaAsync(AppId, input.UploadMediaFileType.Value, file, 50000);
                return uploadResult.media_id;
            }
    
            /// <summary>
            /// 保存文件到本地
            /// </summary>
            /// <param name="file"></param>
            /// <param name="isTemp"></param>
            /// <returns></returns>
            [NonAction]//不是API
            private async Task<string> GetPath(IFormFile file, bool isTemp = true)
            {
                var dir = isTemp ? "temp" : "permanent";
                // 保存到网站根目录下的 uploads 目录
                var savePath = Path.Combine(App.HostEnvironment.ContentRootPath, "uploads", dir);
                if (!Directory.Exists(savePath)) Directory.CreateDirectory(savePath);
    
                // 避免文件名重复,采用 GUID 生成
                var filePath = Path.Combine(savePath, Guid.NewGuid().ToString("N") + Path.GetExtension(file.FileName));  // 可以替代为你需要存储的真实路径
                using (var stream = System.IO.File.Create(filePath))
                {
                    await file.CopyToAsync(stream);
                }
                return filePath;
            }
        }
    }

    这里的MaterialAddInput是一个入参实体类,按照规则放在Material文件下的Dto文件夹中

    namespace WeiXinApi.Application.Services
    {
        public class MaterialAddInput
        {
            /// <summary>
            /// 文件类型
            /// </summary>
            [Required(ErrorMessage = "文件类型不能为空")]
            public UploadMediaFileType? UploadMediaFileType { get; set; }
    
            /// <summary>
            /// 文件
            /// </summary>
            [Required(ErrorMessage = "文件不存在")]
            public IFormFile File { get; set; }
        }
    }

    通过swagger上传素材

    将获取到的id放到视频处理里面

    发布服务器,测试一下,这下回复正常了。

    五、小视频消息的处理

    IMessageService写接口

            /// <summary>
            /// 处理小视频消息
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            Task<IResponseMessageBase> OnReceiveShortVideoRequestAsync(RequestMessageShortVideo requestMessage);

    MessageService实现接口,这里我就简答的回复文字

      public async Task<IResponseMessageBase> OnReceiveShortVideoRequestAsync(RequestMessageShortVideo requestMessage)
            {
                var responseText = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
                responseText.Content = "您刚才发送的是小视频";
                return responseText;
            }

    CustomMessageHandler重写OnShortVideoRequestAsync

            /// <summary>
            /// 小视频消息处理
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            public override async Task<IResponseMessageBase> OnShortVideoRequestAsync(RequestMessageShortVideo requestMessage)
            {
                var result = await _messageService.OnReceiveShortVideoRequestAsync(requestMessage);
                return result;
            }

    发布服务器测试一下,发现好像没法发小视频给微信公众号,也就没法测试,所以这里就不演示测试结果了,反正应该没问题

    六、位置消息的处理与回复

    IMessageService定义接口

            /// <summary>
            /// 处理位置信息
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            Task<IResponseMessageBase> OnReceiveLocationRequestAsync(RequestMessageLocation requestMessage);

    实现接口,这里我直接用的官方demo里的,返回图文消息,不得不吐槽官方demo,经纬度都搞反了。。。。。。

    public async Task<IResponseMessageBase> OnReceiveLocationRequestAsync(RequestMessageLocation requestMessage)
            {
                var responseNews = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageNews>(requestMessage);
                var markersList = new List<BaiduMarkers>();
                markersList.Add(new BaiduMarkers()
                {
                    Longitude = requestMessage.Location_Y,
                    Latitude = requestMessage.Location_X,
                    Color = "red",
                    Label = "S",
                    Size = BaiduMarkerSize.m
                });
    
                var mapUrl = BaiduMapHelper.GetBaiduStaticMap(requestMessage.Location_Y, requestMessage.Location_X, 1, 6, markersList);
                responseNews.Articles.Add(new Article()
                {
                    Description = string.Format("【来自百度地图】您刚才发送了地理位置信息。Location_X:{0},Location_Y:{1},Scale:{2},标签:{3}",
                               requestMessage.Location_X, requestMessage.Location_Y,
                               requestMessage.Scale, requestMessage.Label),
                    PicUrl = mapUrl,
                    Title = "定位地点周边地图",
                    Url = mapUrl
                });
    
                return responseNews;
            }

    CustomMessageHandler重写OnLocationRequestAsync

            /// <summary>
            /// 处理位置信息
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            public override async Task<IResponseMessageBase> OnLocationRequestAsync(RequestMessageLocation requestMessage)
            {
                var result = await _messageService.OnReceiveLocationRequestAsync(requestMessage);
                return result;
            }

    发布到服务器,查看下效果,没毛病

    七、链接消息的处理与回复

    IMessageService定义接口

            /// <summary>
            /// 处理链接消息
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            Task<IResponseMessageBase> OnReceiveLinkRequestAsync(RequestMessageLink requestMessage);

    实现接口,这里我直接返回图文消息

         public async Task<IResponseMessageBase> OnReceiveLinkRequestAsync(RequestMessageLink requestMessage)
            {
                var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageNews>(requestMessage);
                responseMessage.Articles.Add(new Article
                {
                    Title = requestMessage.Title,
                    Description = requestMessage.Description,
                    PicUrl = "https://pic.cnblogs.com/avatar/668465/20210318093258.png",
                    Url = requestMessage.Url
                });
                return responseMessage;
            }

    CustomMessageHandler重写OnLinkRequestAsync

            /// <summary>
            /// 处理链接消息
            /// </summary>
            /// <param name="requestMessage"></param>
            /// <returns></returns>
            public override async Task<IResponseMessageBase> OnLinkRequestAsync(RequestMessageLink requestMessage)
            {
                var result = await _messageService.OnReceiveLinkRequestAsync(requestMessage);
                return result;
            }

    上传到服务器,测试一下,没毛病

    八、本章Gitee地址链接

    https://gitee.com/huguodong520/weixinapi/tree/%E5%85%B6%E4%BB%96%E6%B6%88%E6%81%AF%E5%A4%84%E7%90%86/

  • 相关阅读:
    【JAVA基础】String 类
    【Java基础】ArrayList类
    【Java基础】Scanner类
    【Java基础】类--------封装
    spring boot默认访问静态资源
    spring boot整合servlet、filter、Listener等组件方式
    在web项目中搭建一个spring mvc + spring + mybatis的环境
    spring mvc注解版01
    spring mvc简单介绍xml版
    服务器和客户端的理解
  • 原文地址:https://www.cnblogs.com/huguodong/p/16308142.html
Copyright © 2020-2023  润新知