using System.Diagnostics;
using System.Reflection;
namespace Admin.NET.Plugin.AiDOP.Infrastructure;
///
/// 本进程在 ETL 执行机注册表(ado_etl_instance)中的身份。
///
/// 为什么要进程级唯一:同一台机器可能同时跑多个后端进程(开发时的
/// dotnet run + 已发布的服务),它们连的是同一个库。若只用机器名做标识,
/// 两个进程会抢同一行注册记录,指派给谁都说不清。故标识取
/// 机器名:进程ID:启动时间 三段,进程重启后必然产生新行。
///
/// 为什么用启动时间而不是随机 GUID:运维要在页面上认出「这是哪台、什么时候起的」。
/// GUID 每次重启都变且不可读,排障时无法和服务器上的进程对上。
///
/// 静态只读,进程生命周期内不变,可在任意线程反复读取。
///
public static class AidopInstanceIdentity
{
static AidopInstanceIdentity()
{
MachineName = SafeGet(() => Environment.MachineName, "unknown-host");
ProcessId = SafeGet(() => Environment.ProcessId, 0);
StartedAt = SafeGet(() => Process.GetCurrentProcess().StartTime, DateTime.Now);
AppVersion = SafeGet(
() => Assembly.GetEntryAssembly()?.GetName().Version?.ToString(),
null);
// 长度受 ado_etl_instance.instance_id 的 200 字符约束;机器名过长时截断,
// 进程ID + 启动时间仍能保证同机不同进程互不覆盖。
var host = MachineName.Length > 120 ? MachineName[..120] : MachineName;
InstanceId = $"{host}:{ProcessId}:{StartedAt:yyyyMMddHHmmss}";
}
/// 注册表主键语义列,形如 DESKTOP-ABC:12345:20260925071200。
public static string InstanceId { get; }
public static string MachineName { get; }
public static int ProcessId { get; }
/// 本进程启动时间。取不到时退化为静态构造时刻,不影响唯一性。
public static DateTime StartedAt { get; }
/// 入口程序集版本,仅供页面展示与排障;取不到为 null。
public static string AppVersion { get; }
///
/// 身份计算不得成为启动故障点:容器内取进程启动时间、受限环境取机器名都可能抛异常,
/// 一旦抛出会让整个插件装配失败,而这里拿到的只是展示用信息。
///
private static T SafeGet(Func get, T fallback)
{
try { return get(); }
catch { return fallback; }
}
}