ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

.Net6+Fruion+Sqlsugar+SenparcSdk开发微信公众号系列之五:自定义MessageService来处理消息

2022-05-25 09:02:15  阅读:144  来源: 互联网

标签:自定义 SenparcSdk 微信 currentMessageContext result requestMessage var public respons


一、目的

在胜派SDK的官方Demo中,我发现他把所有处理消息请求的方法都放在了CustomMessageHandler,这就导致了CustomMessageHandler异常的臃肿,维护起来也挺麻烦。

 

  所以我就想把处理消息这块封提取出来,这样代码就清爽了很多,而且代码维护也变得简单。如图所示,拿到消息之后直接丢给messageService处理,不需要关系如何处理的,只需要返回处理结果,这也是面向接口编程的好处。

 二、自定义MessageService

WeiXinApi.Application项目services文件夹新建Message文件夹并新建MessageService类和接口

 MessageService类继承IMessageService接口,并且通过Furion注册生命周期为瞬时

namespace WeiXinApi.Application.Services
{
    public class MessageService : IMessageService, ITransient
    {

    }
}

在CustomMessageHandler.cs中重写OnTextRequestAsync处理文字消息的请求方法

   public override async Task<IResponseMessageBase> OnTextRequestAsync(RequestMessageText requestMessage)
        {

            return await base.OnTextRequestAsync(requestMessage);
        }

根据官方demo中的代码,OnTextRequestAsync方法的返回类型是IResponseMessageBase,要用到的参数是requestMessage,currentMessageContext,GlobalMessageContext.ExpireMinutes, GlobalMessageContext.MaxRecordCount

 

 所以我们需要在IMessageService中定义一个方法OnTextRequestAsync

namespace WeiXinApi.Application.Services
{
    public interface IMessageService
    {
        /// <summary>
        /// 处理文字消息
        /// </summary>
        /// <param name="requestMessage"></param>
        /// <param name="mpMessageContext"></param>
        /// <param name="ExpireMinutes"></param>
        /// <param name="MaxRecordCount"></param>
        /// <returns></returns>
        Task<ResponseMessageText> OnTextRequestAsync(RequestMessageText requestMessage, DefaultMpMessageContext mpMessageContext, int ExpireMinutes, int MaxRecordCount);
    }
}

先简单的实现一下接口,基本就是把官方demo中的代码简化了一下

        public async Task<ResponseMessageText> OnTextRequestAsync(RequestMessageText requestMessage, DefaultMpMessageContext mpMessageContext, int ExpireMinutes, int MaxRecordCount)
        {
            var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
            var requestHandler = await requestMessage.StartHandler()
                //关键字不区分大小写,按照顺序匹配成功后将不再运行下面的逻辑
                .Keyword("你好", () =>
                {
                    responseMessage.Content = "你也好啊!";
                    return responseMessage;
                }).Default(async () =>
                {
                    var result = new StringBuilder();
                    result.AppendFormat("您刚才发送了文字信息:{0}\r\n\r\n", requestMessage.Content);

                    var currentMessageContext = mpMessageContext;
                    if (currentMessageContext.RequestMessages.Count > 1)
                    {
                        result.AppendFormat("您此前还发送了如下消息({0}/{1}):\r\n", currentMessageContext.RequestMessages.Count,
                            currentMessageContext.StorageData);
                        for (int i = currentMessageContext.RequestMessages.Count - 2; i >= 0; i--)
                        {
                            var historyMessage = currentMessageContext.RequestMessages[i];
                            result.AppendFormat("{0} 【{1}】{2}\r\n",
                                historyMessage.CreateTime.ToString("HH:mm:ss"),
                                historyMessage.MsgType.ToString(),
                                (historyMessage is RequestMessageText)
                                    ? (historyMessage as RequestMessageText).Content
                                    : $"[非文字类型{((historyMessage is IRequestMessageEventKey eventKey) ? $"-{eventKey.EventKey}" : "")}]"
                                );
                        }
                        result.AppendLine("\r\n");
                    }

                    result.AppendFormat("如果您在{0}分钟内连续发送消息,记录将被自动保留(当前设置:最多记录{1}条)。过期后记录将会自动清除。\r\n",
                        ExpireMinutes, MaxRecordCount);
                    result.AppendLine("\r\n");
                    result.AppendLine(
                        "您还可以发送【位置】【图片】【语音】【视频】等类型的信息(注意是这几种类型,不是这几个文字),查看不同格式的回复。\r\nSDK官方地址:https://sdk.weixin.senparc.com");
                    responseMessage.Content = result.ToString();

                    return responseMessage;
                });

            return responseMessage;
        }

这里的currentMessageContext.StorageData这是一个用于储存任何和用户上下文有关数据的容器,WeixinContext和IMessageContext没有对它进行任何引用,完全由开发者决定里面的内容(比如用户执行到哪一步、或某个比较重要的位置信息等等),类似于Session的作用。这里官网的demo里用到了,我直接把官方demo里的,拿过来抄了,直接在CustomMessageHandler.cs重写下面两个方法

        public override async Task OnExecutedAsync(CancellationToken cancellationToken)
        {
            //演示:MessageContext.StorageData

            var currentMessageContext = await base.GetUnsafeMessageContext();//为了在分布式缓存下提高读写效率,使用此方法,如果需要获取实时数据,应该使用 base.GetCurrentMessageContext()
            currentMessageContext.StorageData = ((int)currentMessageContext.StorageData) + 1;
            GlobalMessageContext.UpdateMessageContext(currentMessageContext);//储存到缓存
            await base.OnExecutedAsync(cancellationToken);
        }


        public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
        {
            var responseMessage = base.CreateResponseMessage<ResponseMessageText>(); //ResponseMessageText也可以是News等其他类型
            responseMessage.Content = "这条消息来自DefaultResponseMessage。";
            return responseMessage;
        }

每次用户发送消息都会存到缓存中,所以我们要在ConfigureServices里注入缓存服务

 services.AddMemoryCache();//使用本地缓存必须添加

修改CustomMessageHandler,将Imessageservice通过构造函数传进来。

 修改OnTextRequestAsync,当接收到文字消息的时候,调用messageservice里的OnTextRequestAsync方法

   public override async Task<IResponseMessageBase> OnTextRequestAsync(RequestMessageText requestMessage)
        {
            var currentMessageContext = await base.GetCurrentMessageContext();
            var result = await _messageService.OnTextRequestAsync(requestMessage, currentMessageContext, GlobalMessageContext.ExpireMinutes, GlobalMessageContext.MaxRecordCount);
            return result;
        }

最后就是在WeixinService里注入IMessageService

    private readonly IMessageService _messageService;

        public WeiXinService(IHttpContextAccessor​ httpContextAccessor, IMessageService messageService)
        {
            this._httpContextAccessor = httpContextAccessor;
            this._messageService = messageService;
        }

new CustomMessageHandler的时候把_messageservice传进去

 发布到云服务器,测试一下效果,没毛病

 三、动态回复消息

上面的例子中,虽然可以自动回复消息,但是回复内容都是写死在代码里,灵活性太差,我们可以将自动回复内容改为从数据库读取,然后再回复。正好趁着这个机会推荐一波Sqlugar,国产最NB的ORM。

 引入sqlsugar的nuget包

我们使用的是Sqlsugar的单例模式,简单粗暴,直接在WeiXinApi.Core项目下新建DB文件夹

 直接定义静态变量Db

using Furion;
using SqlSugar;
using System;
using System.IO;

namespace WeiXinApi.Core
{
    public class DbContext
    {
        public static string ConnectionString = Path.Combine(App.WebHostEnvironment.ContentRootPath, "weixin.sqlite");

        public static SqlSugarScope Db = new SqlSugarScope(new ConnectionConfig()
        {
            DbType = SqlSugar.DbType.Sqlite,
            ConnectionString = "DataSource=" + ConnectionString,
            IsAutoCloseConnection = true
        },
       db =>
        {
            //单例参数配置,所有上下文生效
            db.Aop.OnLogExecuting = (s, p) =>
             {
                 var sql = UtilMethods.GetSqlString(DbType.SqlServer, s, p);
                 Console.WriteLine(sql);
             };
        });
    }
}

 我们需要创建数据库和表,这里我直接使用的sqlite数据库,生成表和实体我用的是sqlsugar推荐的webfirst,具体用法可以去官网看看

 创建完会自动生成sqlite文件

 下面开始建表,使用的是类建表

 先简单的建一个消息回复表

选择创建的类,点击预览

WeiXinApi.Core项目新建Entity文件夹

 在文件夹下新建MessageReceive实体类,将预览的实体类复制进去

using SqlSugar;

namespace WeiXinApi.Core
{
    /// <summary>
    /// 自动回复表
    ///</summary>
    [SugarTable("MessageReceive")]
    public class MessageReceive
    {
        /// <summary>
        /// 主键 
        ///</summary>
        [SugarColumn(ColumnName = "Id", IsPrimaryKey = true,IsIdentity = true)]
        public int Id { get; set; }
        /// <summary>
        /// 回复类型:文字,图片等 
        ///</summary>
        [SugarColumn(ColumnName = "ReceiveType")]
        public int ReceiveType { get; set; }
        /// <summary>
        /// 关键字 
        ///</summary>
        [SugarColumn(ColumnName = "KeyWords")]
        public string KeyWords { get; set; }
        /// <summary>
        /// 回复内容 
        ///</summary>
        [SugarColumn(ColumnName = "ReceiveString")]
        public string ReceiveString { get; set; }
    }
}

因为我们的回复类型可以是枚举,所以我们新建一个枚举类ReceiveType

namespace WeiXinApi.Core
{
    public enum ReceiveType
    {
        文字 = 1,
        图片
    }
}

将实体中的ReceiveType从int改为我们的枚举

 我们需要一些数据,首先将建的表同步到数据库

手动添加一些数据

INSERT INTO "MessageReceive" ("Id", "ReceiveType", "KeyWords", "ReceiveString") VALUES (1, '1', '你好', '你也好');
INSERT INTO "MessageReceive" ("Id", "ReceiveType", "KeyWords", "ReceiveString") VALUES (2, '1', '在吗', '我在');
INSERT INTO "MessageReceive" ("Id", "ReceiveType", "KeyWords", "ReceiveString") VALUES (3, '1', '激活码', '1234567');

测试一下有没有数据

 查到了3条数据

IMessageService新增一个接口

         /// <summary>
        /// 从数据库处理文字消息
        /// </summary>
        /// <param name="requestMessage"></param>
        /// <returns></returns>
        Task<ResponseMessageText> OnTextDbRequestAsync(RequestMessageText requestMessage);

实现接口

public async Task<ResponseMessageText> OnTextDbRequestAsync(RequestMessageText requestMessage)
        {
            var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
            var receives = await DbContext.Db.Queryable<MessageReceive>().ToListAsync();//获取列表
            var receive = receives.Where(it => it.KeyWords == requestMessage.Content).FirstOrDefault();//查找关键字是否存在
            if (receive != null)
            {
                responseMessage.Content = receive.ReceiveString;
            }
            else
            {
                //如果关键字搜不到,列出关键字
                var result = new StringBuilder();
                result.AppendFormat("听不懂你再说什么,可以试试下面的关键字\r\n\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,改成OnTextDbRequestAsync

 发布服务器测试一下,没毛病

 

标签:自定义,SenparcSdk,微信,currentMessageContext,result,requestMessage,var,public,respons
来源: https://www.cnblogs.com/huguodong/p/16305579.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有