using System.Text.Json; using XFEEDToolBox.Core.Models; namespace XFEEDToolBox.Core.Data; public static class MarketParser { public static MarketSnapshot ParseLocalMarket(JsonElement root, Station? known = null, DataSource source = DataSource.Journal) { var time = root.Time() ?? throw new JsonException("行情缺少 timestamp。"); var id = root.Long("MarketID"); if (id <= 0) throw new JsonException("行情缺少有效 MarketID。"); var type = root.Text("StationType"); var station = (known?.MarketId == id ? known : new Station())! with { MarketId = id, Name = root.Text("StationName"), SystemName = root.Text("StarSystem"), Type = type, IsFleetCarrier = type.Equals("FleetCarrier", StringComparison.OrdinalIgnoreCase), IsPlanetary = type.StartsWith("Crater", StringComparison.OrdinalIgnoreCase), MaxLandingPad = known?.MaxLandingPad is > LandingPad.Unknown ? known.MaxLandingPad : JsonFields.PadFromType(type), UpdatedAt = time }; var items = root.Get("Items"); if (items.ValueKind != JsonValueKind.Array) throw new JsonException("行情缺少 Items 数组。"); return new(station, items.Items().Select(x => ParseQuote(x, id, time, source)).Where(x => x.Commodity.Length > 0).ToArray(), time, source); } public static MarketSnapshot? ParseEddn(JsonElement root) { var schema = root.Text("$schemaRef"); if (!schema.EndsWith("/commodity/3", StringComparison.Ordinal) || !Uri.TryCreate(schema, UriKind.Absolute, out var uri) || uri.Host != "eddn.edcd.io") return null; var header = root.Get("header"); var version = header.Text("gameversion"); if (version.StartsWith("3.", StringComparison.Ordinal) || version.Contains("Legacy", StringComparison.OrdinalIgnoreCase)) return null; var message = root.Get("message"); var time = message.Time() ?? throw new JsonException("EDDN 行情缺少时间。"); if (time > DateTimeOffset.UtcNow.AddMinutes(5) || time < DateTimeOffset.UtcNow.AddDays(-30)) return null; var id = message.Long("marketId"); if (id <= 0 || string.IsNullOrWhiteSpace(message.Text("systemName")) || string.IsNullOrWhiteSpace(message.Text("stationName"))) return null; if (message.Get("commodities").ValueKind != JsonValueKind.Array) throw new JsonException("EDDN 行情缺少 commodities。"); var type = message.Text("stationType"); var station = new Station { MarketId = id, Name = message.Text("stationName"), SystemName = message.Text("systemName"), Type = type, MaxLandingPad = JsonFields.PadFromType(type), IsFleetCarrier = type.Equals("FleetCarrier", StringComparison.OrdinalIgnoreCase), IsPlanetary = type.StartsWith("Crater", StringComparison.OrdinalIgnoreCase), IsRestricted = message.Text("carrierDockingAccess") is "friends" or "squadron" or "none", UpdatedAt = time }; var prohibited = message.Get("prohibited").Items().Where(x => x.ValueKind == JsonValueKind.String).Select(x => CommodityCatalog.Key(x.GetString()!)).ToHashSet(); var quotes = message.Get("commodities").Items().Select(x => ParseQuote(x, id, time, DataSource.Eddn)) .Where(x => x.Commodity.Length > 0).Select(x => x with { Illegal = x.Illegal || prohibited.Contains(x.Commodity) }).ToArray(); return new(station, quotes, time, DataSource.Eddn); } public static CommodityQuote ParseQuote(JsonElement item, long id, DateTimeOffset time, DataSource source) { var name = item.Text("Name"); var key = CommodityCatalog.Key(name, item.Long("id", item.Long("commodityId")), item.Text("symbol")); var illegal = item.Get("statusFlags").Items().Any(x => x.ValueKind == JsonValueKind.String && x.GetString()!.Contains("illegal", StringComparison.OrdinalIgnoreCase)); return new() { MarketId = id, Commodity = key, DisplayName = item.Text("Name_Localised", name.Trim('$', ';').Replace("_name", "")), Category = item.Text("Category_Localised", item.Text("categoryname", item.Text("Category"))), BuyPrice = Math.Max(0, item.Long("buyPrice")), SellPrice = Math.Max(0, item.Long("sellPrice")), MeanPrice = Math.Max(0, item.Long("meanPrice")), Supply = Math.Max(0, item.Long("stock", item.Long("supply"))), Demand = Math.Max(0, item.Long("demand")), Illegal = illegal || item.Flag("illegal") || item.Flag("isIllegal") || item.Text("legality").Equals("illegal", StringComparison.OrdinalIgnoreCase), Rare = item.Flag("rare") || item.Flag("isRare"), CollectedAt = time, Source = source }; } }