按日期分组展示用户交易余额的循环逻辑问题排查
解决按日期分组展示每日收支余额的循环问题
你的问题核心是控制器未正确传递所有日期的分组数据,或Thymeleaf循环逻辑错误导致仅展示最后一组数据。以下是两种可行的解决方法:
方案一:直接在Thymeleaf中遍历分组后的Map
控制器代码(确保传递完整的分组数据)
@GetMapping("/wallet") public String walletPage(Model model) { // 获取全部交易记录 List<Transaction> allTransactions = transactionService.listAll(); // 按交易日期分组 Map<LocalDate, List<Transaction>> transactionsByDate = allTransactions.stream() .collect(Collectors.groupingBy(Transaction::getTransactionDate)); // 将分组数据传入页面 model.addAttribute("transactionsByDate", transactionsByDate); return "wallet"; }
Thymeleaf页面代码(遍历所有日期分组)
<div class="daily-transactions"> <h2>每日收支明细</h2> <!-- 遍历日期-交易列表的键值对 --> <div th:each="dateEntry : ${transactionsByDate}"> <div class="day-group"> <h3 th:text="${dateEntry.key}">2024-05-20</h3> <!-- 计算当日总收入 --> <p>当日收入:<span th:text="${#numbers.formatDecimal(dateEntry.value.stream().filter(t -> t.type == 'INCOME').mapToDouble(t -> t.amount).sum(), 2, 2)}">0.00</span></p> <!-- 计算当日总支出 --> <p>当日支出:<span th:text="${#numbers.formatDecimal(dateEntry.value.stream().filter(t -> t.type == 'EXPENSE').mapToDouble(t -> t.amount).sum(), 2, 2)}">0.00</span></p> <!-- 计算当日余额 --> <p class="balance"> 当日余额:<span th:text="${#numbers.formatDecimal( dateEntry.value.stream().filter(t -> t.type == 'INCOME').mapToDouble(t -> t.amount).sum() - dateEntry.value.stream().filter(t -> t.type == 'EXPENSE').mapToDouble(t -> t.amount).sum(), 2, 2)}">0.00</span> </p> </div> </div> </div>
方案二:提前在控制器封装每日余额DTO(推荐)
把计算逻辑移到后端,页面更简洁易维护。
1. 创建每日余额DTO
public class DailyBalance { private LocalDate date; private double totalIncome; private double totalExpense; private double dailyBalance; // 构造器 public DailyBalance(LocalDate date, double totalIncome, double totalExpense) { this.date = date; this.totalIncome = totalIncome; this.totalExpense = totalExpense; this.dailyBalance = totalIncome - totalExpense; } // Getter方法(无需Setter,只读数据) public LocalDate getDate() { return date; } public double getTotalIncome() { return totalIncome; } public double getTotalExpense() { return totalExpense; } public double getDailyBalance() { return dailyBalance; } }
2. 控制器代码(生成每日余额列表)
@GetMapping("/wallet") public String walletPage(Model model) { List<Transaction> allTransactions = transactionService.listAll(); // 分组并转换为DailyBalance列表,支持按日期排序 List<DailyBalance> dailyBalances = allTransactions.stream() .collect(Collectors.groupingBy(Transaction::getTransactionDate)) .entrySet().stream() .map(entry -> { LocalDate date = entry.getKey(); double income = entry.getValue().stream() .filter(t -> "INCOME".equals(t.getType())) .mapToDouble(Transaction::getAmount) .sum(); double expense = entry.getValue().stream() .filter(t -> "EXPENSE".equals(t.getType())) .mapToDouble(Transaction::getAmount) .sum(); return new DailyBalance(date, income, expense); }) .sorted(Comparator.comparing(DailyBalance::getDate).reversed()) // 按日期倒序展示 .collect(Collectors.toList()); model.addAttribute("dailyBalances", dailyBalances); return "wallet"; }
3. Thymeleaf页面代码(直接遍历余额列表)
<div class="daily-balances"> <h2>每日收支余额</h2> <div th:each="balance : ${dailyBalances}"> <div class="day-item"> <h3 th:text="${balance.date}">2024-05-20</h3> <p>收入:<span th:text="${#numbers.formatDecimal(balance.totalIncome, 2, 2)}">0.00</span> 元</p> <p>支出:<span th:text="${#numbers.formatDecimal(balance.totalExpense, 2, 2)}">0.00</span> 元</p> <p class="balance" th:classappend="${balance.dailyBalance >= 0 ? 'positive' : 'negative'}"> 当日余额:<span th:text="${#numbers.formatDecimal(balance.dailyBalance, 2, 2)}">0.00</span> 元 </p> </div> </div> </div>
关键注意事项
- 避免在控制器循环分组时,用同一个Model属性反复覆盖数据(这是导致仅展示最后一组的常见原因),必须传递完整的集合/Map。
- Thymeleaf遍历Map时,要遍历
entrySet(即th:each="entry : ${map}"),通过entry.key和entry.value分别获取日期和当日交易列表。
内容的提问来源于stack exchange,提问作者koderkodo
相关产品推荐
相关产品推荐

