Files
Evergreen-Health/Tools/Ib43.LocalMaintenance/Program.cs
T
2026-11-11 16:11:41 +08:00

72 lines
4.6 KiB
C#

using Admin.NET.Core;
using Evergreen.Health.Application.Health.Entities;
using Evergreen.Health.Application.Health.Integrations.Ib43;
using Microsoft.Extensions.Configuration;
using SqlSugar;
using System.Text.RegularExpressions;
if (args.Length is < 2 or > 3 || !Regex.IsMatch(args[1], "^1[3-9][0-9]{9}$") ||
(args.Length == 3 && args[2] is not ("--apply" or "--verify")))
{
Console.Error.WriteLine("用法: <项目根目录> <健康端手机号> [--apply|--verify];默认只预检。");
return 1;
}
try
{
var directory = Path.Combine(Path.GetFullPath(args[0]), "Admin.NET", "Evergreen.Health.Application", "Configuration");
var config = new ConfigurationBuilder().AddJsonFile(Path.Combine(directory, "Database.json"))
.AddJsonFile(Path.Combine(directory, "Ib43.json")).AddEnvironmentVariables().Build();
var dbOptions = config.GetSection("DbConnection").Get<DbConnectionOptions>();
dbOptions.PostConfigure(dbOptions, config);
var connection = dbOptions.ConnectionConfigs.Single(c => c.ConfigId?.ToString() == SqlSugarConst.MainConfigId);
if (connection.DbSettings.EnableConnStringEncrypt)
throw new InvalidOperationException("该工具不支持加密连接配置,已停止。");
if (connection.DbType is not (DbType.MySql or DbType.MySqlConnector))
throw new InvalidOperationException("当前维护工具只允许项目 MySQL 主库。");
var options = new Ib43Options();
config.GetSection("Ib43").Bind(options);
Ib43Configuration.ApplyEnvironmentOverrides(options);
SqlSugarSetup.SetDbConfig(connection);
// 控制台没有 Web 容器。沿用表和列映射,但不调用依赖 Web DI 的二级缓存。
connection.ConfigureExternalServices.DataInfoCacheService = null;
connection.MoreSettings.IsAutoRemoveDataCache = false;
using var db = new SqlSugarClient(connection);
db.Ado.CommandTimeOut = 10;
foreach (var table in new[] { "health_user", "health_ib43_binding" })
if (!db.DbMaintenance.IsAnyTable(table, false)) throw new InvalidOperationException("缺少业务表,请先启动新版后端。");
var runner = new Ib43LocalBinding(db, TimeProvider.System);
var actor = Environment.MachineName + "\\" + Environment.UserName;
var preview = await runner.Run(args[1], options, actor, false);
Console.WriteLine($"预检通过: userId={preview.UserId}, evergreen-test/IB43SIM001, left, simulation=true");
if (args.Length == 2) { Console.WriteLine("未修改数据库。加 --apply 执行绑定。"); return 0; }
if (args[2] == "--verify")
{
var repository = new Ib43DataRepository(db);
var binding = await repository.OwnedBinding(preview.UserId, preview.Id);
var hasAudit = await db.Queryable<HealthIb43MaintenanceAudit>().Where(a => a.BindingId == binding.Id && a.UserId == binding.UserId).CountAsync() > 0;
var observations = await db.Queryable<HealthIb43Observation>().Where(o => o.BindingId == binding.Id && o.UserId == binding.UserId).CountAsync();
var readings = await repository.Latest(binding.UserId, binding.Id);
var sampleMatches = readings.TryGetValue("heartRate", out var heart) && heart.Value == 72 &&
readings.TryGetValue("respiration", out var breath) && breath.Value == 16 &&
readings.TryGetValue("snoring", out var snoring) && snoring.Value == 0;
Console.WriteLine($"bindingId={binding.Id}, audit={hasAudit}, observations={observations}, expectedSample={sampleMatches}");
return hasAudit && observations > 0 && sampleMatches ? 0 : 2;
}
// 只维护新增审计表,不初始化/重建其他业务表,不启动 Web、MQTT 或种子数据。
if (!db.DbMaintenance.IsAnyTable("health_ib43_maintenance_audit", false))
db.CodeFirst.InitTables<HealthIb43MaintenanceAudit>();
var result = await runner.Run(args[1], options, actor, true);
var verified = await db.Queryable<HealthIb43Binding>().Where(b => b.Id == result.Id && b.UserId == result.UserId &&
b.Side == "left" && b.Simulation && b.EndedAtMs == null).CountAsync();
if (verified != 1) throw new InvalidOperationException("绑定读回验证失败。");
Console.WriteLine($"PASS: bindingId={result.Id}, userId={result.UserId}; 右侧未修改。重复运行复用已有绑定。");
return 0;
}
catch (InvalidOperationException error) { Console.Error.WriteLine(error.Message); return 1; }
catch (Exception error)
{
// 不打印第三方数据库异常正文,避免连接凭据进入控制台。
Console.Error.WriteLine($"维护失败 ({error.GetType().Name})。请检查数据库连接和表结构;未输出凭据。");
return 1;
}