using BoardApp.Config; namespace BoardApp.Services; public sealed class WarehouseCalendarService { public const string WarehouseCalendarDataSrcId = "warehouse_calendar_events"; private readonly SqlDataSourceService _sql; public WarehouseCalendarService(SqlDataSourceService sql) { _sql = sql; } private static object? RowVal(Dictionary row, params string[] names) { foreach (var name in names) { var kv = row.FirstOrDefault(x => string.Equals(x.Key, name, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(kv.Key)) return kv.Value; } return null; } public static string FormatDateKey(int y, int m, int d) => $"{y}-{m:D2}-{d:D2}"; private static string? DateKeyFromDatestr(object? v) { if (v is DateTime dt) { return FormatDateKey(dt.Year, dt.Month, dt.Day); } if (v is string s) { var t = s.Trim(); if (System.Text.RegularExpressions.Regex.IsMatch(t, @"^\d{4}-\d{2}-\d{2}")) { return t[..10]; } if (DateTime.TryParse(t, out var dt2)) { return FormatDateKey(dt2.Year, dt2.Month, dt2.Day); } } return null; } private static string? StrOpt(object? v) { if (v is null) return null; var s = v.ToString()?.Trim(); return string.IsNullOrEmpty(s) ? null : s; } private static WarehousePlanEntry RowToPlanEntry(Dictionary row) { var datestrRaw = RowVal(row, "datestr", "DispatchDate"); var key = DateKeyFromDatestr(datestrRaw); var datestr = key ?? (datestrRaw?.ToString() ?? ""); return new WarehousePlanEntry { Datestr = datestr, Eventstr = StrOpt(RowVal(row, "eventstr", "ShipmentCode")) ?? "(无单号)", Note_a = StrOpt(RowVal(row, "note_a", "C1")), Note_b = StrOpt(RowVal(row, "note_b")), Event_status = StrOpt(RowVal(row, "event_status")), Event_src = StrOpt(RowVal(row, "event_src", "CustomerShortName")), Event_to = StrOpt(RowVal(row, "event_to", "ShippingToCountry")) }; } public async Task>> LoadWarehousePlansGroupedByDate(int year, int month) { var startDate = new DateTime(year, month, 1); var endDate = startDate.AddMonths(1); var rows = await _sql.ExecuteDataSourceById(WarehouseCalendarDataSrcId, new Dictionary { ["startDate"] = startDate, ["endDate"] = endDate }); var map = new Dictionary>(StringComparer.Ordinal); foreach (var r in rows) { var key = DateKeyFromDatestr(RowVal(r, "datestr", "DispatchDate")); if (key is null) continue; if (!map.TryGetValue(key, out var list)) { list = new List(); map[key] = list; } var entry = RowToPlanEntry(r); entry.Datestr = key; list.Add(entry); } return map; } public sealed class CalendarCell { public int? Day { get; set; } public string? DateKey { get; set; } public bool IsToday { get; set; } public List Plans { get; set; } = new(); } public static List> BuildMonthRows(int year, int month, Dictionary> plansByDate) { var firstDay = new DateTime(year, month, 1); var daysInMonth = DateTime.DaysInMonth(year, month); var startOffset = (((int)firstDay.DayOfWeek) + 6) % 7; // Mon=0..Sun=6 var today = DateTime.Today; var todayKey = FormatDateKey(today.Year, today.Month, today.Day); var cells = new List(); for (var i = 0; i < startOffset; i++) { cells.Add(new CalendarCell { Day = null, DateKey = null, IsToday = false, Plans = new List() }); } for (var d = 1; d <= daysInMonth; d++) { var key = FormatDateKey(year, month, d); cells.Add(new CalendarCell { Day = d, DateKey = key, IsToday = string.Equals(key, todayKey, StringComparison.Ordinal), Plans = plansByDate.TryGetValue(key, out var ps) ? ps : new List() }); } while (cells.Count % 7 != 0) { cells.Add(new CalendarCell { Day = null, DateKey = null, IsToday = false, Plans = new List() }); } var rows = new List>(); for (var i = 0; i < cells.Count; i += 7) { rows.Add(cells.GetRange(i, 7)); } return rows; } }