wanshenmean
4 天以前 f7ec7a32e8cddcb976093c826e3a0d1ed61fb335
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Dashboard/DashboardController.cs
@@ -25,99 +25,129 @@
        [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
            try
            {
                TodayInbound = todayInbound,
                TodayOutbound = todayOutbound,
                MonthInbound = monthInbound,
                MonthOutbound = monthOutbound,
                TotalStock = totalStock
            });
                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>
        /// <remarks>
        /// 注意:数据在 SQL 层过滤后,在应用层按日期分组。
        /// SqlSugar 的 GroupBy 不支持对 .Date 这样的计算列直接生成 SQL GROUP BY,
        /// 因此采用此方式以确保跨数据库兼容性。
        /// </remarks>
        [HttpGet("DailyStats")]
        public async Task<WebResponseContent> DailyStats([FromQuery] int days = 30)
        {
            if (days <= 0) days = 30;
            if (days > 365) days = 365;
            try
            {
                if (days <= 0) days = 30;
                if (days > 365) days = 365;
            var startDate = DateTime.Today.AddDays(-days + 1);
                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 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();
                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);
                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")]
        public async Task<WebResponseContent> WeeklyStats([FromQuery] int weeks = 12)
        {
            if (weeks <= 0) weeks = 12;
            try
            {
                if (weeks <= 0) weeks = 12;
            var startDate = DateTime.Today.AddDays(-weeks * 7);
                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 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();
                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);
                return WebResponseContent.Instance.OK(null, result);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"每周统计获取失败: {ex.Message}");
            }
        }
        private string GetWeekKey(DateTime date)
@@ -133,31 +163,43 @@
        /// <summary>
        /// 每月统计
        /// </summary>
        /// <remarks>
        /// 注意:数据在 SQL 层过滤后,在应用层按年月分组。
        /// SqlSugar 的 GroupBy 不支持匿名对象 (Year, Month) 直接映射到 SQL GROUP BY,
        /// 因此采用此方式以确保跨数据库兼容性。
        /// </remarks>
        [HttpGet("MonthlyStats")]
        public async Task<WebResponseContent> MonthlyStats([FromQuery] int months = 12)
        {
            if (months <= 0) months = 12;
            try
            {
                if (months <= 0) months = 12;
            var startDate = DateTime.Today.AddMonths(-months + 1);
            startDate = new DateTime(startDate.Year, startDate.Month, 1);
                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 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();
                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);
                return WebResponseContent.Instance.OK(null, result);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"每月统计获取失败: {ex.Message}");
            }
        }
        /// <summary>
@@ -166,47 +208,64 @@
        [HttpGet("StockAgeDistribution")]
        public async Task<WebResponseContent> StockAgeDistribution()
        {
            var now = DateTime.Now;
            // 使用 SQL 直接分组统计,避免加载所有数据到内存
            var result = new[]
            try
            {
                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() }
            };
                var today = DateTime.Today;
            return WebResponseContent.Instance.OK(null, result);
                // 使用 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")]
        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);
            try
            {
                // 查询仓库名称
                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();
                // 使用 SQL GROUP BY 在数据库层面聚合,仅返回聚合结果
                var stockGroups = await _db.Queryable<Dt_StockInfo>()
                    .GroupBy(s => s.WarehouseId)
                    .Select(s => new { s.WarehouseId, Count = SqlFunc.AggregateCount(s.Id) })
                    .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();
                var result = stockGroups
                    .Select(g => new
                    {
                        Warehouse = warehouseDict.TryGetValue(g.WarehouseId, out var name) ? name : $"仓库{g.WarehouseId}",
                        Count = g.Count
                    })
                    .ToList();
            return WebResponseContent.Instance.OK(null, result);
                return WebResponseContent.Instance.OK(null, result);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"各仓库库存分布获取失败: {ex.Message}");
            }
        }
    }
}