如何使用Thymeleaf将ResultSet数据映射到HTML周状态表并实现状态顺延
自行车周状态表格实现方案
核心思路是不要在模板层做复杂的状态匹配和顺延逻辑,先在Controller层把数据预处理成模板可直接渲染的结构,再传给页面,避免Thymeleaf写冗余难维护的判断逻辑。
步骤1:Controller层数据预处理
首先调整Controller里的逻辑,提前算好每辆自行车在周内每一天的最终状态:
- 先固定当前自然周的7个日期,按周一到周日排序生成
weekDates列表,统一使用LocalDate类型(截断时分秒,避免日期匹配失败) - 把查询得到的
weekList按自行车ID分组,再按状态变更日期升序排序 - 对每辆自行车,先取周前最后一条状态作为初始状态,再逐天遍历周日期:如果当天有状态变更,就更新当前状态为最新值,把当天的状态存入双层映射结构,最终得到
bikeId -> 日期 -> 状态详情的映射表 - 把周日期列表、预处理好的日状态映射、全量自行车列表都传入Model
参考代码:
// 1. 生成当前自然周的7个日期(周一到周日,示例以2022-05-03当周为例,可替换为动态计算逻辑) List<LocalDate> weekDates = new ArrayList<>(); LocalDate weekStart = LocalDate.of(2022,5,3); for (int i=0; i<7; i++){ weekDates.add(weekStart.plusDays(i)); } // 2. 按bikeId分组状态记录 Map<Long, List<BikeWeek>> statusGroupByBike = weekList.stream() .collect(Collectors.groupingBy(BikeWeek::getId)); // 3. 预处理每辆车每天的状态 Map<Long, Map<LocalDate, BikeWeek>> dailyStatusMap = new HashMap<>(); for (Bike bike : bikes) { List<BikeWeek> bikeStatuses = statusGroupByBike.getOrDefault(bike.getId(), Collections.emptyList()) .stream() .sorted(Comparator.comparing(BikeWeek::getDate)) .toList(); // 初始状态取周前最后一条(日期早于周起始的那条,即SQL中row_number=1且不在周区间的记录) BikeWeek currentStatus = bikeStatuses.stream() .filter(s -> s.getDate().before(java.sql.Date.valueOf(weekStart))) .findFirst() .orElse(null); Map<LocalDate, BikeWeek> dayStatus = new HashMap<>(); for (LocalDate day : weekDates) { // 检查当天是否有状态变更,有则更新当前状态 final LocalDate targetDay = day; Optional<BikeWeek> dayChange = bikeStatuses.stream() .filter(s -> s.getDate().equals(java.sql.Date.valueOf(targetDay))) .findFirst(); if (dayChange.isPresent()){ currentStatus = dayChange.get(); } dayStatus.put(day, currentStatus); } dailyStatusMap.put(bike.getId(), dayStatus); } // 4. 传入Model model.addAttribute("weekDates", weekDates); model.addAttribute("dailyStatusMap", dailyStatusMap); model.addAttribute("bikes", bikes);
注意:如果
BikeWeek.getDate()返回的是java.util.Date类型,建议在SQL查询时直接转为日期类型截断时间,或者在Java层统一转成LocalDate再做比较,避免时分秒不一致导致日期匹配失败。
步骤2:Thymeleaf模板渲染
模板层只做遍历渲染,不需要写状态顺延的判断逻辑:
- 表头遍历
weekDates生成周一到周日的列,不用硬写固定日期 - 表体先遍历
bikes生成每一行的自行车信息列 - 每行内再遍历
weekDates,直接从dailyStatusMap里取对应自行车、对应日期的状态渲染即可
参考模板代码:
<div class="s-table-container"> <table class="s-table"> <thead> <tr> <th style="text-align: left;">bike</th> <th th:each="day : ${weekDates}" style="text-align: center;" th:text="${#temporals.format(day, 'EEEE')}"></th> </tr> </thead> <tbody> <tr th:each="bike : ${bikes}"> <td style="text-align: left;" th:text="${bike.name} + ' ' + ${bike.number}"></td> <td th:each="day : ${weekDates}" style="text-align: center;"> <span th:if="${dailyStatusMap[bike.id][day] != null}" th:text="${dailyStatusMap[bike.id][day].status} + ' (' + ${dailyStatusMap[bike.id][day].name} + ' ' + ${dailyStatusMap[bike.id][day].surname} + ')'"> </span> <span th:unless="${dailyStatusMap[bike.id][day] != null}">无状态记录</span> </td> </tr> </tbody> </table> </div>
如果需要展示租用人姓名/姓氏,直接在单元格里取对应字段即可;如果某辆车完全没有状态记录,也可以自定义展示默认值。
内容的提问来源于stack exchange,提问作者Vitalii Pshenychniuk
相关产品推荐
相关产品推荐

