Files
qhmes/yy-admin-master/YY.Admin.Services/Service/Warehouse/WarehouseService.cs

163 lines
5.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.Extensions.Configuration;
using System.Net.Http;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using YY.Admin.Core;
using YY.Admin.Core.Entity;
using YY.Admin.Core.Services;
namespace YY.Admin.Services.Service.Warehouse;
/// <summary>
/// 仓库数据只读服务启动与重连时后台刷新远端写入缓存GetAllAsync 优先读缓存,避免库区等业务每次打开都全量拉仓库。
/// </summary>
public class WarehouseService : IWarehouseService, ISingletonDependency
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _configuration;
private readonly INetworkMonitor _networkMonitor;
private readonly ILoggerService _logger;
private readonly object _cacheLock = new();
private readonly SemaphoreSlim _emptyLoadGate = new(1, 1);
private readonly string _cacheFilePath;
private List<MesXslWarehouse> _localCache = new();
private static readonly JsonSerializerOptions _jsonOpts = new()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
public WarehouseService(
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
INetworkMonitor networkMonitor,
ILoggerService logger)
{
_httpClientFactory = httpClientFactory;
_configuration = configuration;
_networkMonitor = networkMonitor;
_logger = logger;
var appDataDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"YY.Admin", "sync-cache");
Directory.CreateDirectory(appDataDir);
_cacheFilePath = Path.Combine(appDataDir, "warehouse-cache.json");
LoadCacheFromDisk();
_logger.Information($"[仓库数据] 服务初始化,本地缓存={_localCache.Count} 条");
if (_networkMonitor.IsOnline)
_ = Task.Run(() => RefreshFromRemoteAsync(CancellationToken.None));
_networkMonitor.StatusChanged += isOnline =>
{
if (isOnline)
_ = Task.Run(() => RefreshFromRemoteAsync(CancellationToken.None));
};
}
private string BaseUrl => (_configuration.GetValue<string>("JeecgIntegration:BaseUrl") ?? "http://localhost:8080/jeecg-boot").TrimEnd('/');
private int DefaultTenantId => (int?)_configuration.GetValue<long?>("JeecgIntegration:DefaultTenantId") ?? 1002;
public async Task<List<MesXslWarehouse>> GetAllAsync(CancellationToken ct = default)
{
lock (_cacheLock)
{
if (_localCache.Count > 0 || !_networkMonitor.IsOnline)
return _localCache.ToList();
}
// 缓存为空且在线:仅此时阻塞拉远端(首次安装或清缓存后)
await _emptyLoadGate.WaitAsync(ct).ConfigureAwait(false);
try
{
lock (_cacheLock)
{
if (_localCache.Count > 0)
return _localCache.ToList();
}
if (!_networkMonitor.IsOnline)
{
lock (_cacheLock)
return _localCache.ToList();
}
try
{
await RefreshFromRemoteAsync(ct).ConfigureAwait(false);
}
catch
{
/* 登录前拉取失败等场景,仍返回当前缓存(可能仍为空) */
}
}
finally
{
_emptyLoadGate.Release();
}
lock (_cacheLock)
return _localCache.ToList();
}
private async Task RefreshFromRemoteAsync(CancellationToken ct)
{
try
{
var result = new List<MesXslWarehouse>();
int pageNo = 1;
const int pageSize = 500;
while (true)
{
var url = $"{BaseUrl}/xslmes/mesXslWarehouse/anon/list?pageNo={pageNo}&pageSize={pageSize}&tenantId={DefaultTenantId}";
using var client = _httpClientFactory.CreateClient("JeecgApi");
var resp = await client.GetAsync(url, ct).ConfigureAwait(false);
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
using var doc = JsonDocument.Parse(json);
if (!doc.RootElement.TryGetProperty("result", out var resultEl)) break;
if (resultEl.TryGetProperty("records", out var recordsEl))
{
var page = recordsEl.Deserialize<List<MesXslWarehouse>>(_jsonOpts);
if (page != null) result.AddRange(page);
}
long total = 0;
if (resultEl.TryGetProperty("total", out var totalEl)) total = totalEl.GetInt64();
if (result.Count >= total || (resultEl.TryGetProperty("records", out var r2) && r2.GetArrayLength() < pageSize)) break;
pageNo++;
}
lock (_cacheLock)
{
_localCache = result;
SaveCacheToDiskUnsafe();
}
_logger.Information($"[仓库数据] 远端刷新成功,共 {result.Count} 条");
}
catch (Exception ex)
{
_logger.Warning($"[仓库数据] 远端刷新失败,使用缓存: {ex.Message}");
}
}
private void LoadCacheFromDisk()
{
try
{
if (!File.Exists(_cacheFilePath)) return;
var json = File.ReadAllText(_cacheFilePath);
_localCache = JsonSerializer.Deserialize<List<MesXslWarehouse>>(json, _jsonOpts) ?? new();
}
catch { _localCache = new(); }
}
private void SaveCacheToDiskUnsafe()
{
try { File.WriteAllText(_cacheFilePath, JsonSerializer.Serialize(_localCache, _jsonOpts)); } catch { }
}
}