按时间维度汇总业务流转历史需设计合理时间键并组合下游收集器:自然日用LocalDate分组计数,小时级用DateTimeFormatter格式化字符串并保持插入顺序,月度多指标聚合推荐teeing或自定义容器,跨天业务需统一时间偏移对齐运营日。

用 Collectors.groupingBy 按时间维度汇总业务流转历史,核心是把时间字段(如日期、小时、月份)作为分组键,再结合下游收集器做聚合统计。关键不在“怎么写”,而在于“怎么设计时间键”和“怎么组合下游操作”。
按自然日(yyyy-MM-dd)分组统计流转次数
适合看每日业务量趋势。需先将原始时间戳(如 LocalDateTime)截取到日期粒度,再作为分组键:
Map<LocalDate, Long> dailyCount = histories.stream()
.collect(Collectors.groupingBy(
h -> h.getOccurTime().toLocalDate(), // 提取日期
Collectors.counting()
));
注意:若原始时间为 Instant 或 Date,需指定时区转换,例如 instant.atZone(ZoneId.systemDefault()).toLocalDate()。
按小时(yyyy-MM-dd HH)分组并收集完整记录列表
适合分析高峰时段的详细流转行为。构造带小时信息的字符串或使用 LocalDateTime.withMinute(0).withSecond(0) 截断:
Map<String, List<BusinessFlow>> hourlyRecords = histories.stream()
.collect(Collectors.groupingBy(
h -> h.getOccurTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH")),
LinkedHashMap::new, // 保持插入顺序(可选)
Collectors.toList()
));
用 LinkedHashMap 可让结果按时间先后有序,便于后续遍历展示;若需进一步统计每小时的审批通过率,可将 Collectors.toList() 替换为自定义下游收集器。
按月分组并同时计算多个指标(数量、平均耗时、最大延迟)
多指标聚合需用 Collectors.collectingAndThen 或封装成自定义容器。推荐使用 Collectors.teeing(Java 12+)或分步映射:
- 用
groupingBy先按年月分组:h -> YearMonth.from(h.getOccurTime()) - 下游用
Collectors.teeing同时计算计数、平均耗时(单位秒)、最大延迟(假设字段为delaySeconds):
Map<YearMonth, Map<String, Object>> monthlyStats = histories.stream()
.collect(Collectors.groupingBy(
h -> YearMonth.from(h.getOccurTime()),
Collectors.teeing(
Collectors.counting(),
Collectors.averagingLong(h -> h.getDurationSeconds()),
Collectors.maxBy(Comparator.comparingLong(h -> h.getDelaySeconds()))
)
));
实际中建议封装一个 MonthlySummary 类,配合 Collectors.collectingAndThen 做类型安全聚合,避免 Map<String, Object> 的硬编码取值。
处理跨天业务与时间窗口对齐(如凌晨2点归入前一日)
真实业务中,“日维度”常按运营日(如02:00–02:00)而非自然日。此时不能直接用 toLocalDate(),需手动偏移:
- 将发生时间减去2小时,再取日期:
h.getOccurTime().minusHours(2).toLocalDate() - 或用
TemporalAdjusters配合自定义逻辑,确保所有 00:00–01:59 的记录归属前一天
该偏移必须统一应用在分组键提取环节,且要和前端/报表口径严格一致,否则会出现数据割裂。

















