.Net6+Fruion+Sqlsugar+SenparcSdk開發微信公衆號系列之五:自定義MessageService來處理消息

一、目的

在勝派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

 發佈服務器測試一下,沒毛病

 四、本章Gitee地址

https://gitee.com/huguodong520/weixinapi/tree/%E8%87%AA%E5%AE%9A%E4%B9%89MessageService/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章