using SqlSugar; namespace FoodLabeling.Application.Helpers; /// /// 探测标签模板 Company/Region scope 列与关联表是否已迁移(fl_label_template_scope.sql)。 /// 未迁移时 ORM 实体不含 AppliedPartnerType/AppliedRegionType,读写走 raw SQL 或关联表推断。 /// public static class LabelTemplateScopeSchemaHelper { private static LabelTemplateScopeSchemaStatus? _cached; public sealed class LabelTemplateScopeSchemaStatus { public bool HasAppliedPartnerTypeColumn { get; init; } public bool HasAppliedRegionTypeColumn { get; init; } public bool HasPartnerScopeTable { get; init; } public bool HasRegionScopeTable { get; init; } public bool HasBorderTypeColumn { get; init; } public bool HasContentsColumn { get; init; } /// partner/region 关联表均已创建(用于多选明细)。 public bool HasPartnerRegionScopeTables => HasPartnerScopeTable && HasRegionScopeTable; /// 列 + 关联表均已迁移。 public bool IsFullSchema => HasAppliedPartnerTypeColumn && HasAppliedRegionTypeColumn && HasPartnerRegionScopeTables; } public static async Task GetStatusAsync(ISqlSugarClient db) { if (_cached is not null) { return _cached; } try { var partnerColCount = await db.Ado.GetIntAsync( """ SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_label_template' AND COLUMN_NAME = 'AppliedPartnerType' """); var regionColCount = await db.Ado.GetIntAsync( """ SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_label_template' AND COLUMN_NAME = 'AppliedRegionType' """); var partnerTableCount = await db.Ado.GetIntAsync( """ SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_label_template_partner' """); var regionTableCount = await db.Ado.GetIntAsync( """ SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_label_template_region' """); var borderColCount = await db.Ado.GetIntAsync( """ SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_label_template' AND COLUMN_NAME = 'BorderType' """); var contentsColCount = await db.Ado.GetIntAsync( """ SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_label_template' AND COLUMN_NAME = 'Contents' """); _cached = new LabelTemplateScopeSchemaStatus { HasAppliedPartnerTypeColumn = partnerColCount > 0, HasAppliedRegionTypeColumn = regionColCount > 0, HasPartnerScopeTable = partnerTableCount > 0, HasRegionScopeTable = regionTableCount > 0, HasBorderTypeColumn = borderColCount > 0, HasContentsColumn = contentsColCount > 0 }; } catch { _cached = new LabelTemplateScopeSchemaStatus(); } return _cached; } /// 单元测试或切换库后调用。 public static void ResetCacheForTests() => _cached = null; /// 清除缓存并重新探测 schema(应用启动早于迁移脚本执行后调用)。 public static async Task RefreshStatusAsync(ISqlSugarClient db) { _cached = null; return await GetStatusAsync(db); } /// /// 是否已创建 fl_label_template_partnerfl_label_template_region。 /// public static async Task HasPartnerRegionScopeTablesAsync(ISqlSugarClient db) { var status = await GetStatusAsync(db); return status.HasPartnerRegionScopeTables; } /// /// 是否已创建 fl_label_template_partner;缓存未命中时刷新后再探测。 /// public static async Task EnsurePartnerScopeTableAsync(ISqlSugarClient db, bool needsPartnerRows) { var status = await GetStatusAsync(db); if (status.HasPartnerScopeTable) { return true; } if (!needsPartnerRows) { return false; } status = await RefreshStatusAsync(db); return status.HasPartnerScopeTable; } /// /// 是否已创建 fl_label_template_region;缓存未命中时刷新后再探测。 /// public static async Task EnsureRegionScopeTableAsync(ISqlSugarClient db, bool needsRegionRows) { var status = await GetStatusAsync(db); if (status.HasRegionScopeTable) { return true; } if (!needsRegionRows) { return false; } status = await RefreshStatusAsync(db); return status.HasRegionScopeTable; } /// /// 已迁移 scope 列时,写入 AppliedPartnerType / AppliedRegionType(未迁移则 no-op)。 /// public static async Task SetAppliedScopeTypesAsync( ISqlSugarClient db, string templateId, string appliedPartnerType, string appliedRegionType) { if (string.IsNullOrWhiteSpace(templateId)) { return; } var status = await GetStatusAsync(db); if (!status.HasAppliedPartnerTypeColumn && !status.HasAppliedRegionTypeColumn) { return; } if (status.HasAppliedPartnerTypeColumn && status.HasAppliedRegionTypeColumn) { await db.Ado.ExecuteCommandAsync( """ UPDATE fl_label_template SET AppliedPartnerType = @partnerType, AppliedRegionType = @regionType WHERE Id = @id """, new { partnerType = appliedPartnerType.Trim(), regionType = appliedRegionType.Trim(), id = templateId.Trim() }); return; } if (status.HasAppliedPartnerTypeColumn) { await db.Ado.ExecuteCommandAsync( "UPDATE fl_label_template SET AppliedPartnerType = @type WHERE Id = @id", new { type = appliedPartnerType.Trim(), id = templateId.Trim() }); } if (status.HasAppliedRegionTypeColumn) { await db.Ado.ExecuteCommandAsync( "UPDATE fl_label_template SET AppliedRegionType = @type WHERE Id = @id", new { type = appliedRegionType.Trim(), id = templateId.Trim() }); } } /// 已迁移 BorderType 列时写入(未迁移则 no-op)。 public static async Task SetBorderTypeAsync(ISqlSugarClient db, string templateId, string borderType) { if (string.IsNullOrWhiteSpace(templateId)) { return; } var status = await GetStatusAsync(db); if (!status.HasBorderTypeColumn) { return; } await db.Ado.ExecuteCommandAsync( "UPDATE fl_label_template SET BorderType = @border WHERE Id = @id", new { border = borderType.Trim(), id = templateId.Trim() }); } /// 读取模板外框类型;列不存在时返回 none public static async Task GetBorderTypeForTemplateAsync(ISqlSugarClient db, string templateId) { if (string.IsNullOrWhiteSpace(templateId)) { return "none"; } var status = await GetStatusAsync(db); if (!status.HasBorderTypeColumn) { return "none"; } var value = await db.Ado.GetScalarAsync( "SELECT BorderType FROM fl_label_template WHERE Id = @id LIMIT 1", new { id = templateId.Trim() }); var text = value?.ToString()?.Trim(); return string.IsNullOrWhiteSpace(text) ? "none" : text; } /// 批量读取模板 Contents;列不存在时返回空字典。 public static async Task> GetContentsMapAsync( ISqlSugarClient db, IReadOnlyList templateIds) { var result = new Dictionary(StringComparer.Ordinal); if (templateIds.Count == 0) { return result; } var status = await GetStatusAsync(db); if (!status.HasContentsColumn) { return result; } var rows = await db.Ado.SqlQueryAsync( """ SELECT Id, Contents FROM fl_label_template WHERE Id IN (@ids) """, new { ids = templateIds.ToArray() }); foreach (var row in rows) { var text = row.Contents?.Trim(); if (!string.IsNullOrWhiteSpace(text)) { result[row.Id] = text; } } return result; } /// 读取单条模板 Contents;列不存在或未写入时返回 null。 public static async Task GetContentsForTemplateAsync(ISqlSugarClient db, string templateId) { if (string.IsNullOrWhiteSpace(templateId)) { return null; } var map = await GetContentsMapAsync(db, new[] { templateId.Trim() }); return map.TryGetValue(templateId.Trim(), out var text) ? text : null; } /// 已迁移 Contents 列时写入(未迁移则 no-op)。 public static async Task SetContentsAsync(ISqlSugarClient db, string templateId, string? contents) { if (string.IsNullOrWhiteSpace(templateId)) { return; } var status = await GetStatusAsync(db); if (!status.HasContentsColumn) { return; } await db.Ado.ExecuteCommandAsync( "UPDATE fl_label_template SET Contents = @contents WHERE Id = @id", new { contents = contents?.Trim() ?? string.Empty, id = templateId.Trim() }); } private sealed class ContentsRow { public string Id { get; init; } = string.Empty; public string? Contents { get; init; } } }