using System.Net; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; namespace LicenseSdk; /// /// License System V5 授权服务 .NET 客户端。 /// 封装后台 /api/card/* 接口:登录验证、心跳、设备列表、解绑。 /// 提供两套等价方法: /// - LoginAsync / HeartbeatAsync / ListDevicesAsync / UnbindAsync:返回 JSON 文档,非 2xx 抛异常(兼容旧版); /// - LoginResponseAsync 等带 ResponseAsync 后缀方法:返回 HTTP 状态码 + 原始响应体(便于展示服务端业务错误)。 /// public sealed class LicenseClient { private readonly HttpClient http; private readonly string machineCode; private static readonly JsonSerializerOptions JsonOptions = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; public LicenseClient(HttpClient http, string machineCode) { this.http = http; this.machineCode = machineCode.Trim().ToUpperInvariant(); } public Task LoginAsync(string cardKey, string? deviceName = null) => PostAsync("/api/card/verify", new { cardKey, machineCode, deviceName }); public Task HeartbeatAsync(string cardKey) => PostAsync("/api/card/heartbeat", new { cardKey, machineCode }); public Task ListDevicesAsync(string cardKey) => PostAsync("/api/card/devices", new { cardKey, machineCode }); public Task UnbindAsync(string cardKey) => PostAsync("/api/card/unbind", new { cardKey, machineCode }); public Task LoginResponseAsync(string cardKey, string? deviceName = null) => PostResponseAsync("/api/card/verify", new { cardKey, machineCode, deviceName }); public Task HeartbeatResponseAsync(string cardKey) => PostResponseAsync("/api/card/heartbeat", new { cardKey, machineCode }); public Task ListDevicesResponseAsync(string cardKey) => PostResponseAsync("/api/card/devices", new { cardKey, machineCode }); public Task UnbindResponseAsync(string cardKey) => PostResponseAsync("/api/card/unbind", new { cardKey, machineCode }); private async Task PostAsync(string path, object payload) { using var response = await http.PostAsJsonAsync(path, payload, JsonOptions); response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsync(); } private async Task PostResponseAsync(string path, object payload) { using var response = await http.PostAsJsonAsync(path, payload, JsonOptions); string raw = await response.Content.ReadAsStringAsync(); JsonDocument? doc = null; if (!string.IsNullOrWhiteSpace(raw)) { try { doc = JsonDocument.Parse(raw); } catch (JsonException) { /* 非 JSON 响应,保留原始文本 */ } } return new ApiResponse(response.StatusCode, raw, doc); } } /// 非 2xx 不抛异常的响应包装:HTTP 状态码 + 原始响应体 + 尽力解析的 JSON 文档。 public sealed class ApiResponse { public HttpStatusCode StatusCode { get; } public string RawBody { get; } public JsonDocument? Doc { get; } public ApiResponse(HttpStatusCode statusCode, string rawBody, JsonDocument? doc) { StatusCode = statusCode; RawBody = rawBody; Doc = doc; } public bool IsSuccess => (int)StatusCode is >= 200 and < 300; public bool OkFlag => Doc is { } d && d.RootElement.TryGetProperty("ok", out var ok) && ok.ValueKind == JsonValueKind.True; }