Files
serein-flow/Serein.Proto.WebSocket/Handle/WebSocketHandleModule.cs
fengjiayi 819320f355 1. 修改了Serein.Library中的ObjectPool工具类,提供了对象归还的默认处理方法(通过构造方法指定Action<T>委托)
2. 不再显式调用流程上下文的Reset()方法,而改为通过对象池调用
3. 同样的,对于同样使用了ObjectPool管理上下文的Serein.Proto.WebSocket项目而言,也进行了同2.一样的修改
2025-08-23 16:19:30 +08:00

383 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace Serein.Proto.WebSocket.Handle
{
/// <summary>
/// Json消息处理模块
/// </summary>
public class WebSocketHandleModule
{
/// <summary>
/// Json消息处理模块
/// </summary>
public WebSocketHandleModule(WebSocketModuleConfig config)
{
_moduleConfig = config;
_methodInvokeConfigs = new ConcurrentDictionary<string, MethodInvokeConfiguration>();
_myMsgIdHash = new HashSet<string>();
}
/// <summary>
/// 模块的处理配置
/// </summary>
private readonly WebSocketModuleConfig _moduleConfig;
/// <summary>
/// 用来判断消息是否重复
/// </summary>
private readonly HashSet<string> _myMsgIdHash;
/// <summary>
/// 存储处理数据的配置
/// </summary>
private readonly ConcurrentDictionary<string, MethodInvokeConfiguration> _methodInvokeConfigs ;
/// <summary>
/// 添加处理配置
/// </summary>
/// <param name="config">处理模块</param>
internal bool AddHandleConfigs(WebSocketMethodConfig config)
{
if (!_methodInvokeConfigs.ContainsKey(config.ThemeValue))
{
_methodInvokeConfigs[config.ThemeValue] = config;
return true;
}
else
{
return false;
}
}
/// <summary>
/// 移除某个处理模块
/// </summary>
/// <param name="socketControlBase"></param>
/// <returns></returns>
public bool RemoveConfig(ISocketHandleModule socketControlBase)
{
foreach (var kv in _methodInvokeConfigs.ToArray())
{
var config = kv.Value;
_methodInvokeConfigs.TryRemove(kv.Key, out _);
}
return _methodInvokeConfigs.Count == 0;
}
/// <summary>
/// 卸载当前模块的所有配置
/// </summary>
public void UnloadConfig()
{
var temp = _methodInvokeConfigs.Values;
_methodInvokeConfigs.Clear();
}
/// <summary>
/// 处理JSON数据
/// </summary>
public async Task HandleAsync(WebSocketHandleContext context)
{
var jsonObject = context.MsgRequest; // 获取到消息
context.Model = new ModuleConfig(); // 设置当前模块配置
context.Model.IsResponseUseReturn = _moduleConfig.IsResponseUseReturn;
if (jsonObject is null)
{
context.TriggerExceptionTracking($"请求没有获取到消息");
return; // 没有获取到消息
}
if (!jsonObject.TryGetValue(_moduleConfig.MsgIdJsonKey, out var msgIdToken) || msgIdToken.IsNull)
{
context.TriggerExceptionTracking($"消息Id从JSON键[{_moduleConfig.MsgIdJsonKey}]提取失败");
return; // 没有获取到消息
}
if (msgIdToken.Type != IJsonToken.TokenType.Value)
{
context.TriggerExceptionTracking($"请求消息Id[{_moduleConfig.ThemeJsonKey}]需要值类型,当前类型为[{msgIdToken.Type}]");
return; // 没有获取到消息
}
var msgId = msgIdToken.ToString(); // 获取Id
context.Model.MsgId = msgId;
// 验证消息ID是否重复
if (!_myMsgIdHash.Add(msgId))
{
context.TriggerExceptionTracking($"消息Id[{msgId}]重复发送");
return; // 消息重复
}
if(!jsonObject.TryGetValue(_moduleConfig.ThemeJsonKey, out var themeToken) || themeToken.IsNull)
{
context.TriggerExceptionTracking($"主题从JSON键[{_moduleConfig.ThemeJsonKey}]提取失败");
return; // 没有获取到消息
}
if(themeToken.Type != IJsonToken.TokenType.Value)
{
context.TriggerExceptionTracking($"请求主题[{_moduleConfig.ThemeJsonKey}]需要值类型,当前类型为[{themeToken.Type}]");
return; // 没有获取到消息
}
var theme = themeToken.ToString(); // 获取主题
context.Model.Theme = theme;
// 验证主题
if (!_methodInvokeConfigs.TryGetValue(theme, out var handldConfig))
{
context.TriggerExceptionTracking($"不存在这样的主题");
return;
}
// 验证数据
if (!jsonObject.TryGetValue(_moduleConfig.DataJsonKey, out var dataToken))
{
context.TriggerExceptionTracking($"数据从JSON键[{_moduleConfig.DataJsonKey}]提取失败");
return; // 没有主题
}
if(dataToken.Type != IJsonToken.TokenType.Object)
{
context.TriggerExceptionTracking($"数据需要 JSON Object当前类型为[{dataToken.Type}]");
return;
}
context.MsgData = dataToken; // 添加消息
context.MsgRequest = jsonObject; // 添加原始消息
try
{
if (TryGetParameters(handldConfig, context, out var args))
{
var result = await InvokeAsync(handldConfig, args);
if (handldConfig.IsReturnValue)
{
await RepliedAsync(_moduleConfig, context, result);
}
}
}
catch (Exception ex)
{
context.TriggerExceptionTracking(ex);
}
finally
{
context.Handle = true;
}
}
/// <summary>
/// 调用
/// </summary>
/// <param name="config"></param>
/// <param name="args"></param>
/// <returns></returns>
public static async Task<object> InvokeAsync(MethodInvokeConfiguration config, object?[] args)
{
if (config.DelegateDetails is null)
{
throw new InvalidOperationException("DelegateDetails 为 null, 无法进行调用.");
}
var instance = config.InstanceFactory?.Invoke();
var result = await config.DelegateDetails.InvokeAsync(instance, args);
return result;
}
/// <summary>
/// 获取入参参数
/// </summary>
/// <param name="config">处理配置</param>
/// <param name="context">处理上下文</param>
/// <param name="args">返回的入参参数</param>
/// <returns></returns>
internal static bool TryGetParameters(MethodInvokeConfiguration config, WebSocketHandleContext context, out object?[] args)
{
args = new object[config.ParameterType.Length];
var theme = context.Model.Theme;
var msgId = context.Model.MsgId;
List<string> exTips = [$"主题 {theme} 消息Id {msgId}"];
bool isCanInvoke = true; ; // 表示是否可以调用方法
for (int i = 0; i < config.ParameterType.Length; i++)
{
var type = config.ParameterType[i]; // 入参变量类型
var argName = config.ParameterName[i]; // 入参参数名称
#region ID
if (config.UseMsgId[i])
{
args[i] = msgId;
}
#endregion
#region DATA JSON数据
else if (config.UseRequest[i])
{
args[i] = context.MsgRequest?.ToObject(type);
}
#endregion
#region DATA JSON数据
else if (config.UseData[i])
{
args[i] = context.MsgData?.ToObject(type);
}
#endregion
#region
else if (!config.IsNeedSendDelegate[i])
{
var jsonValue = context.MsgData?.GetValue(argName);
if(jsonValue is null)
{
isCanInvoke = false;
exTips.Add($"参数 {argName}({i}) 不存在,请检查参数名称是否正确");
continue;
}
var data = jsonValue.ToObject(type);
if (data is null)
{
isCanInvoke = false;
exTips.Add($"参数 {argName}({i}) 解析失败,类型:{type.FullName},值:{jsonValue},请检查参数类型是否正确");
continue;
}
args[i] = data;
}
#endregion
#region
else if (config.UseContent[i])
{
args[i] = context;
}
#endregion
#region
else if (config.IsNeedSendDelegate[i]) // 传递SendAsync委托
{
if (config.CachedSendDelegates != null && config.CachedSendDelegates[i] != null)
{
args[i] = config.CachedSendDelegates[i];
continue;
}
Delegate? del = null;
var st = config.SendDelegateType[i];
switch (st)
{
case SereinWebSocketService.SendType.ObjectAsync:
del = new Func<object, Task>(async data =>
{
var jsonText = JsonHelper.Serialize(data);
await context.SendAsync(jsonText);
});
break;
case SereinWebSocketService.SendType.StringAsync:
del = new Func<string, Task>(async data =>
{
await context.SendAsync(data);
});
break;
case SereinWebSocketService.SendType.Object:
del = new Action<object>(data =>
{
var jsonText = JsonHelper.Serialize(data);
_ = context.SendAsync(jsonText);
});
break;
case SereinWebSocketService.SendType.String:
del = new Action<string>(data =>
{
_ = context.SendAsync(data);
});
break;
}
if (del is not null)
{
config.CachedSendDelegates![i] = del;
args[i] = del;
}
else
{
isCanInvoke = false; // 方法要求参数不能为空,终止调用
exTips.Add($"参数 {argName}({i}) 发送委托类型错误");
break;
}
}
#endregion
}
if (!isCanInvoke)
{
string ex = string.Join(Environment.NewLine, exTips);
context.TriggerExceptionTracking(ex);
}
return isCanInvoke;
}
/// <summary>
/// 返回消息
/// </summary>
/// <param name="moduleConfig"></param>
/// <param name="context"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task RepliedAsync(WebSocketModuleConfig moduleConfig,
WebSocketHandleContext context,
object data)
{
if (context.OnExceptionTracking is null)
{
context.TriggerExceptionTracking(new NullReferenceException($"没有定义处理回复消息 OnExceptionTracking 回调函数"));
return;
}
// 返回结果
var responseData = context.OnReplyMakeData?.Invoke(context, data);
if (responseData is null)
{
context.TriggerExceptionTracking(new ArgumentNullException($"处理回调函数 OnReplyMakeData 返回 null"));
return;
}
var responseContent = JsonHelper.Serialize(responseData);
await context.SendAsync(responseContent);
/* if (moduleConfig.IsResponseUseReturn)
{
var responseContent = JsonHelper.Serialize(data);
await context.SendAsync(responseContent);
}
else
{
IJsonToken jsonData;
jsonData = JsonHelper.Object(obj =>
{
obj[moduleConfig.MsgIdJsonKey] = context.MsgId;
obj[moduleConfig.ThemeJsonKey] = context.MsgTheme;
obj[moduleConfig.DataJsonKey] = data is null ? null : JsonHelper.FromObject(data);
});
var msg = jsonData.ToString();
await context.SendAsync(msg);
}*/
}
}
}