using System.Globalization;
using FoodLabeling.Application.Services.DbModels;
using SqlSugar;
namespace FoodLabeling.Application.Helpers;
///
/// Print Log「Label ID」:按门店 + 自然日对打印任务排序,格式 yyyyMMdd-n(非 fl_label.LabelCode)。
///
public static class ReportsPrintLogDailyLabelIdHelper
{
///
/// 为列表/导出行计算门店当日打印序号。
///
public static async Task> ResolveDailyLabelIdsAsync(
ISqlSugarClient db,
IReadOnlyList tasks)
{
var result = new Dictionary(StringComparer.Ordinal);
if (tasks.Count == 0)
{
return result;
}
foreach (var task in tasks)
{
if (string.IsNullOrWhiteSpace(task.TaskId))
{
continue;
}
result.TryAdd(task.TaskId.Trim(), "无");
}
var buckets = tasks
.Where(t =>
!string.IsNullOrWhiteSpace(t.TaskId) &&
!string.IsNullOrWhiteSpace(t.LocationId) &&
t.PrintedAt != default)
.GroupBy(t => (LocationId: t.LocationId!.Trim(), Day: t.PrintedAt.Date))
.ToList();
foreach (var bucket in buckets)
{
var locId = bucket.Key.LocationId;
var dayStart = bucket.Key.Day;
var dayEnd = dayStart.AddDays(1);
var dayTasks = await db.Queryable()
.Where(t => t.LocationId == locId)
.Where(t =>
SqlFunc.IsNull(t.PrintedAt, t.CreationTime) >= dayStart &&
SqlFunc.IsNull(t.PrintedAt, t.CreationTime) < dayEnd)
.OrderBy(t => SqlFunc.IsNull(t.PrintedAt, t.CreationTime), OrderByType.Asc)
.OrderBy(t => t.Id, OrderByType.Asc)
.Select(t => new { t.Id, PrintedAt = SqlFunc.IsNull(t.PrintedAt, t.CreationTime) })
.ToListAsync();
for (var i = 0; i < dayTasks.Count; i++)
{
result[dayTasks[i].Id] = Format(dayStart, i + 1);
}
}
return result;
}
///
/// 预览/即将打印:取门店在指定自然日的下一个打印序号(不落库)。
///
public static async Task ResolveNextDailyLabelIdAsync(
ISqlSugarClient db,
string locationId,
DateTime referenceTime)
{
var locId = locationId?.Trim();
if (string.IsNullOrWhiteSpace(locId))
{
return "无";
}
var dayStart = referenceTime.Date;
var dayEnd = dayStart.AddDays(1);
var count = await db.Queryable()
.Where(t => t.LocationId == locId)
.Where(t =>
SqlFunc.IsNull(t.PrintedAt, t.CreationTime) >= dayStart &&
SqlFunc.IsNull(t.PrintedAt, t.CreationTime) < dayEnd)
.CountAsync();
return Format(dayStart, count + 1);
}
public static string Format(DateTime printDay, int sequence) =>
$"{printDay:yyyyMMdd}-{sequence}";
///
/// 为门店当日后续打印任务预留连续序号(不含已存在任务之外的并发锁,与列表展示规则一致)。
///
public static async Task> ResolveNextDailyLabelIdsAsync(
ISqlSugarClient db,
string locationId,
DateTime day,
int count)
{
var result = new List();
if (count <= 0 || string.IsNullOrWhiteSpace(locationId))
{
return result;
}
var locId = locationId.Trim();
var dayStart = day.Date;
var dayEnd = dayStart.AddDays(1);
var existingCount = await db.Queryable()
.Where(t => t.LocationId == locId)
.Where(t =>
SqlFunc.IsNull(t.PrintedAt, t.CreationTime) >= dayStart &&
SqlFunc.IsNull(t.PrintedAt, t.CreationTime) < dayEnd)
.CountAsync();
for (var i = 1; i <= count; i++)
{
result.Add(Format(dayStart, existingCount + i));
}
return result;
}
///
/// App Print Log 单日筛选:解析 / 为服务器本地自然日。
/// 两者均未传时返回 null(不按日过滤,兼容 App 未传 printDate 的历史行为)。
///
public static DateTime? ResolvePrintLogFilterCalendarDay(DateTime? printDate, string? printDateDay)
{
var text = printDateDay?.Trim();
if (!string.IsNullOrWhiteSpace(text))
{
if (DateTime.TryParseExact(
text,
"yyyy-MM-dd",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out var parsedExact))
{
return parsedExact.Date;
}
if (DateTime.TryParse(
text,
CultureInfo.InvariantCulture,
DateTimeStyles.AllowWhiteSpaces,
out var parsed))
{
return parsed.Date;
}
throw new Volo.Abp.UserFriendlyException("printDate 格式不正确,请使用 yyyy-MM-dd");
}
if (!printDate.HasValue)
{
return null;
}
var value = printDate.Value;
return value.Kind == DateTimeKind.Utc ? value.ToLocalTime().Date : value.Date;
}
///
/// 按自然日过滤打印任务(MySQL DATE(IFNULL(PrintedAt, CreationTime)),避免 DateTime 区间比较时区偏差)。
///
public static ISugarQueryable ApplyPrintTaskCalendarDayFilter(
ISugarQueryable query,
DateTime filterDay)
{
var dayText = filterDay.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
return query.Where("DATE(IFNULL(PrintedAt, CreationTime)) = @filterDay", new { filterDay = dayText });
}
///
/// 多表 Join 查询按自然日过滤(首表别名 t)。
///
public static ISugarQueryable ApplyPrintTaskCalendarDayFilter(
ISugarQueryable query,
DateTime filterDay)
{
var dayText = filterDay.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
return query.Where("DATE(IFNULL(t.PrintedAt, t.CreationTime)) = @filterDay", new { filterDay = dayText });
}
///
/// 五表 Join(首表别名 t)。
///
public static ISugarQueryable ApplyPrintTaskCalendarDayFilter(
ISugarQueryable query,
DateTime filterDay)
{
var dayText = filterDay.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
return query.Where("DATE(IFNULL(t.PrintedAt, t.CreationTime)) = @filterDay", new { filterDay = dayText });
}
///
/// App Print Log 单日筛选:printDate 的自然日区间;未传则默认服务器当天。
///
public static (DateTime dayStart, DateTime dayEndExcl) ResolvePrintLogFilterDayRange(DateTime? printDate)
{
var day = ResolvePrintLogFilterCalendarDay(printDate, null) ?? DateTime.Today;
return (day, day.AddDays(1));
}
public readonly struct PrintTaskScopeKey
{
public PrintTaskScopeKey(string taskId, string? locationId, DateTime printedAt)
{
TaskId = taskId;
LocationId = locationId;
PrintedAt = printedAt;
}
public string TaskId { get; }
public string? LocationId { get; }
public DateTime PrintedAt { get; }
}
}