| | |
| | | using Microsoft.AspNetCore.Mvc; |
| | | using SqlSugar; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_WMSServer.Controllers.Dashboard |
| | | { |
| | | /// <summary> |
| | | /// 仪表盘 |
| | | /// </summary> |
| | | [Route("api/Dashboard")] |
| | | [ApiController] |
| | | public class DashboardController : ControllerBase |
| | | { |
| | | private readonly ISqlSugarClient _db; |
| | | |
| | | public DashboardController(ISqlSugarClient db) |
| | | { |
| | | _db = db; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 总览数据 |
| | | /// </summary> |
| | | [HttpGet("Overview")] |
| | | public async Task<WebResponseContent> Overview() |
| | | { |
| | | var today = DateTime.Today; |
| | | var firstDayOfMonth = new DateTime(today.Year, today.Month, 1); |
| | | |
| | | // 今日入库数 |
| | | var todayInbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= today && t.TaskType >= 500 && t.TaskType < 600) |
| | | .CountAsync(); |
| | | |
| | | // 今日出库数 |
| | | var todayOutbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= today && t.TaskType >= 100 && t.TaskType < 200) |
| | | .CountAsync(); |
| | | |
| | | // 本月入库数 |
| | | var monthInbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= firstDayOfMonth && t.TaskType >= 500 && t.TaskType < 600) |
| | | .CountAsync(); |
| | | |
| | | // 本月出库数 |
| | | var monthOutbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= firstDayOfMonth && t.TaskType >= 100 && t.TaskType < 200) |
| | | .CountAsync(); |
| | | |
| | | // 当前总库存 |
| | | var totalStock = await _db.Queryable<Dt_StockInfo>().CountAsync(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, new |
| | | { |
| | | TodayInbound = todayInbound, |
| | | TodayOutbound = todayOutbound, |
| | | MonthInbound = monthInbound, |
| | | MonthOutbound = monthOutbound, |
| | | TotalStock = totalStock |
| | | }); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 每日统计 |
| | | /// </summary> |
| | | [HttpGet("DailyStats")] |
| | | public async Task<WebResponseContent> DailyStats([FromQuery] int days = 30) |
| | | { |
| | | if (days <= 0) days = 30; |
| | | if (days > 365) days = 365; |
| | | |
| | | var startDate = DateTime.Today.AddDays(-days + 1); |
| | | |
| | | var query = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= startDate) |
| | | .Select(t => new { t.InsertTime, t.TaskType }) |
| | | .ToListAsync(); |
| | | |
| | | var result = query |
| | | .GroupBy(t => t.InsertTime.Date) |
| | | .Select(g => new |
| | | { |
| | | Date = g.Key.ToString("yyyy-MM-dd"), |
| | | Inbound = g.Count(t => t.TaskType >= 500 && t.TaskType < 600), |
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200) |
| | | }) |
| | | .OrderBy(x => x.Date) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 每周统计 |
| | | /// </summary> |
| | | [HttpGet("WeeklyStats")] |
| | | public async Task<WebResponseContent> WeeklyStats([FromQuery] int weeks = 12) |
| | | { |
| | | if (weeks <= 0) weeks = 12; |
| | | |
| | | var startDate = DateTime.Today.AddDays(-weeks * 7); |
| | | |
| | | var query = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= startDate) |
| | | .Select(t => new { t.InsertTime, t.TaskType }) |
| | | .ToListAsync(); |
| | | |
| | | var result = query |
| | | .GroupBy(t => GetWeekKey(t.InsertTime)) |
| | | .Select(g => new |
| | | { |
| | | Week = g.Key, |
| | | Inbound = g.Count(t => t.TaskType >= 500 && t.TaskType < 600), |
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200) |
| | | }) |
| | | .OrderBy(x => x.Week) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | |
| | | private string GetWeekKey(DateTime date) |
| | | { |
| | | // 获取周一开始的周 (ISO 8601) |
| | | var diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7; |
| | | var monday = date.AddDays(-diff); |
| | | var weekNum = System.Globalization.CultureInfo.InvariantCulture |
| | | .Calendar.GetWeekOfYear(monday, System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); |
| | | return $"{monday.Year}-W{weekNum:D2}"; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 每月统计 |
| | | /// </summary> |
| | | [HttpGet("MonthlyStats")] |
| | | public async Task<WebResponseContent> MonthlyStats([FromQuery] int months = 12) |
| | | { |
| | | if (months <= 0) months = 12; |
| | | |
| | | var startDate = DateTime.Today.AddMonths(-months + 1); |
| | | startDate = new DateTime(startDate.Year, startDate.Month, 1); |
| | | |
| | | var query = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= startDate) |
| | | .Select(t => new { t.InsertTime, t.TaskType }) |
| | | .ToListAsync(); |
| | | |
| | | var result = query |
| | | .GroupBy(t => new { t.InsertTime.Year, t.InsertTime.Month }) |
| | | .Select(g => new |
| | | { |
| | | Month = $"{g.Key.Year}-{g.Key.Month:D2}", |
| | | Inbound = g.Count(t => t.TaskType >= 500 && t.TaskType < 600), |
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200) |
| | | }) |
| | | .OrderBy(x => x.Month) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 库存库龄分布 |
| | | /// </summary> |
| | | [HttpGet("StockAgeDistribution")] |
| | | public async Task<WebResponseContent> StockAgeDistribution() |
| | | { |
| | | var now = DateTime.Now; |
| | | |
| | | // 使用 SQL 直接分组统计,避免加载所有数据到内存 |
| | | var result = new[] |
| | | { |
| | | new { Range = "7天内", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, now) <= 7).CountAsync() }, |
| | | new { Range = "7-30天", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, now) > 7 && SqlFunc.DateDiff(DateType.Day, s.CreateDate, now) <= 30).CountAsync() }, |
| | | new { Range = "30-90天", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, now) > 30 && SqlFunc.DateDiff(DateType.Day, s.CreateDate, now) <= 90).CountAsync() }, |
| | | new { Range = "90天以上", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, now) > 90).CountAsync() } |
| | | }; |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 各仓库库存分布 |
| | | /// </summary> |
| | | [HttpGet("StockByWarehouse")] |
| | | public async Task<WebResponseContent> StockByWarehouse() |
| | | { |
| | | // 先查询仓库名称 |
| | | var warehouses = await _db.Queryable<Dt_Warehouse>() |
| | | .Select(w => new { w.WarehouseId, w.WarehouseName }) |
| | | .ToListAsync(); |
| | | var warehouseDict = warehouses.ToDictionary(w => w.WarehouseId, w => w.WarehouseName); |
| | | |
| | | // 查询库存数据并在内存中分组 |
| | | var stocks = await _db.Queryable<Dt_StockInfo>() |
| | | .Select(s => new { s.WarehouseId }) |
| | | .ToListAsync(); |
| | | |
| | | var result = stocks |
| | | .GroupBy(s => s.WarehouseId) |
| | | .Select(g => new |
| | | { |
| | | Warehouse = warehouseDict.TryGetValue(g.Key, out var name) ? name : $"仓库{g.Key}", |
| | | Count = g.Count() |
| | | }) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | } |
| | | } |
| | | using Microsoft.AspNetCore.Authorization;
|
| | | using Microsoft.AspNetCore.Mvc;
|
| | | using SqlSugar;
|
| | | using WIDESEA_Common.LocationEnum;
|
| | | using WIDESEA_Core;
|
| | | using WIDESEA_Model.Models;
|
| | |
|
| | | namespace WIDESEA_WMSServer.Controllers.Dashboard
|
| | | {
|
| | | /// <summary>
|
| | | /// 仪表盘
|
| | | /// </summary>
|
| | | [Route("api/Dashboard")]
|
| | | [ApiController]
|
| | | public class DashboardController : ControllerBase
|
| | | {
|
| | | private readonly ISqlSugarClient _db;
|
| | |
|
| | | public DashboardController(ISqlSugarClient db)
|
| | | {
|
| | | _db = db;
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | | /// 总览数据
|
| | | /// </summary>
|
| | | [HttpGet("Overview"), AllowAnonymous]
|
| | | public async Task<WebResponseContent> Overview()
|
| | | {
|
| | | try
|
| | | {
|
| | | var today = DateTime.Today;
|
| | | var firstDayOfMonth = new DateTime(today.Year, today.Month, 1);
|
| | |
|
| | | // 今日入库数
|
| | | var todayInbound = await _db.Queryable<Dt_Task_Hty>()
|
| | | .Where(t => t.InsertTime >= today && t.TaskType >= 500 && t.TaskType < 600)
|
| | | .CountAsync();
|
| | |
|
| | | // 今日出库数
|
| | | var todayOutbound = await _db.Queryable<Dt_Task_Hty>()
|
| | | .Where(t => t.InsertTime >= today && t.TaskType >= 100 && t.TaskType < 200)
|
| | | .CountAsync();
|
| | |
|
| | | // 本月入库数
|
| | | var monthInbound = await _db.Queryable<Dt_Task_Hty>()
|
| | | .Where(t => t.InsertTime >= firstDayOfMonth && t.TaskType >= 500 && t.TaskType < 600)
|
| | | .CountAsync();
|
| | |
|
| | | // 本月出库数
|
| | | var monthOutbound = await _db.Queryable<Dt_Task_Hty>()
|
| | | .Where(t => t.InsertTime >= firstDayOfMonth && t.TaskType >= 100 && t.TaskType < 200)
|
| | | .CountAsync();
|
| | |
|
| | | // 当前总库存
|
| | | var totalStock = await _db.Queryable<Dt_StockInfo>().CountAsync();
|
| | |
|
| | | return WebResponseContent.Instance.OK(null, new
|
| | | {
|
| | | TodayInbound = todayInbound,
|
| | | TodayOutbound = todayOutbound,
|
| | | MonthInbound = monthInbound,
|
| | | MonthOutbound = monthOutbound,
|
| | | TotalStock = totalStock
|
| | | });
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | return WebResponseContent.Instance.Error($"总览数据获取失败: {ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | | /// 每日统计
|
| | | /// </summary>
|
| | | [HttpGet("DailyStats"), AllowAnonymous]
|
| | | public async Task<WebResponseContent> DailyStats([FromQuery] int days = 30)
|
| | | {
|
| | | try
|
| | | {
|
| | | if (days <= 0) days = 30;
|
| | | if (days > 365) days = 365;
|
| | |
|
| | | var startDate = DateTime.Today.AddDays(-days + 1);
|
| | | var endDate = DateTime.Today; // 包含今天
|
| | |
|
| | | var query = await _db.Queryable<Dt_Task_Hty>()
|
| | | .Where(t => t.InsertTime >= startDate && t.InsertTime <= endDate)
|
| | | .Select(t => new { t.InsertTime, t.TaskType })
|
| | | .ToListAsync();
|
| | |
|
| | | // 生成日期范围
|
| | | var allDates = new List<DateTime>();
|
| | | for (var date = startDate; date <= endDate; date = date.AddDays(1))
|
| | | {
|
| | | allDates.Add(date);
|
| | | }
|
| | |
|
| | | // 按日期分组统计
|
| | | var groupedData = query
|
| | | .GroupBy(t => t.InsertTime.Date)
|
| | | .Select(g => new
|
| | | {
|
| | | Date = g.Key,
|
| | | Inbound = g.Count(t => t.TaskType >= 200 && t.TaskType < 300),
|
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200)
|
| | | })
|
| | | .ToDictionary(x => x.Date, x => x);
|
| | |
|
| | | // 补全缺失日期
|
| | | var result = allDates.Select(date =>
|
| | | {
|
| | | if (groupedData.TryGetValue(date, out var data))
|
| | | {
|
| | | return new
|
| | | {
|
| | | Date = date.ToString("MM-dd"),
|
| | | Inbound = data.Inbound,
|
| | | Outbound = data.Outbound
|
| | | };
|
| | | }
|
| | | else
|
| | | {
|
| | | return new
|
| | | {
|
| | | Date = date.ToString("MM-dd"),
|
| | | Inbound = 0,
|
| | | Outbound = 0
|
| | | };
|
| | | }
|
| | | })
|
| | | .OrderBy(x => x.Date)
|
| | | .ToList();
|
| | |
|
| | | return WebResponseContent.Instance.OK(null, result);
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | return WebResponseContent.Instance.Error($"每日统计获取失败: {ex.Message}");
|
| | | }
|
| | | }
|
| | | /// <summary>
|
| | | /// 每周统计
|
| | | /// </summary>
|
| | | /// <remarks>
|
| | | /// 注意:数据在 SQL 层过滤后,在应用层按 ISO 8601 周键分组。
|
| | | /// 周键为 "YYYY-Www" 格式,无法直接在 SQL 层用 GROUP BY 实现。
|
| | | /// </remarks>
|
| | | [HttpGet("WeeklyStats"), AllowAnonymous]
|
| | | public async Task<WebResponseContent> WeeklyStats([FromQuery] int weeks = 12)
|
| | | {
|
| | | try
|
| | | {
|
| | | if (weeks <= 0) weeks = 12;
|
| | |
|
| | | var startDate = DateTime.Today.AddDays(-weeks * 7);
|
| | |
|
| | | var query = await _db.Queryable<Dt_Task_Hty>()
|
| | | .Where(t => t.InsertTime >= startDate)
|
| | | .Select(t => new { t.InsertTime, t.TaskType })
|
| | | .ToListAsync();
|
| | |
|
| | | var result = query
|
| | | .GroupBy(t => GetWeekKey(t.InsertTime))
|
| | | .Select(g => new
|
| | | {
|
| | | Week = g.Key,
|
| | | Inbound = g.Count(t => t.TaskType >= 200 && t.TaskType < 300),
|
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200)
|
| | | })
|
| | | .OrderBy(x => x.Week)
|
| | | .ToList();
|
| | |
|
| | | return WebResponseContent.Instance.OK(null, result);
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | return WebResponseContent.Instance.Error($"每周统计获取失败: {ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | private string GetWeekKey(DateTime date)
|
| | | {
|
| | | // 获取周一开始的周 (ISO 8601)
|
| | | var diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7;
|
| | | var monday = date.AddDays(-diff);
|
| | | var weekNum = System.Globalization.CultureInfo.InvariantCulture
|
| | | .Calendar.GetWeekOfYear(monday, System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
|
| | | return $"{monday.Year}-W{weekNum:D2}";
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | | /// 每月统计
|
| | | /// </summary>
|
| | | /// <remarks>
|
| | | /// 按年月统计入站和出站任务数量
|
| | | /// </remarks>
|
| | | [HttpGet("MonthlyStats"), AllowAnonymous]
|
| | | public async Task<WebResponseContent> MonthlyStats([FromQuery] int months = 12)
|
| | | {
|
| | | try
|
| | | {
|
| | | if (months <= 0) months = 12;
|
| | |
|
| | | var startDate = DateTime.Today.AddMonths(-months + 1);
|
| | | startDate = new DateTime(startDate.Year, startDate.Month, 1);
|
| | |
|
| | | var monthlyStats = await _db.Queryable<Dt_Task_Hty>()
|
| | | .Where(t => t.InsertTime >= startDate)
|
| | | .GroupBy(t => new { t.InsertTime.Year, t.InsertTime.Month })
|
| | | .Select(t => new
|
| | | {
|
| | | Year = t.InsertTime.Year,
|
| | | Month = t.InsertTime.Month,
|
| | | Inbound = SqlFunc.AggregateSum(
|
| | | SqlFunc.IIF(t.TaskType >= 200 && t.TaskType < 300, 1, 0)
|
| | | ),
|
| | | Outbound = SqlFunc.AggregateSum(
|
| | | SqlFunc.IIF(t.TaskType >= 100 && t.TaskType < 200, 1, 0)
|
| | | )
|
| | | })
|
| | | .OrderBy(t => t.Year)
|
| | | .OrderBy(t => t.Month)
|
| | | .ToListAsync();
|
| | |
|
| | | // 生成所有需要统计的月份列表
|
| | | var allMonths = new List<DateTime>();
|
| | | var currentMonth = startDate;
|
| | | var endMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
|
| | |
|
| | | while (currentMonth <= endMonth)
|
| | | {
|
| | | allMonths.Add(currentMonth);
|
| | | currentMonth = currentMonth.AddMonths(1);
|
| | | }
|
| | |
|
| | | // 将查询结果转换为字典,方便查找
|
| | | var statsDict = monthlyStats.ToDictionary(
|
| | | s => $"{s.Year}-{s.Month:D2}",
|
| | | s => new { s.Inbound, s.Outbound }
|
| | | );
|
| | |
|
| | | // 构建完整的结果列表,包含所有月份
|
| | | var result = new List<object>();
|
| | | foreach (var month in allMonths)
|
| | | {
|
| | | var monthKey = $"{month.Year}-{month.Month:D2}";
|
| | |
|
| | | if (statsDict.TryGetValue(monthKey, out var stat))
|
| | | {
|
| | | result.Add(new
|
| | | {
|
| | | Month = monthKey,
|
| | | Inbound = stat.Inbound,
|
| | | Outbound = stat.Outbound
|
| | | });
|
| | | }
|
| | | else
|
| | | {
|
| | | result.Add(new
|
| | | {
|
| | | Month = monthKey,
|
| | | Inbound = 0,
|
| | | Outbound = 0
|
| | | });
|
| | | }
|
| | | }
|
| | |
|
| | | return WebResponseContent.Instance.OK(null, result);
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | // 记录异常日志(实际项目中建议使用日志框架)
|
| | | // _logger.LogError(ex, "每月统计获取失败");
|
| | |
|
| | | return WebResponseContent.Instance.Error($"每月统计获取失败: {ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | | /// 库存库龄分布
|
| | | /// </summary>
|
| | | [HttpGet("StockAgeDistribution"), AllowAnonymous]
|
| | | public async Task<WebResponseContent> StockAgeDistribution()
|
| | | {
|
| | | try
|
| | | {
|
| | | var today = DateTime.Today;
|
| | |
|
| | | // 使用 SQL 直接分组统计,避免加载所有数据到内存
|
| | | var result = new[]
|
| | | {
|
| | | new { Range = "7天内", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) <= 7).CountAsync() },
|
| | | new { Range = "7-30天", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) > 7 && SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) <= 30).CountAsync() },
|
| | | new { Range = "30-90天", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) > 30 && SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) <= 90).CountAsync() },
|
| | | new { Range = "90天以上", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) > 90).CountAsync() }
|
| | | };
|
| | |
|
| | | return WebResponseContent.Instance.OK(null, result);
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | return WebResponseContent.Instance.Error($"库存库龄分布获取失败: {ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | | /// 各仓库库存分布
|
| | | /// </summary>
|
| | | /// <remarks>
|
| | | /// 使用 SQL GROUP BY 在数据库层面聚合,避免加载全部库存记录到内存。
|
| | | /// </remarks>
|
| | | [HttpGet("StockByWarehouse"), AllowAnonymous]
|
| | | public async Task<WebResponseContent> StockByWarehouse()
|
| | | {
|
| | | try
|
| | | {
|
| | | // 查询所有仓库信息
|
| | | var warehouses = await _db.Queryable<Dt_Warehouse>()
|
| | | .Select(w => new { w.WarehouseId, w.WarehouseName })
|
| | | .ToListAsync();
|
| | |
|
| | | // 查询所有货位信息,按仓库分组统计总数
|
| | | var locationGroups = await _db.Queryable<Dt_LocationInfo>()
|
| | | .GroupBy(l => l.WarehouseId)
|
| | | .Select(l => new
|
| | | {
|
| | | WarehouseId = l.WarehouseId,
|
| | | TotalLocations = SqlFunc.AggregateCount(l.Id)
|
| | | })
|
| | | .ToListAsync();
|
| | |
|
| | | // 查询状态不为Free的货位信息(有货货位),按仓库分组统计
|
| | | var occupiedLocationGroups = await _db.Queryable<Dt_LocationInfo>()
|
| | | .Where(l => l.LocationStatus != (int)LocationStatusEnum.Free)
|
| | | .GroupBy(l => l.WarehouseId)
|
| | | .Select(l => new
|
| | | {
|
| | | WarehouseId = l.WarehouseId,
|
| | | OccupiedLocations = SqlFunc.AggregateCount(l.Id)
|
| | | })
|
| | | .ToListAsync();
|
| | |
|
| | | // 将仓库信息与货位统计信息合并
|
| | | var result = warehouses.Select(w =>
|
| | | {
|
| | | var totalLocations = locationGroups.FirstOrDefault(lg => lg.WarehouseId == w.WarehouseId)?.TotalLocations ?? 0;
|
| | | var occupiedLocations = occupiedLocationGroups.FirstOrDefault(og => og.WarehouseId == w.WarehouseId)?.OccupiedLocations ?? 0;
|
| | | var emptyLocations = totalLocations - occupiedLocations;
|
| | |
|
| | | var occupiedPercentage = totalLocations > 0 ? Math.Round((double)occupiedLocations / totalLocations * 100, 2) : 0.0;
|
| | | var emptyPercentage = totalLocations > 0 ? Math.Round((double)emptyLocations / totalLocations * 100, 2) : 0.0;
|
| | |
|
| | | return new
|
| | | {
|
| | | Warehouse = w.WarehouseName,
|
| | | Total = totalLocations,
|
| | | HasStock = occupiedLocations,
|
| | | NoStock = emptyLocations,
|
| | | HasStockPercentage = $"{occupiedPercentage}%",
|
| | | NoStockPercentage = $"{emptyPercentage}%"
|
| | | };
|
| | | }).ToList();
|
| | |
|
| | | return WebResponseContent.Instance.OK(null, result);
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | return WebResponseContent.Instance.Error($"各仓库库存分布获取失败: {ex.Message}");
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|