using FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
using FoodLabeling.Application.Services.DbModels;
using SqlSugar;
namespace FoodLabeling.Application.Helpers;
///
/// 标签模板 Contents:汇总各控件展示名(编辑器 Element name / typeAdd 推导),逗号拼接。
///
public static class LabelTemplateContentsHelper
{
///
/// 保存时解析 Contents:优先入参 ,否则从 推导。
///
public static string ResolveForSave(string? inputContents, IReadOnlyList? elements)
{
var trimmed = inputContents?.Trim();
if (!string.IsNullOrWhiteSpace(trimmed))
{
return trimmed;
}
return BuildFromElements(elements);
}
///
/// 从 elements 推导 Contents(去重、按 OrderNum 排序)。
///
public static string BuildFromElements(IReadOnlyList? elements)
{
if (elements is null || elements.Count == 0)
{
return string.Empty;
}
var names = CollectOrderedDistinctLabels(
elements
.OrderBy(e => e.OrderNum)
.ThenBy(e => e.Id, StringComparer.Ordinal)
.Select(e => ResolveElementContentsLabel(e.ElementName, e.TypeAdd, e.ElementType)));
return names.Count > 0 ? string.Join(", ", names) : string.Empty;
}
///
/// 批量从 fl_label_template_element 推导 Contents。Key = 模板主键 Id。
///
public static async Task> ResolveContentsMapFromElementsAsync(
ISqlSugarClient db,
IReadOnlyList templateIds)
{
var result = new Dictionary(StringComparer.Ordinal);
if (templateIds.Count == 0)
{
return result;
}
var rows = await db.Queryable()
.Where(x => templateIds.Contains(x.TemplateId))
.Select(x => new ElementContentsRow
{
TemplateId = x.TemplateId,
ElementName = x.ElementName,
TypeAdd = x.TypeAdd,
ElementType = x.ElementType,
OrderNum = x.OrderNum,
ElementKey = x.ElementKey
})
.ToListAsync();
foreach (var group in rows.GroupBy(x => x.TemplateId))
{
var names = CollectOrderedDistinctLabels(
group
.OrderBy(x => x.OrderNum)
.ThenBy(x => x.ElementKey, StringComparer.Ordinal)
.Select(x => ResolveElementContentsLabel(x.ElementName, x.TypeAdd, x.ElementType)));
if (names.Count > 0)
{
result[group.Key] = string.Join(", ", names);
}
}
return result;
}
///
/// 单控件 Contents 展示名:优先用户编辑的 Element name;旧 slug 则按 typeAdd 推导「控件名 (For Template)」。
///
public static string ResolveElementContentsLabel(string? elementName, string? typeAdd, string? elementType)
{
var derived = DeriveLabelFromTypeAdd(typeAdd, elementType);
var name = elementName?.Trim();
if (string.IsNullOrWhiteSpace(name))
{
return derived;
}
if (IsLegacySlugElementName(name, typeAdd, elementType))
{
return string.IsNullOrWhiteSpace(derived) ? name : derived;
}
return name;
}
///
/// 从 typeAdd(如 template_Text、label_Nutrition Facts)推导展示名。
///
public static string DeriveLabelFromTypeAdd(string? typeAdd, string? elementType)
{
var ta = typeAdd?.Trim();
if (!string.IsNullOrWhiteSpace(ta))
{
var idx = ta.IndexOf('_');
if (idx >= 0 && idx < ta.Length - 1)
{
var prefix = ta[..idx].Trim().ToLowerInvariant();
var field = ta[(idx + 1)..].Trim();
if (!string.IsNullOrWhiteSpace(field))
{
var category = MapPrefixToCategoryTitle(prefix);
return category != null ? $"{field} ({category})" : field;
}
}
}
return MapElementTypeToDisplayLabel(elementType);
}
private static bool IsLegacySlugElementName(string name, string? typeAdd, string? elementType)
{
if (name.Contains(' ') || name.Contains('('))
{
return false;
}
if (!LegacySlugPattern.IsMatch(name))
{
return false;
}
var derived = DeriveLabelFromTypeAdd(typeAdd, elementType);
if (string.IsNullOrWhiteSpace(derived))
{
return true;
}
var field = ExtractTypeAddField(typeAdd);
if (string.IsNullOrWhiteSpace(field))
{
return true;
}
var slugField = SlugPaletteLabel(field);
var slugName = name.Trim().ToLowerInvariant();
if (slugName == slugField)
{
return true;
}
if (slugName.StartsWith(slugField, StringComparison.Ordinal)
&& slugName.Length > slugField.Length
&& int.TryParse(slugName[slugField.Length..], out _))
{
return true;
}
return false;
}
private static string? ExtractTypeAddField(string? typeAdd)
{
var ta = typeAdd?.Trim();
if (string.IsNullOrWhiteSpace(ta))
{
return null;
}
var idx = ta.IndexOf('_');
return idx >= 0 && idx < ta.Length - 1 ? ta[(idx + 1)..].Trim() : null;
}
private static string SlugPaletteLabel(string label) =>
new string(label.Trim().ToLowerInvariant().Where(char.IsLetterOrDigit).ToArray());
private static string? MapPrefixToCategoryTitle(string prefix) =>
prefix switch
{
"template" => "For Template",
"label" => "For Label",
"auto" => "Entered Automatically",
"print" => "Entered When Printing",
_ => null
};
private static string MapElementTypeToDisplayLabel(string? elementType)
{
var t = (elementType ?? string.Empty).Trim().ToUpperInvariant();
return t switch
{
"TEXT_PRODUCT" => "Label Name (For Label)",
"TEXT_PRICE" => "Price (For Label)",
"NUTRITION" or "NUTRITIONFACTS" => "Nutrition Facts (For Label)",
"DURATION" => "Duration (For Label)",
"DATE" => "Current Date (Entered Automatically)",
"TIME" => "Current Time (Entered Automatically)",
"BARCODE" => "Barcode (For Label)",
"QRCODE" => "QR Code (For Label)",
"BLANK" => "Blank Space (For Template)",
"IMAGE" => "Image (For Label)",
"TEXT_STATIC" => "Text (For Template)",
_ => string.IsNullOrWhiteSpace(t) ? string.Empty : t.Replace('_', ' ')
};
}
private static List CollectOrderedDistinctLabels(IEnumerable labels)
{
var seen = new HashSet(StringComparer.OrdinalIgnoreCase);
var names = new List();
foreach (var label in labels)
{
var text = label?.Trim();
if (string.IsNullOrWhiteSpace(text) || !seen.Add(text))
{
continue;
}
names.Add(text);
}
return names;
}
private static readonly System.Text.RegularExpressions.Regex LegacySlugPattern =
new("^[a-z0-9]+$", System.Text.RegularExpressions.RegexOptions.Compiled);
private sealed class ElementContentsRow
{
public string TemplateId { get; init; } = string.Empty;
public string ElementName { get; init; } = string.Empty;
public string? TypeAdd { get; init; }
public string ElementType { get; init; } = string.Empty;
public int OrderNum { get; init; }
public string ElementKey { get; init; } = string.Empty;
}
}