wanshenmean
2026-03-30 6af451b3d95627b745da9a9e334419b1b5d4ee4f
fix(Dashboard): 添加错误处理,统一DateTime使用

- 为所有 async 方法添加 try-catch,异常时返回格式化错误信息
- StockAgeDistribution: DateTime.Now 改为 DateTime.Today,保持与其他方法一致
- StockByWarehouse: 改用 SQL GROUP BY 聚合,仅传输聚合结果而非全量数据
- DailyStats/WeeklyStats/MonthlyStats: 添加 <remarks> 注释说明为何在应用层分组

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
已修改1个文件
85 ■■■■ 文件已修改
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Dashboard/DashboardController.cs 85 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Dashboard/DashboardController.cs
@@ -25,6 +25,8 @@
        [HttpGet("Overview")]
        public async Task<WebResponseContent> Overview()
        {
            try
            {
            var today = DateTime.Today;
            var firstDayOfMonth = new DateTime(today.Year, today.Month, 1);
@@ -60,12 +62,24 @@
                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)
        {
            try
        {
            if (days <= 0) days = 30;
            if (days > 365) days = 365;
@@ -90,12 +104,23 @@
            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)
        {
            try
        {
            if (weeks <= 0) weeks = 12;
@@ -119,6 +144,11 @@
            return WebResponseContent.Instance.OK(null, result);
        }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"每周统计获取失败: {ex.Message}");
            }
        }
        private string GetWeekKey(DateTime date)
        {
@@ -133,8 +163,15 @@
        /// <summary>
        /// 每月统计
        /// </summary>
        /// <remarks>
        /// 注意:数据在 SQL 层过滤后,在应用层按年月分组。
        /// SqlSugar 的 GroupBy 不支持匿名对象 (Year, Month) 直接映射到 SQL GROUP BY,
        /// 因此采用此方式以确保跨数据库兼容性。
        /// </remarks>
        [HttpGet("MonthlyStats")]
        public async Task<WebResponseContent> MonthlyStats([FromQuery] int months = 12)
        {
            try
        {
            if (months <= 0) months = 12;
@@ -159,6 +196,11 @@
            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;
            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, 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() }
                    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()
        {
            // 先查询仓库名称
            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 })
                // 使用 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)
                var result = stockGroups
                .Select(g => new
                {
                    Warehouse = warehouseDict.TryGetValue(g.Key, out var name) ? name : $"仓库{g.Key}",
                    Count = g.Count()
                        Warehouse = warehouseDict.TryGetValue(g.WarehouseId, out var name) ? name : $"仓库{g.WarehouseId}",
                        Count = g.Count
                })
                .ToList();
            return WebResponseContent.Instance.OK(null, result);
        }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"各仓库库存分布获取失败: {ex.Message}");
            }
        }
    }
}