using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using Oms.Application.Dtos.Orders; using Oms.Domain.OrdersAggregate.Consts; using Oms.Domain.SystemAggregate.Consts; namespace Oms.Application.Shared; /// /// OMS 通用文档与展示辅助方法。 /// public static class OmsDocHelper { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true }; private static readonly JsonSerializerOptions SnapshotJsonOptions = new() { PropertyNameCaseInsensitive = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; /// /// 将页码规范为至少 1。 /// public static int NormalizePage(int page) => page <= 0 ? 1 : page; /// /// 将每页条数规范为 1~200。 /// public static int NormalizePageSize(int pageSize) => pageSize <= 0 ? 20 : Math.Min(pageSize, 200); /// /// 将默认 DateTime 转为 null。 /// public static DateTime? NullIfDefault(DateTime value) => value == default ? null : value; /// /// 将库内 JSON 字符串转为 API 可输出的 CLR 对象(避免 JsonElement 被 Newtonsoft 序列化成 valueKind)。 /// public static object? DeserializeJsonObject(string? json) { if (string.IsNullOrWhiteSpace(json)) { return null; } using var document = JsonDocument.Parse(json); return ConvertJsonElement(document.RootElement); } /// /// 反序列化 JSON 字符串为指定类型。 /// public static T? DeserializeJson(string? json) { if (string.IsNullOrWhiteSpace(json)) { return default; } return JsonSerializer.Deserialize(json, JsonOptions); } /// /// 将请求体中的动态 JSON(JsonElement / JToken / Dictionary 等)规范为可落库的 JSON 字符串。 /// public static string? SerializeJson(T? value) { if (value is null) { return null; } if (value is JsonElement element) { return element.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined ? null : element.GetRawText(); } if (value is JsonNode node) { return node.ToJsonString(); } if (value is string text) { return string.IsNullOrWhiteSpace(text) ? null : text; } var typeName = value.GetType().FullName; if (typeName is not null && typeName.StartsWith("Newtonsoft.Json.Linq.", StringComparison.Ordinal)) { return value.ToString(); } return JsonSerializer.Serialize(value, JsonOptions); } /// /// 序列化发布快照(忽略 null,避免写出旧兼容字段)。 /// public static string? SerializeSnapshotJson(T? value) { if (value is null) { return null; } return JsonSerializer.Serialize(value, SnapshotJsonOptions); } /// /// 将快照对象转为 API 输出结构(与落库 JSON 同构,避免预览/列表/落库序列化不一致)。 /// public static object? ToSnapshotApiContent(T? value) { if (value is null) { return null; } var json = SerializeSnapshotJson(value); return string.IsNullOrWhiteSpace(json) ? null : DeserializeJsonObject(json); } public static SalesProductRulesPublishSnapshotDto? DeserializeRulesSnapshot(string? json) { var snapshot = DeserializeJson(json); PublishSnapshotNormalizer.NormalizeRulesSnapshot(snapshot); return snapshot; } public static SalesProductPricePublishSnapshotDto? DeserializePriceSnapshot(string? json) { var snapshot = DeserializeJson(json); PublishSnapshotNormalizer.NormalizePriceSnapshot(snapshot); return snapshot; } public static ServiceChannelPricePublishSnapshotDto? DeserializeChannelPriceSnapshot(string? json) { var snapshot = DeserializeJson(json); PublishSnapshotNormalizer.NormalizeChannelPriceSnapshot(snapshot); return snapshot; } /// /// 同步态对比用:先序列化再反序列化规范化,消除 Dictionary/JsonElement、字段顺序等噪声。 /// public static string CanonicalizeSalesProductPriceSnapshotJson(object? snapshot) { var raw = SerializeSnapshotJson(snapshot) ?? string.Empty; return SerializeSnapshotJson(DeserializePriceSnapshot(raw)) ?? string.Empty; } /// /// 同步态对比用:规则快照规范化 canonical JSON。 /// public static string CanonicalizeSalesProductRulesSnapshotJson(object? snapshot) { var raw = SerializeSnapshotJson(snapshot) ?? string.Empty; return SerializeSnapshotJson(DeserializeRulesSnapshot(raw)) ?? string.Empty; } /// /// 同步态对比用:渠道买价快照规范化 canonical JSON。 /// public static string CanonicalizeChannelPriceSnapshotJson(object? snapshot) { var raw = SerializeSnapshotJson(snapshot) ?? string.Empty; return SerializeSnapshotJson(DeserializeChannelPriceSnapshot(raw)) ?? string.Empty; } /// /// 发布同步态哈希:canonical + 去掉 PackVersion/AreaName 等钉包展示字段。 /// public static string HashSalesProductPriceSnapshotForSync(object? snapshot) { var dto = DeserializePriceSnapshot(SerializeSnapshotJson(snapshot)); PublishSnapshotNormalizer.StripVolatilePackPinsForSyncCompare(dto); return SerializeSnapshotJson(dto) ?? string.Empty; } public static string HashSalesProductRulesSnapshotForSync(object? snapshot) { var dto = DeserializeRulesSnapshot(SerializeSnapshotJson(snapshot)); PublishSnapshotNormalizer.StripVolatilePackPinsForSyncCompare(dto); return SerializeSnapshotJson(dto) ?? string.Empty; } public static string HashChannelPriceSnapshotForSync(object? snapshot) { var dto = DeserializeChannelPriceSnapshot(SerializeSnapshotJson(snapshot)); PublishSnapshotNormalizer.StripVolatilePackPinsForSyncCompare(dto); return SerializeSnapshotJson(dto) ?? string.Empty; } /// /// 解析渠道买价发布 Content,供列表/对比 API 使用(兼容旧快照审计字段)。 /// public static object? ParseChannelPricePublishContentForApi(string? json) { var snapshot = DeserializeChannelPriceSnapshot(json); return snapshot is null ? DeserializeJsonObject(json) : ToSnapshotApiContent(snapshot); } /// /// 解析产品销售卖价发布 Content,供列表/对比 API 使用。 /// public static object? ParseSalesProductPricePublishContentForApi(string? json) { var snapshot = DeserializePriceSnapshot(json); return snapshot is null ? DeserializeJsonObject(json) : ToSnapshotApiContent(snapshot); } private static object? ConvertJsonElement(JsonElement element) => element.ValueKind switch { JsonValueKind.Object => element.EnumerateObject() .ToDictionary(property => property.Name, property => ConvertJsonElement(property.Value)), JsonValueKind.Array => element.EnumerateArray() .Select(ConvertJsonElement) .ToList(), JsonValueKind.String => element.GetString(), JsonValueKind.Number => element.TryGetInt64(out var integer) ? integer : element.GetDecimal(), JsonValueKind.True => true, JsonValueKind.False => false, JsonValueKind.Null or JsonValueKind.Undefined => null, _ => element.GetRawText() }; /// /// 反序列化 JSON 字符串数组。 /// public static List DeserializeStringList(string? json) => JsonStringArrayHelper.Deserialize(json); /// /// 将订单状态码转为中文名称。 /// public static string? GetOrderStatusName(string statusCode) { return statusCode switch { OrderStatusCodes.Draft => "草稿", OrderStatusCodes.Predeclared => "已预报", OrderStatusCodes.PendingAllocation => "待排货", OrderStatusCodes.PendingPayment => "待支付", OrderStatusCodes.PendingShip => "待发货", OrderStatusCodes.PendingOnline => "待上网", OrderStatusCodes.Shipping => "运输中", OrderStatusCodes.DeliverySuccess => "派送成功", OrderStatusCodes.DeliveryFailed => "派送失败", OrderStatusCodes.Problematic => "问题件", OrderStatusCodes.Deleted => "已删除", OrderStatusCodes.Claimed => "理赔件", _ => statusCode }; } /// /// 将仓库类型码转为中文名称。 /// public static string GetWarehouseTypeName(string warehouseType) { return warehouseType switch { WarehouseTypeCodes.Main => "主仓", WarehouseTypeCodes.Transit => "中转仓", WarehouseTypeCodes.Return => "退货仓", WarehouseTypeCodes.Other => "其他", _ => warehouseType }; } }