XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEToolBox

【WPF】XFE工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;
using System.Text.Json.Serialization;

namespace XFEToolBox.Server.Realtime;

public sealed class ChatRealtimeEnvelope
{
    public const int CurrentVersion = 1;

    public int Version { get; init; } = CurrentVersion;

    public string EventId { get; init; } = Guid.NewGuid().ToString("N");

    public string Type { get; init; } = string.Empty;

    public DateTimeOffset OccurredAtUtc { get; init; } = DateTimeOffset.UtcNow;

    public string? ConversationId { get; init; }

    public string? ActorUserId { get; init; }

    public JsonElement Payload { get; init; }

    [JsonIgnore]
    public bool HasPayload => Payload.ValueKind is not JsonValueKind.Undefined and not JsonValueKind.Null;
}

internal static class ChatRealtimeProtocol
{
    public static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
    {
        PropertyNameCaseInsensitive = true
    };

    public static ChatRealtimeEnvelope Create(
        string type,
        object? payload = null,
        string? actorUserId = null,
        string? conversationId = null) => new()
    {
        Type = type,
        ActorUserId = actorUserId,
        ConversationId = conversationId,
        Payload = JsonSerializer.SerializeToElement(payload ?? new { }, JsonOptions)
    };

    public static bool TryParse(string json, out ChatRealtimeEnvelope envelope, out string error)
    {
        envelope = default!;
        error = string.Empty;
        try
        {
            var parsed = JsonSerializer.Deserialize<ChatRealtimeEnvelope>(json, JsonOptions);
            if (parsed is null)
            {
                error = "消息体为空。";
                return false;
            }

            if (parsed.Version != ChatRealtimeEnvelope.CurrentVersion)
            {
                error = "不支持的实时协议版本。";
                return false;
            }

            if (string.IsNullOrWhiteSpace(parsed.Type) || parsed.Type.Length > 64 ||
                !parsed.Type.All(character => char.IsLetterOrDigit(character) || character is '.' or '-' or '_'))
            {
                error = "事件类型无效。";
                return false;
            }

            if (string.IsNullOrWhiteSpace(parsed.EventId) || parsed.EventId.Length > 96)
            {
                error = "事件标识无效。";
                return false;
            }

            envelope = parsed;
            return true;
        }
        catch (JsonException)
        {
            error = "消息不是有效的 JSON。";
            return false;
        }
    }

    public static bool TryGetRequiredString(JsonElement payload, string propertyName, out string value)
    {
        value = string.Empty;
        if (payload.ValueKind != JsonValueKind.Object ||
            !payload.TryGetProperty(propertyName, out var property) ||
            property.ValueKind != JsonValueKind.String)
            return false;

        value = property.GetString()?.Trim() ?? string.Empty;
        return value.Length > 0;
    }

    public static string? GetOptionalString(JsonElement payload, string propertyName)
    {
        if (payload.ValueKind != JsonValueKind.Object ||
            !payload.TryGetProperty(propertyName, out var property) ||
            property.ValueKind != JsonValueKind.String)
            return null;
        var value = property.GetString()?.Trim();
        return string.IsNullOrWhiteSpace(value) ? null : value;
    }
}