158 lines
5.4 KiB
C#
158 lines
5.4 KiB
C#
using System;
|
||
using System.Data.SQLite;
|
||
using System.Net.Http;
|
||
using System.Net.Http.Headers;
|
||
using System.Text.Json;
|
||
using System.Text.RegularExpressions;
|
||
using System.Threading.Tasks;
|
||
using Электронная_Фармация.Properties;
|
||
|
||
namespace Электронная_Фармация.Classes
|
||
{
|
||
/// <summary>
|
||
/// Глобальные номера заказов вида EX-0000001.
|
||
/// Источник истины — сервер (никогда не повторяются между аптеками).
|
||
/// Offline fallback — локальный счётчик EX- (потом сервер может заменить при отправке).
|
||
/// </summary>
|
||
public static class OrderNumberHelper
|
||
{
|
||
private static readonly Regex ExPattern = new Regex(@"^EX-(\d{1,7})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||
|
||
/// <summary>
|
||
/// Выделяет номер для нового локального заказа.
|
||
/// Сначала пробует сервер /api/buyer/global-sign/next, иначе локальный EX-#######.
|
||
/// </summary>
|
||
public static string AllocateForNewOrder()
|
||
{
|
||
try
|
||
{
|
||
var fromServer = AllocateFromServerAsync().GetAwaiter().GetResult();
|
||
if (!string.IsNullOrWhiteSpace(fromServer))
|
||
{
|
||
return fromServer;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppDebugLog.Info("OrderNumber", $"Сервер недоступен для GlobalSign: {ex.Message}");
|
||
}
|
||
|
||
return AllocateLocalFallback();
|
||
}
|
||
|
||
public static async Task<string> AllocateFromServerAsync()
|
||
{
|
||
var token = Settings.Default.stringToken;
|
||
var baseUrl = AppConfig.ApiBaseUrl;
|
||
if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(baseUrl))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
using (var client = new HttpClient { Timeout = TimeSpan.FromSeconds(8) })
|
||
{
|
||
client.BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/");
|
||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Trim());
|
||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||
|
||
using (var response = await client.PostAsync("api/buyer/global-sign/next", new StringContent(string.Empty)).ConfigureAwait(false))
|
||
{
|
||
var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
AppDebugLog.Info("OrderNumber", $"global-sign/next HTTP {(int)response.StatusCode}: {body}");
|
||
return null;
|
||
}
|
||
|
||
using (var doc = JsonDocument.Parse(body))
|
||
{
|
||
if (doc.RootElement.TryGetProperty("global_sign", out var el))
|
||
{
|
||
var sign = el.GetString();
|
||
if (IsValidEx(sign))
|
||
{
|
||
return Normalize(sign);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
public static string AllocateLocalFallback()
|
||
{
|
||
long max = 0;
|
||
try
|
||
{
|
||
using (var con = new SQLiteConnection(AppConfig.SqliteConnectionString))
|
||
{
|
||
con.Open();
|
||
using (var cmd = new SQLiteCommand(
|
||
"SELECT OrderNumber FROM Orders WHERE OrderNumber LIKE 'EX-%'", con))
|
||
using (var reader = cmd.ExecuteReader())
|
||
{
|
||
while (reader.Read())
|
||
{
|
||
var n = ParseExNumber(reader[0]?.ToString());
|
||
if (n > max)
|
||
{
|
||
max = n;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppDebugLog.Info("OrderNumber", $"Локальный max EX-: {ex.Message}");
|
||
}
|
||
|
||
return FormatEx(max + 1);
|
||
}
|
||
|
||
public static bool IsValidEx(string value)
|
||
{
|
||
return ParseExNumber(value) > 0;
|
||
}
|
||
|
||
public static string Normalize(string value)
|
||
{
|
||
var n = ParseExNumber(value);
|
||
return n > 0 ? FormatEx(n) : value?.Trim();
|
||
}
|
||
|
||
public static string FormatEx(long n)
|
||
{
|
||
if (n < 1)
|
||
{
|
||
n = 1;
|
||
}
|
||
|
||
if (n > 9999999)
|
||
{
|
||
return "EX-" + n.ToString();
|
||
}
|
||
|
||
return "EX-" + n.ToString("D7");
|
||
}
|
||
|
||
public static long ParseExNumber(string value)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(value))
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
var m = ExPattern.Match(value.Trim());
|
||
if (!m.Success)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
return long.TryParse(m.Groups[1].Value, out var n) ? n : 0;
|
||
}
|
||
}
|
||
}
|