You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#结合LINQ实现月度内每周工时汇总 优化重复查询逻辑

解决方案

现存问题

你当前的写法存在三个明显问题:

  • 对同一数据源重复执行4次Sum遍历,存在不必要的性能损耗
  • 逐周硬编码判断逻辑,后续周数调整(比如当月跨5周)需要手动新增重复代码块,维护成本高
  • 前后端耦合度高,周范围计算依赖前端moment.js的结果,容易因为两端周起始规则不一致出现统计偏差

1. 日期周范围计算实现

直接给日期类型写扩展方法,不需要依赖前端传入周起止参数,后端可直接根据ShiftDate计算所属周的范围,默认周起始日为周一,可按需修改为周日:

public static class DateTimeExtension
{
    /// <summary>
    /// 获取指定日期所在周的起止时间
    /// </summary>
    /// <param name="date">输入日期</param>
    /// <param name="weekStartDay">周起始日,默认周一</param>
    /// <returns>(周开始日期0点,周结束日期23:59:59)</returns>
    public static (DateTime Start, DateTime End) GetWeekRange(this DateTime date, DayOfWeek weekStartDay = DayOfWeek.Monday)
    {
        int offset = (7 + (date.DayOfWeek - weekStartDay)) % 7;
        DateTime start = date.AddDays(-offset).Date;
        DateTime end = start.AddDays(6).Date.AddSeconds(86399); // 取当天最后一秒
        return (start, end);
    }
}

2. 单次遍历完成全周工时统计

核心思路是把所有待统计的周规则放到集合中统一管理,只遍历一次班次数据源,就能把所有工时匹配到对应周完成累加,完全消除重复的判断逻辑。

写法1:兼容现有前端传参模式

如果你暂时还要保留前端传入vStart1~vEnd4的逻辑,先把周参数组装为统一集合:

// 周规则集合,后续增减周数只需要修改这里即可
var weekRules = new List<(DateTime Start, DateTime End, string PropName)>
{
    (vStart1, vEnd1, "Week1"),
    (vStart2, vEnd2, "Week2"),
    (vStart3, vEnd3, "Week3"),
    (vStart4, vEnd4, "Week4")
};

// 初始化统计结果,所有周初始工时为0
var weekStats = weekRules.ToDictionary(rule => rule.PropName, rule => 0m); // 数值类型和HoursWorked保持一致即可

// 单次遍历所有班次完成统计
foreach (var shift in pg)
{
    var matchedWeek = weekRules.FirstOrDefault(rule => 
        shift.ShiftDate >= rule.Start && shift.ShiftDate <= rule.End);
    if (matchedWeek != default)
    {
        weekStats[matchedWeek.PropName] += shift.HoursWorked;
    }
}

// 直接取统计结果赋值
var result = new
{
    Week1 = weekStats["Week1"],
    Week2 = weekStats["Week2"],
    Week3 = weekStats["Week3"],
    Week4 = weekStats["Week4"]
};

写法2:后端自动计算周范围(推荐)

完全去掉前端传周范围的逻辑,后端根据要统计的月份自动识别所有涉及的周,自动适配当月是4周还是5周的情况,不需要硬编码Week1~Week4:

// 例:统计2024年6月的班次数据
int statYear = 2024;
int statMonth = 6;
var monthShifts = pg.Where(s => s.ShiftDate.Year == statYear && s.ShiftDate.Month == statMonth).ToList();

// 自动提取当月所有不重复的周范围,按时间排序
var weekRules = monthShifts
    .Select(s => s.ShiftDate.GetWeekRange())
    .DistinctBy(range => range.Start)
    .OrderBy(range => range.Start)
    .Select((range, idx) => (range.Start, range.End, PropName = $"Week{idx+1}"))
    .ToList();

// 后续统计逻辑和上面完全一致,自动输出Week1、Week2...对应结果
var weekStats = weekRules.ToDictionary(rule => rule.PropName, rule => 0m);
foreach (var shift in monthShifts)
{
    var shiftWeek = shift.ShiftDate.GetWeekRange();
    var matchedWeek = weekRules.First(r => r.Start == shiftWeek.Start);
    weekStats[matchedWeek.PropName] += shift.HoursWorked;
}

方案优势

  • 性能更好:仅遍历一次数据源,比多次执行Sum的遍历次数少
  • 维护性强:没有重复逻辑,周数调整不需要复制粘贴判断代码
  • 准确性高:周计算逻辑统一收敛在后端,避免前后端周起始规则不一致导致的统计错误

内容的提问来源于stack exchange,提问作者Rowandinho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 20:06:26