334 lines
9.1 KiB
C#
334 lines
9.1 KiB
C#
using AyCode.Core.Extensions;
|
|
using MessagePack;
|
|
using Newtonsoft.Json.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using AyCode.Core.Interfaces;
|
|
using System.Collections.Generic;
|
|
using System.Linq.Expressions;
|
|
|
|
namespace AyCode.Services.SignalRs;
|
|
|
|
public class IdMessage
|
|
{
|
|
public List<string> Ids { get; private set; }
|
|
|
|
public IdMessage()
|
|
{
|
|
Ids = [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates IdMessage with multiple parameters serialized directly as JSON.
|
|
/// Each parameter is serialized independently without array wrapping.
|
|
/// Use object[] explicitly to pass multiple parameters.
|
|
/// </summary>
|
|
public IdMessage(object[] ids)
|
|
{
|
|
// Pre-allocate capacity to avoid list resizing
|
|
Ids = new List<string>(ids.Length);
|
|
for (var i = 0; i < ids.Length; i++)
|
|
{
|
|
Ids.Add(ids[i].ToJson());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates IdMessage with a single parameter serialized as JSON.
|
|
/// Collections (List, Array, etc.) are serialized as a single JSON array.
|
|
/// </summary>
|
|
public IdMessage(object id)
|
|
{
|
|
// Pre-allocate for single item
|
|
Ids = new List<string>(1) { id.ToJson() };
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates IdMessage with multiple Guid parameters.
|
|
/// Each Guid is serialized as a separate Id entry.
|
|
/// </summary>
|
|
public IdMessage(IEnumerable<Guid> ids)
|
|
{
|
|
// Materialize to array once to get count and avoid multiple enumeration
|
|
var idsArray = ids as Guid[] ?? ids.ToArray();
|
|
Ids = new List<string>(idsArray.Length);
|
|
for (var i = 0; i < idsArray.Length; i++)
|
|
{
|
|
Ids.Add(idsArray[i].ToJson());
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Join("; ", Ids);
|
|
}
|
|
}
|
|
|
|
[MessagePackObject]
|
|
public class SignalPostJsonMessage
|
|
{
|
|
[Key(0)]
|
|
public string PostDataJson { get; set; } = "";
|
|
|
|
public SignalPostJsonMessage()
|
|
{}
|
|
protected SignalPostJsonMessage(string postDataJson) => PostDataJson = postDataJson;
|
|
}
|
|
|
|
[MessagePackObject(AllowPrivate = false)]
|
|
public class SignalPostJsonDataMessage<TPostDataType> : SignalPostJsonMessage, ISignalPostMessage<TPostDataType> //where TPostDataType : class
|
|
{
|
|
[IgnoreMember]
|
|
private TPostDataType? _postData;
|
|
|
|
[IgnoreMember]
|
|
public TPostDataType PostData
|
|
{
|
|
get
|
|
{
|
|
return _postData ??= PostDataJson.JsonTo<TPostDataType>()!;
|
|
}
|
|
private init
|
|
{
|
|
_postData = value;
|
|
PostDataJson = _postData.ToJson();
|
|
}
|
|
}
|
|
|
|
public SignalPostJsonDataMessage() : base()
|
|
{}
|
|
public SignalPostJsonDataMessage(TPostDataType postData) => PostData = postData;
|
|
public SignalPostJsonDataMessage(string postDataJson) : base(postDataJson)
|
|
{}
|
|
}
|
|
|
|
[MessagePackObject]
|
|
public class SignalPostMessage<TPostData>(TPostData postData) : ISignalPostMessage<TPostData>
|
|
{
|
|
[Key(0)]
|
|
public TPostData? PostData { get; set; } = postData;
|
|
}
|
|
|
|
public interface ISignalPostMessage<TPostData> : ISignalRMessage
|
|
{
|
|
TPostData? PostData { get; }
|
|
}
|
|
|
|
[MessagePackObject]
|
|
public class SignalRequestByIdMessage(Guid id) : ISignalRequestMessage<Guid>, IId<Guid>
|
|
{
|
|
[Key(0)]
|
|
public Guid Id { get; set; } = id;
|
|
}
|
|
|
|
public interface ISignalRequestMessage<TRequestId> : ISignalRMessage
|
|
{
|
|
TRequestId Id { get; set; }
|
|
}
|
|
|
|
public interface ISignalRMessage
|
|
{ }
|
|
|
|
|
|
[MessagePackObject]
|
|
public sealed class SignalResponseJsonMessage : ISignalResponseMessage<string>
|
|
{
|
|
[Key(0)] public int MessageTag { get; set; }
|
|
|
|
[Key(1)] public SignalResponseStatus Status { get; set; }
|
|
|
|
[Key(2)] public string? ResponseData { get; set; } = null;
|
|
|
|
[IgnoreMember]
|
|
public string? ResponseDataJson => ResponseData;
|
|
|
|
public SignalResponseJsonMessage(){}
|
|
|
|
public SignalResponseJsonMessage(int messageTag, SignalResponseStatus status)
|
|
{
|
|
Status = status;
|
|
MessageTag = messageTag;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a response with the given data serialized as JSON.
|
|
/// If responseData is already a JSON string (starts with { or [), it will be used directly.
|
|
/// All other data types are serialized to JSON format.
|
|
/// </summary>
|
|
public SignalResponseJsonMessage(int messageTag, SignalResponseStatus status, object? responseData) : this(messageTag, status)
|
|
{
|
|
if (responseData == null)
|
|
{
|
|
ResponseData = null;
|
|
return;
|
|
}
|
|
|
|
// If responseData is already a JSON string, use it directly
|
|
if (responseData is string strData)
|
|
{
|
|
var trimmed = strData.Trim();
|
|
if (trimmed.Length > 1 && (trimmed[0] == '{' || trimmed[0] == '[') && (trimmed[^1] == '}' || trimmed[^1] == ']'))
|
|
{
|
|
// Already JSON - use directly without re-serialization
|
|
ResponseData = strData;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Serialize to JSON
|
|
ResponseData = responseData.ToJson();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Signal response message with lazy deserialization support.
|
|
/// ResponseData is only deserialized on first access and cached.
|
|
/// Use ResponseDataJson for direct JSON access without deserialization.
|
|
/// </summary>
|
|
[MessagePackObject(AllowPrivate = false)]
|
|
public sealed class SignalResponseMessage<TResponseData> : ISignalResponseMessage<TResponseData>
|
|
{
|
|
[IgnoreMember]
|
|
private TResponseData? _responseData;
|
|
|
|
[IgnoreMember]
|
|
private bool _isDeserialized;
|
|
|
|
[Key(0)]
|
|
public int MessageTag { get; set; }
|
|
|
|
[Key(1)]
|
|
public SignalResponseStatus Status { get; set; }
|
|
|
|
/// <summary>
|
|
/// Raw JSON string. Use this for direct JSON access without triggering deserialization.
|
|
/// </summary>
|
|
[Key(2)]
|
|
public string? ResponseDataJson { get; set; }
|
|
|
|
/// <summary>
|
|
/// Deserialized response data. Lazy-loaded on first access.
|
|
/// </summary>
|
|
[IgnoreMember]
|
|
public TResponseData? ResponseData
|
|
{
|
|
get
|
|
{
|
|
if (!_isDeserialized)
|
|
{
|
|
_isDeserialized = true;
|
|
|
|
_responseData = ResponseDataJson != null
|
|
? ResponseDataJson.JsonTo<TResponseData>()
|
|
: default;
|
|
}
|
|
|
|
return _responseData;
|
|
}
|
|
set
|
|
{
|
|
_isDeserialized = true;
|
|
_responseData = value;
|
|
ResponseDataJson = value?.ToJson();
|
|
}
|
|
}
|
|
|
|
public SignalResponseMessage()
|
|
{
|
|
}
|
|
|
|
public SignalResponseMessage(int messageTag, SignalResponseStatus status)
|
|
{
|
|
MessageTag = messageTag;
|
|
Status = status;
|
|
}
|
|
|
|
public SignalResponseMessage(int messageTag, SignalResponseStatus status, TResponseData? responseData)
|
|
: this(messageTag, status)
|
|
{
|
|
ResponseData = responseData;
|
|
}
|
|
|
|
public SignalResponseMessage(int messageTag, SignalResponseStatus status, string? responseDataJson)
|
|
: this(messageTag, status)
|
|
{
|
|
ResponseDataJson = responseDataJson;
|
|
}
|
|
}
|
|
|
|
public interface ISignalResponseMessage<TResponseData> : ISignalResponseMessage
|
|
{
|
|
/// <summary>
|
|
/// Deserialized response data. May trigger lazy deserialization.
|
|
/// </summary>
|
|
TResponseData? ResponseData { get; set; }
|
|
|
|
/// <summary>
|
|
/// Raw JSON string for direct access without deserialization.
|
|
/// </summary>
|
|
string? ResponseDataJson { get; }
|
|
}
|
|
|
|
public interface ISignalResponseMessage : ISignalRMessage
|
|
{
|
|
int MessageTag { get; set; }
|
|
SignalResponseStatus Status { get; set; }
|
|
}
|
|
|
|
public enum SignalResponseStatus : byte
|
|
{
|
|
Error = 0,
|
|
Success = 5
|
|
}
|
|
|
|
/// <summary>
|
|
/// Signal response message with binary serialized data.
|
|
/// Used when SerializerOptions.SerializerType == Binary for better performance.
|
|
/// </summary>
|
|
[MessagePackObject]
|
|
public sealed class SignalResponseBinaryMessage : ISignalResponseMessage<byte[]>
|
|
{
|
|
[Key(0)] public int MessageTag { get; set; }
|
|
|
|
[Key(1)] public SignalResponseStatus Status { get; set; }
|
|
|
|
[Key(2)] public byte[]? ResponseData { get; set; }
|
|
|
|
[IgnoreMember]
|
|
public string? ResponseDataJson => ResponseData != null ? Convert.ToBase64String(ResponseData) : null;
|
|
|
|
public SignalResponseBinaryMessage() { }
|
|
|
|
public SignalResponseBinaryMessage(int messageTag, SignalResponseStatus status)
|
|
{
|
|
Status = status;
|
|
MessageTag = messageTag;
|
|
}
|
|
|
|
public SignalResponseBinaryMessage(int messageTag, SignalResponseStatus status, object? responseData, AcBinarySerializerOptions? options = null)
|
|
: this(messageTag, status)
|
|
{
|
|
if (responseData == null)
|
|
{
|
|
ResponseData = null;
|
|
return;
|
|
}
|
|
|
|
// If responseData is already a byte array, use it directly
|
|
if (responseData is byte[] byteData)
|
|
{
|
|
ResponseData = byteData;
|
|
return;
|
|
}
|
|
|
|
// Serialize to binary
|
|
ResponseData = options != null
|
|
? responseData.ToBinary(options)
|
|
: responseData.ToBinary();
|
|
}
|
|
}
|
|
|
|
public interface IAcSignalRHubClient : IAcSignalRHubBase
|
|
{
|
|
Task SendMessageToServerAsync(int messageTag, ISignalRMessage? message, int? requestId );
|
|
//Task SendRequestToServerAsync(int messageTag, int requestId);
|
|
} |