using System.Text.Json;
using FoodLabeling.Application.Helpers;
using FoodLabeling.Application.Contracts.Dtos.Common;
using FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
using FoodLabeling.Application.Contracts.IServices;
using FoodLabeling.Application.Services.DbModels;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Guids;
using Volo.Abp.Uow;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace FoodLabeling.Application.Services;
///
/// 标签模板管理(Label Templates)
///
public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppService
{
private readonly ISqlSugarDbContext _dbContext;
private readonly IGuidGenerator _guidGenerator;
public LabelTemplateAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator)
{
_dbContext = dbContext;
_guidGenerator = guidGenerator;
}
public async Task> GetListAsync(LabelTemplateGetListInputVo input)
{
input ??= new LabelTemplateGetListInputVo();
RefAsync total = 0;
var keyword = input.Keyword?.Trim();
var scopedLocationIds = await LocationScopeBindingHelper.ResolveFilteredLocationIdsForListAsync(
_dbContext.SqlSugarClient, input.PartnerId, input.GroupId, input.LocationId);
var query = _dbContext.SqlSugarClient.Queryable()
.Where(x => !x.IsDeleted)
.WhereIF(!string.IsNullOrWhiteSpace(keyword), x =>
x.TemplateName.Contains(keyword!) || x.TemplateCode.Contains(keyword!))
.WhereIF(!string.IsNullOrWhiteSpace(input.LabelType), x => x.LabelType == input.LabelType)
.WhereIF(input.State != null, x => x.State == input.State);
query = await LabelTemplateScopeHelper.ApplyTemplateScopeFilterAsync(
_dbContext.SqlSugarClient, query, scopedLocationIds);
query = ApplyLabelTemplateListSorting(query, input.Sorting);
query = LabelTemplateQueryHelper.ProjectListColumns(query);
var pageSize = input.MaxResultCount <= 0 ? 10 : input.MaxResultCount;
var pageEntities = await query.ToPageListAsync(input.SkipCount, pageSize, total);
var templateIds = pageEntities.Select(x => x.Id).ToList();
var elementCountMap = new Dictionary(StringComparer.Ordinal);
if (templateIds.Count > 0)
{
var elementCounts = await _dbContext.SqlSugarClient.Queryable()
.Where(x => templateIds.Contains(x.TemplateId))
.GroupBy(x => x.TemplateId)
.Select(x => new { TemplateId = x.TemplateId, Count = SqlFunc.AggregateCount(x.Id) })
.ToListAsync();
elementCountMap = elementCounts.ToDictionary(x => x.TemplateId, x => (int)x.Count);
}
var scopeMap = await LabelTemplateScopeHelper.BuildScopeDisplayMapAsync(
_dbContext.SqlSugarClient, pageEntities);
var itemsMap = templateIds.Count > 0
? await LabelTemplateListItemsHelper.ResolveTemplateItemsMapAsync(
_dbContext.SqlSugarClient, templateIds)
: new Dictionary(StringComparer.Ordinal);
var items = pageEntities.Select(x =>
{
scopeMap.TryGetValue(x.Id, out var scope);
itemsMap.TryGetValue(x.Id, out var itemsDisplay);
var lastEdited = x.LastModificationTime ?? x.CreationTime;
var contentsCount = elementCountMap.TryGetValue(x.Id, out var c) ? c : 0;
var locationDisplay = scope?.Location ?? EmptyDisplay;
var sizeText = $"{x.Width}x{x.Height}{x.Unit}";
return new LabelTemplateGetListOutputDto
{
Id = x.TemplateCode, // front-end uses templateCode as identifier
TemplateCode = x.TemplateCode,
TemplateName = x.TemplateName,
LabelType = x.LabelType,
LocationText = locationDisplay,
Company = scope?.Company ?? string.Empty,
Region = scope?.Region ?? string.Empty,
Location = locationDisplay,
PartnerIds = scope?.PartnerIds ?? new List(),
CompanyIds = scope?.PartnerIds ?? new List(),
RegionIds = scope?.RegionIds ?? new List(),
GroupIds = scope?.RegionIds ?? new List(),
LocationIds = scope?.LocationIds ?? new List(),
ContentsCount = contentsCount,
Items = itemsDisplay?.Items ?? "无",
ItemNames = itemsDisplay?.ItemNames ?? new List(),
SizeText = sizeText,
VersionNo = x.VersionNo,
LastEdited = lastEdited
};
}).ToList();
return BuildPagedResult(input.SkipCount, pageSize, total, items);
}
///
/// 列表排序:白名单字段 + IFNULL(LastModificationTime, CreationTime),避免 ?? 与原始 Sorting 拼接导致 SQL 异常。
///
private static ISugarQueryable ApplyLabelTemplateListSorting(
ISugarQueryable query,
string? sorting)
{
if (!string.IsNullOrWhiteSpace(sorting))
{
var s = sorting.Trim();
if (s.Equals("TemplateName asc", StringComparison.OrdinalIgnoreCase))
{
return query.OrderBy(x => x.TemplateName);
}
if (s.Equals("TemplateName desc", StringComparison.OrdinalIgnoreCase))
{
return query.OrderByDescending(x => x.TemplateName);
}
if (s.Equals("TemplateCode asc", StringComparison.OrdinalIgnoreCase))
{
return query.OrderBy(x => x.TemplateCode);
}
if (s.Equals("TemplateCode desc", StringComparison.OrdinalIgnoreCase))
{
return query.OrderByDescending(x => x.TemplateCode);
}
if (s.Equals("CreationTime asc", StringComparison.OrdinalIgnoreCase))
{
return query.OrderBy(x => x.CreationTime);
}
if (s.Equals("CreationTime desc", StringComparison.OrdinalIgnoreCase))
{
return query.OrderByDescending(x => x.CreationTime);
}
if (s.Equals("LastModificationTime asc", StringComparison.OrdinalIgnoreCase))
{
return query.OrderBy(x => x.LastModificationTime);
}
if (s.Equals("LastModificationTime desc", StringComparison.OrdinalIgnoreCase))
{
return query.OrderByDescending(x => x.LastModificationTime);
}
}
return query
.OrderBy(x => SqlFunc.IsNull(x.LastModificationTime, x.CreationTime), OrderByType.Desc)
.OrderBy(x => x.TemplateCode, OrderByType.Asc);
}
public async Task GetAsync(string id)
{
var template = await LabelTemplateQueryHelper.ProjectListColumns(
_dbContext.SqlSugarClient.Queryable())
.FirstAsync(x => !x.IsDeleted && x.TemplateCode == id);
if (template is null)
{
throw new UserFriendlyException("模板不存在");
}
var elements = await _dbContext.SqlSugarClient.Queryable()
.Where(x => x.TemplateId == template.Id)
.OrderBy(x => x.OrderNum)
.ToListAsync();
List MapElements()
{
return elements.Select(e =>
{
object? cfg = null;
if (!string.IsNullOrWhiteSpace(e.ConfigJson))
{
cfg = JsonSerializer.Deserialize