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(); 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().Where(a => a.BindingId == binding.Id && a.UserId == binding.UserId).CountAsync() > 0; var observations = await db.Queryable().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(); var result = await runner.Run(args[1], options, actor, true); var verified = await db.Queryable().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; }