Files
ZeroSpace/ZeroSpace/ZeroSpace.Web_Backup_2026.09.28_11.24.30/Services/UnitsApiClient.cs
T

279 lines
8.3 KiB
C#

using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using ZeroSpace.Model;
namespace ZeroSpace.Web.Services;
public class UnitsApiClient : IUnitsApiClient
{
private readonly HttpClient _httpClient;
private readonly ILogger<UnitsApiClient> _logger;
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true
};
private const string AllUnitFields = @"
id
name
hexite
flux
supply
productionTime
health
energy
shields
armorRating
movementSpeed
damagePerSecond
attackRange
attributes
tier
faction
hotkey
buildAtSameTime
limit";
public UnitsApiClient(HttpClient httpClient, ILogger<UnitsApiClient> logger)
{
_httpClient = httpClient;
_logger = logger;
}
public async Task<IReadOnlyList<UnitData>> GetUnitsAsync(string? faction = null, int? tier = null, CancellationToken cancellationToken = default)
{
try
{
Dictionary<string, object?>? whereFilter = null;
if (!string.IsNullOrWhiteSpace(faction) || tier.HasValue)
{
whereFilter = new Dictionary<string, object?>();
if (!string.IsNullOrWhiteSpace(faction))
{
whereFilter["faction"] = new Dictionary<string, object?> { ["eq"] = faction };
}
if (tier.HasValue)
{
whereFilter["tier"] = new Dictionary<string, object?> { ["eq"] = tier.Value };
}
}
var query = $@"
query GetUnits($where: UnitDataFilterInput) {{
units(where: $where) {{
{AllUnitFields}
}}
}}";
var requestBody = new
{
query,
variables = new
{
where = whereFilter
}
};
var response = await _httpClient.PostAsJsonAsync("graphql", requestBody, cancellationToken);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<GraphQLResponse<UnitsQueryResult>>(JsonOptions, cancellationToken);
if (result?.Data?.Units is not null && result.Data.Units.Count > 0)
{
return result.Data.Units;
}
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load units via GraphQL API service. Falling back to static units list.");
}
// Fallback to static model data if GraphQL call fails
var fallback = Units.All.AsEnumerable();
if (!string.IsNullOrWhiteSpace(faction))
{
fallback = fallback.Where(u => u.Faction.Equals(faction, StringComparison.OrdinalIgnoreCase));
}
if (tier.HasValue)
{
fallback = fallback.Where(u => u.Tier == tier.Value);
}
return fallback.ToList();
}
public async Task<UnitData?> GetUnitByIdAsync(string id, CancellationToken cancellationToken = default)
{
try
{
var query = $@"
query GetUnitById($id: String!) {{
unitById(id: $id) {{
{AllUnitFields}
}}
}}";
var requestBody = new
{
query,
variables = new
{
id
}
};
var response = await _httpClient.PostAsJsonAsync("graphql", requestBody, cancellationToken);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<GraphQLResponse<UnitByIdQueryResult>>(JsonOptions, cancellationToken);
if (result?.Data?.UnitById is not null)
{
return result.Data.UnitById;
}
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to get unit '{Id}' via GraphQL API.", id);
}
return Units.All.FirstOrDefault(u => u.Id == id);
}
public async Task<UnitData?> CreateUnitAsync(UnitData unit, CancellationToken cancellationToken = default)
{
try
{
var mutation = $@"
mutation CreateUnit($input: CreateUnitInput!) {{
createUnit(input: $input) {{
{AllUnitFields}
}}
}}";
var input = new
{
id = unit.Id,
name = unit.Name,
hexite = unit.Hexite,
flux = unit.Flux,
supply = unit.Supply,
productionTime = unit.ProductionTime,
health = unit.Health,
energy = unit.Energy,
shields = unit.Shields,
armorRating = unit.ArmorRating,
movementSpeed = unit.MovementSpeed,
damagePerSecond = unit.DamagePerSecond,
attackRange = unit.AttackRange,
attributes = unit.Attributes,
tier = unit.Tier,
faction = unit.Faction,
hotkey = unit.Hotkey,
buildAtSameTime = unit.BuildAtSameTime,
limit = unit.Limit
};
var requestBody = new
{
query = mutation,
variables = new
{
input
}
};
var response = await _httpClient.PostAsJsonAsync("graphql", requestBody, cancellationToken);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<GraphQLResponse<CreateUnitMutationResult>>(JsonOptions, cancellationToken);
return result?.Data?.CreateUnit;
}
_logger.LogError("Failed to create unit via GraphQL. Status: {StatusCode}", response.StatusCode);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating unit via GraphQL.");
}
return null;
}
public async Task<bool> DeleteUnitAsync(string id, CancellationToken cancellationToken = default)
{
try
{
var mutation = @"
mutation DeleteUnit($id: String!) {
deleteUnit(id: $id)
}";
var requestBody = new
{
query = mutation,
variables = new
{
id
}
};
var response = await _httpClient.PostAsJsonAsync("graphql", requestBody, cancellationToken);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<GraphQLResponse<DeleteUnitMutationResult>>(JsonOptions, cancellationToken);
return result?.Data?.DeleteUnit ?? false;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting unit '{Id}' via GraphQL.", id);
}
return false;
}
private sealed class GraphQLResponse<T>
{
[JsonPropertyName("data")]
public T? Data { get; set; }
[JsonPropertyName("errors")]
public List<GraphQLErrorItem>? Errors { get; set; }
}
private sealed class GraphQLErrorItem
{
[JsonPropertyName("message")]
public string Message { get; set; } = string.Empty;
}
private sealed class UnitsQueryResult
{
[JsonPropertyName("units")]
public List<UnitData>? Units { get; set; }
}
private sealed class UnitByIdQueryResult
{
[JsonPropertyName("unitById")]
public UnitData? UnitById { get; set; }
}
private sealed class CreateUnitMutationResult
{
[JsonPropertyName("createUnit")]
public UnitData? CreateUnit { get; set; }
}
private sealed class DeleteUnitMutationResult
{
[JsonPropertyName("deleteUnit")]
public bool DeleteUnit { get; set; }
}
}