using System.Text.Json; namespace WxAgent.Service; public sealed record AccountBinding(string AccountId, int ProcessId, long WindowHandle, string? WechatId, string Nickname, DateTimeOffset BoundAt); public sealed record UiTargetInfo(string TargetId, int ProcessId, long WindowHandle, string? Title, string? WechatId, string? Nickname, bool IsBound); public sealed class AccountBindingStore(ServiceOptions options) { private readonly string path = Path.Combine(options.DataDirectory, "account-bindings.json"); private readonly object gate = new(); public IReadOnlyList ReadAll() { lock (gate) { if (!File.Exists(path)) return []; try { return JsonSerializer.Deserialize(File.ReadAllText(path), ServiceHost.Json) ?? []; } catch (JsonException) { return []; } } } public AccountBinding? Get(string accountId) { var matches = ReadAll().Where(x => string.Equals(x.AccountId, accountId, StringComparison.OrdinalIgnoreCase)).ToArray(); return matches.Length == 1 ? matches[0] : null; } public void Replace(IEnumerable bindings) { var items = bindings.GroupBy(x => x.AccountId, StringComparer.OrdinalIgnoreCase).Select(x => x.Last()).ToArray(); Directory.CreateDirectory(Path.GetDirectoryName(path)!); lock (gate) { var temp = path + ".tmp"; File.WriteAllText(temp, JsonSerializer.Serialize(items, ServiceHost.Json)); File.Move(temp, path, true); } } public void Remove(string accountId) => Replace(ReadAll().Where(x => !string.Equals(x.AccountId, accountId, StringComparison.OrdinalIgnoreCase))); }