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_partner 与 fl_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; }
}
}