.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/

posted @ 2022-05-25 16:59  HuTiger  阅读(502)  评论(0编辑  收藏  举报