一、目的
在勝派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
: #34;[非文字類型{((historyMessage is IRequestMessageEventKey eventKey) ? #34;-{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(#34;{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/