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

一、目的

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

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

二、自定义MessageService

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

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

namespace WeiXinApi.Application.Services

{

publicclass MessageService : IMessageService, ITransient

{

}

}

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

publicoverrideasync Task<IResponseMessageBase> OnTextRequestAsync(RequestMessageText requestMessage)

{

returnawaitbase.OnTextRequestAsync(requestMessage);

}

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

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

namespace WeiXinApi.Application.Services

{

publicinterface 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中的代码简化了一下

publicasync 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重写下面两个方法

publicoverrideasync Task OnExecutedAsync(CancellationToken cancellationToken)

{

//演示:MessageContext.StorageDatavar currentMessageContext = awaitbase.GetUnsafeMessageContext();//为了在分布式缓存下提高读写效率,使用此方法,如果需要获取实时数据,应该使用 base.GetCurrentMessageContext()

currentMessageContext.StorageData = ((int)currentMessageContext.StorageData) + 1;

GlobalMessageContext.UpdateMessageContext(currentMessageContext);//储存到缓存awaitbase.OnExecutedAsync(cancellationToken);

}

publicoverride IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)

{

var responseMessage = base.CreateResponseMessage<ResponseMessageText>(); //ResponseMessageText也可以是News等其他类型

responseMessage.Content = "这条消息来自DefaultResponseMessage。";

return responseMessage;

}

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

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

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

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

publicoverrideasync Task<IResponseMessageBase> OnTextRequestAsync(RequestMessageText requestMessage)

{

var currentMessageContext = awaitbase.GetCurrentMessageContext();

var result = await _messageService.OnTextRequestAsync(requestMessage, currentMessageContext, GlobalMessageContext.ExpireMinutes, GlobalMessageContext.MaxRecordCount);

return result;

}

最后就是在WeixinService里注入IMessageService

privatereadonly 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

{

publicclass DbContext

{

publicstaticstring ConnectionString = Path.Combine(App.WebHostEnvironment.ContentRootPath, "weixin.sqlite");

publicstatic 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")]

publicclass MessageReceive

{

///<summary>/// 主键

///</summary>

[SugarColumn(ColumnName = "Id", IsPrimaryKey = true,IsIdentity = true)]

publicint Id { get; set; }

///<summary>/// 回复类型:文字,图片等

///</summary>

[SugarColumn(ColumnName = "ReceiveType")]

publicint ReceiveType { get; set; }

///<summary>/// 关键字

///</summary>

[SugarColumn(ColumnName = "KeyWords")]

publicstring KeyWords { get; set; }

///<summary>/// 回复内容

///</summary>

[SugarColumn(ColumnName = "ReceiveString")]

publicstring ReceiveString { get; set; }

}

}

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

namespace WeiXinApi.Core

{

publicenum 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);

实现接口

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值