4 Ревизии

Автор SHA1 Съобщение Дата
  bot_dev3 6a1f1d2002 feat(wm-data-engine): #71 历史数据回溯 + 报表生成(修复编译错误 + 重写测试) преди 1 седмица
  bot_dev1 a06da53b26 Merge branch 'feature/issue-71' of http://git.xayunmei.com/bot_ym/water-management-system into feature/issue-71 преди 1 седмица
  bot_dev1 032b601bf7 feat(test): #71 补充单元测试 - HistoryDataController和ReportService测试 преди 1 седмица
  bot_dev1 30c4b0e7b5 feat(data-engine): #71 历史数据回溯 + 报表生成(水量/水质/报警) преди 1 седмица

+ 90
- 137
wm-data-engine/src/main/java/com/water/data_engine/controller/HistoryDataController.java Целия файл

@@ -1,6 +1,7 @@
1 1
 package com.water.data_engine.controller;
2 2
 
3 3
 import com.water.common.core.result.R;
4
+import com.water.data_engine.entity.DataReport;
4 5
 import com.water.data_engine.service.DataStatisticsService;
5 6
 import com.water.data_engine.service.ReportService;
6 7
 import io.swagger.v3.oas.annotations.Operation;
@@ -11,9 +12,11 @@ import lombok.extern.slf4j.Slf4j;
11 12
 import org.springframework.format.annotation.DateTimeFormat;
12 13
 import org.springframework.web.bind.annotation.*;
13 14
 
14
-import java.time.LocalDateTime;
15
+import java.time.LocalDate;
16
+import java.util.LinkedHashMap;
15 17
 import java.util.List;
16 18
 import java.util.Map;
19
+import java.util.stream.Collectors;
17 20
 
18 21
 /**
19 22
  * 历史数据查询和报表生成控制器
@@ -32,285 +35,235 @@ public class HistoryDataController {
32 35
     @Operation(summary = "历史数据回溯查询")
33 36
     @GetMapping("/historical")
34 37
     public R<Map<String, Object>> queryHistoricalData(
35
-            @Parameter(description = "数据类型:water_volume/pressure/water_quality/alarm") 
38
+            @Parameter(description = "数据类型:water_volume/pressure/water_quality/alarm")
36 39
             @RequestParam(required = false) String dataType,
37
-            @Parameter(description = "区域名称") 
40
+            @Parameter(description = "区域名称")
38 41
             @RequestParam(required = false) String area,
39
-            @Parameter(description = "开始时间,格式:yyyy-MM-dd HH:mm:ss") 
42
+            @Parameter(description = "开始时间,格式:yyyy-MM-dd HH:mm:ss")
40 43
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String startTime,
41
-            @Parameter(description = "结束时间,格式:yyyy-MM-dd HH:mm:ss") 
44
+            @Parameter(description = "结束时间,格式:yyyy-MM-dd HH:mm:ss")
42 45
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String endTime) {
43
-        
46
+
44 47
         log.info("开始查询历史数据: dataType={}, area={}, startTime={}, endTime={}", dataType, area, startTime, endTime);
45
-        
46 48
         try {
47 49
             Map<String, Object> result = dataStatisticsService.queryHistoricalData(dataType, area, startTime, endTime);
48 50
             log.info("历史数据查询成功,返回 {} 条记录", result.get("totalRecords"));
49 51
             return R.ok(result);
50 52
         } catch (Exception e) {
51 53
             log.error("历史数据查询失败: {}", e.getMessage());
52
-            return R.error("历史数据查询失败: " + e.getMessage());
54
+            return R.fail("历史数据查询失败: " + e.getMessage());
53 55
         }
54 56
     }
55 57
 
56 58
     @Operation(summary = "水量汇总报表")
57 59
     @GetMapping("/report/water-volume")
58 60
     public R<Map<String, Object>> getWaterVolumeSummary(
59
-            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss") 
61
+            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss")
60 62
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String startTime,
61
-            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss") 
63
+            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss")
62 64
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String endTime) {
63
-        
65
+
64 66
         log.info("开始生成水量汇总报表: startTime={}, endTime={}", startTime, endTime);
65
-        
66 67
         try {
67 68
             Map<String, Object> summary = dataStatisticsService.getWaterVolumeSummary(startTime, endTime);
68 69
             log.info("水量汇总生成成功");
69 70
             return R.ok(summary);
70 71
         } catch (Exception e) {
71 72
             log.error("水量汇总生成失败: {}", e.getMessage());
72
-            return R.error("水量汇总生成失败: " + e.getMessage());
73
+            return R.fail("水量汇总生成失败: " + e.getMessage());
73 74
         }
74 75
     }
75 76
 
76 77
     @Operation(summary = "水质合格率报表")
77 78
     @GetMapping("/report/water-quality")
78 79
     public R<Map<String, Object>> getWaterQualityRate(
79
-            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss") 
80
+            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss")
80 81
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String startTime,
81
-            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss") 
82
+            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss")
82 83
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String endTime) {
83
-        
84
+
84 85
         log.info("开始生成水质合格率报表: startTime={}, endTime={}", startTime, endTime);
85
-        
86 86
         try {
87 87
             Map<String, Object> qualityStats = dataStatisticsService.getWaterQualityRate(startTime, endTime);
88 88
             log.info("水质合格率统计成功");
89 89
             return R.ok(qualityStats);
90 90
         } catch (Exception e) {
91 91
             log.error("水质合格率统计失败: {}", e.getMessage());
92
-            return R.error("水质合格率统计失败: " + e.getMessage());
92
+            return R.fail("水质合格率统计失败: " + e.getMessage());
93 93
         }
94 94
     }
95 95
 
96 96
     @Operation(summary = "报警统计报表")
97 97
     @GetMapping("/report/alarm-statistics")
98 98
     public R<Map<String, Object>> getAlarmStatistics(
99
-            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss") 
99
+            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss")
100 100
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String startTime,
101
-            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss") 
101
+            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss")
102 102
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String endTime) {
103
-        
103
+
104 104
         log.info("开始生成报警统计报表: startTime={}, endTime={}", startTime, endTime);
105
-        
106 105
         try {
107 106
             Map<String, Object> alarmStats = dataStatisticsService.getAlarmStatistics(startTime, endTime);
108 107
             log.info("报警统计生成成功");
109 108
             return R.ok(alarmStats);
110 109
         } catch (Exception e) {
111 110
             log.error("报警统计生成失败: {}", e.getMessage());
112
-            return R.error("报警统计生成失败: " + e.getMessage());
111
+            return R.fail("报警统计生成失败: " + e.getMessage());
113 112
         }
114 113
     }
115 114
 
116 115
     @Operation(summary = "生成水量汇总报表并保存")
117 116
     @PostMapping("/report/water-volume/generate")
118 117
     public R<Map<String, Object>> generateWaterVolumeReport(
119
-            @Parameter(description = "报表周期描述") 
118
+            @Parameter(description = "报表周期描述")
120 119
             @RequestParam String period,
121
-            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss") 
120
+            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss")
122 121
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String startTime,
123
-            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss") 
122
+            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss")
124 123
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String endTime) {
125
-        
124
+
126 125
         log.info("开始生成并保存水量汇总报表: period={}, startTime={}, endTime={}", period, startTime, endTime);
127
-        
128 126
         try {
129
-            var report = reportService.generateWaterVolumeReport(period, startTime, endTime);
130
-            
131
-            Map<String, Object> result = Map.of(
132
-                "reportNo", report.getReportNo(),
133
-                "reportType", report.getReportType(),
134
-                "period", report.getPeriod(),
135
-                "title", report.getTitle(),
136
-                "status", report.getStatus(),
137
-                "createdTime", report.getCreatedTime()
138
-            );
139
-            
140
-            log.info("水量汇总报表生成并保存成功: reportNo={}", report.getReportNo());
141
-            return R.ok(result);
127
+            DataReport report = reportService.generateWaterVolumeReport(period, startTime, endTime);
128
+            log.info("水量汇总报表生成并保存成功: reportCode={}", report.getReportCode());
129
+            return R.ok(toReportMap(report));
142 130
         } catch (Exception e) {
143 131
             log.error("水量汇总报表生成失败: {}", e.getMessage());
144
-            return R.error("水量汇总报表生成失败: " + e.getMessage());
132
+            return R.fail("水量汇总报表生成失败: " + e.getMessage());
145 133
         }
146 134
     }
147 135
 
148 136
     @Operation(summary = "生成水质合格率报表并保存")
149 137
     @PostMapping("/report/water-quality/generate")
150 138
     public R<Map<String, Object>> generateWaterQualityReport(
151
-            @Parameter(description = "报表周期描述") 
139
+            @Parameter(description = "报表周期描述")
152 140
             @RequestParam String period,
153
-            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss") 
141
+            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss")
154 142
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String startTime,
155
-            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss") 
143
+            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss")
156 144
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String endTime) {
157
-        
145
+
158 146
         log.info("开始生成并保存水质合格率报表: period={}, startTime={}, endTime={}", period, startTime, endTime);
159
-        
160 147
         try {
161
-            var report = reportService.generateWaterQualityReport(period, startTime, endTime);
162
-            
163
-            Map<String, Object> result = Map.of(
164
-                "reportNo", report.getReportNo(),
165
-                "reportType", report.getReportType(),
166
-                "period", report.getPeriod(),
167
-                "title", report.getTitle(),
168
-                "status", report.getStatus(),
169
-                "createdTime", report.getCreatedTime()
170
-            );
171
-            
172
-            log.info("水质合格率报表生成并保存成功: reportNo={}", report.getReportNo());
173
-            return R.ok(result);
148
+            DataReport report = reportService.generateWaterQualityReport(period, startTime, endTime);
149
+            log.info("水质合格率报表生成并保存成功: reportCode={}", report.getReportCode());
150
+            return R.ok(toReportMap(report));
174 151
         } catch (Exception e) {
175 152
             log.error("水质合格率报表生成失败: {}", e.getMessage());
176
-            return R.error("水质合格率报表生成失败: " + e.getMessage());
153
+            return R.fail("水质合格率报表生成失败: " + e.getMessage());
177 154
         }
178 155
     }
179 156
 
180 157
     @Operation(summary = "生成报警统计报表并保存")
181 158
     @PostMapping("/report/alarm-statistics/generate")
182 159
     public R<Map<String, Object>> generateAlarmStatisticsReport(
183
-            @Parameter(description = "报表周期描述") 
160
+            @Parameter(description = "报表周期描述")
184 161
             @RequestParam String period,
185
-            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss") 
162
+            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss")
186 163
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String startTime,
187
-            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss") 
164
+            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss")
188 165
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String endTime) {
189
-        
166
+
190 167
         log.info("开始生成并保存报警统计报表: period={}, startTime={}, endTime={}", period, startTime, endTime);
191
-        
192 168
         try {
193
-            var report = reportService.generateAlarmStatisticsReport(period, startTime, endTime);
194
-            
195
-            Map<String, Object> result = Map.of(
196
-                "reportNo", report.getReportNo(),
197
-                "reportType", report.getReportType(),
198
-                "period", report.getPeriod(),
199
-                "title", report.getTitle(),
200
-                "status", report.getStatus(),
201
-                "createdTime", report.getCreatedTime()
202
-            );
203
-            
204
-            log.info("报警统计报表生成并保存成功: reportNo={}", report.getReportNo());
205
-            return R.ok(result);
169
+            DataReport report = reportService.generateAlarmStatisticsReport(period, startTime, endTime);
170
+            log.info("报警统计报表生成并保存成功: reportCode={}", report.getReportCode());
171
+            return R.ok(toReportMap(report));
206 172
         } catch (Exception e) {
207 173
             log.error("报警统计报表生成失败: {}", e.getMessage());
208
-            return R.error("报警统计报表生成失败: " + e.getMessage());
174
+            return R.fail("报警统计报表生成失败: " + e.getMessage());
209 175
         }
210 176
     }
211 177
 
212 178
     @Operation(summary = "生成综合数据报表")
213 179
     @PostMapping("/report/comprehensive/generate")
214 180
     public R<Map<String, Object>> generateComprehensiveReport(
215
-            @Parameter(description = "报表周期描述") 
181
+            @Parameter(description = "报表周期描述")
216 182
             @RequestParam String period,
217
-            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss") 
183
+            @Parameter(description = "查询开始时间,格式:yyyy-MM-dd HH:mm:ss")
218 184
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String startTime,
219
-            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss") 
185
+            @Parameter(description = "查询结束时间,格式:yyyy-MM-dd HH:mm:ss")
220 186
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String endTime) {
221
-        
187
+
222 188
         log.info("开始生成综合数据报表: period={}, startTime={}, endTime={}", period, startTime, endTime);
223
-        
224 189
         try {
225
-            var report = reportService.generateComprehensiveReport(period, startTime, endTime);
226
-            
227
-            Map<String, Object> result = Map.of(
228
-                "reportNo", report.getReportNo(),
229
-                "reportType", report.getReportType(),
230
-                "period", report.getPeriod(),
231
-                "title", report.getTitle(),
232
-                "status", report.getStatus(),
233
-                "createdTime", report.getCreatedTime()
234
-            );
235
-            
236
-            log.info("综合数据报表生成并保存成功: reportNo={}", report.getReportNo());
237
-            return R.ok(result);
190
+            DataReport report = reportService.generateComprehensiveReport(period, startTime, endTime);
191
+            log.info("综合数据报表生成并保存成功: reportCode={}", report.getReportCode());
192
+            return R.ok(toReportMap(report));
238 193
         } catch (Exception e) {
239 194
             log.error("综合数据报表生成失败: {}", e.getMessage());
240
-            return R.error("综合数据报表生成失败: " + e.getMessage());
195
+            return R.fail("综合数据报表生成失败: " + e.getMessage());
241 196
         }
242 197
     }
243 198
 
244 199
     @Operation(summary = "获取报表列表")
245 200
     @GetMapping("/reports")
246 201
     public R<List<Map<String, Object>>> getReports(
247
-            @Parameter(description = "报表类型:water_volume/water_quality/alarm_statistics/comprehensive") 
202
+            @Parameter(description = "报表类型:water_volume/water_quality/alarm_statistics/comprehensive")
248 203
             @RequestParam(required = false) String reportType,
249
-            @Parameter(description = "报表状态:GENERATED/PUBLISHED") 
204
+            @Parameter(description = "报表状态:GENERATED/PUBLISHED")
250 205
             @RequestParam(required = false) String status) {
251
-        
206
+
252 207
         try {
253
-            var reports = reportService.listReports(reportType, status);
208
+            List<DataReport> reports = reportService.listReports(reportType, status);
254 209
             List<Map<String, Object>> result = reports.stream()
255
-                .map(report -> Map.of(
256
-                    "id", report.getId(),
257
-                    "reportNo", report.getReportNo(),
258
-                    "reportType", report.getReportType(),
259
-                    "period", report.getPeriod(),
260
-                    "title", report.getTitle(),
261
-                    "status", report.getStatus(),
262
-                    "createdTime", report.getCreatedTime(),
263
-                    "publishedTime", report.getPublishedTime()
264
-                ))
265
-                .toList();
266
-            
210
+                    .map(this::toReportMap)
211
+                    .collect(Collectors.toList());
267 212
             return R.ok(result);
268 213
         } catch (Exception e) {
269 214
             log.error("获取报表列表失败: {}", e.getMessage());
270
-            return R.error("获取报表列表失败: " + e.getMessage());
215
+            return R.fail("获取报表列表失败: " + e.getMessage());
271 216
         }
272 217
     }
273 218
 
274 219
     @Operation(summary = "获取报表详情")
275 220
     @GetMapping("/reports/{id}")
276 221
     public R<Map<String, Object>> getReportDetail(@PathVariable Long id) {
277
-        
278 222
         try {
279
-            var report = reportService.getReport(id);
223
+            DataReport report = reportService.getReport(id);
280 224
             if (report == null) {
281
-                return R.error("报表不存在");
225
+                return R.fail("报表不存在");
282 226
             }
283
-            
284
-            Map<String, Object> result = Map.of(
285
-                "id", report.getId(),
286
-                "reportNo", report.getReportNo(),
287
-                "reportType", report.getReportType(),
288
-                "period", report.getPeriod(),
289
-                "title", report.getTitle(),
290
-                "status", report.getStatus(),
291
-                "content", report.getContent(),
292
-                "createdTime", report.getCreatedTime(),
293
-                "publishedTime", report.getPublishedTime()
294
-            );
295
-            
296
-            return R.ok(result);
227
+            return R.ok(toReportMap(report));
297 228
         } catch (Exception e) {
298 229
             log.error("获取报表详情失败: {}", e.getMessage());
299
-            return R.error("获取报表详情失败: " + e.getMessage());
230
+            return R.fail("获取报表详情失败: " + e.getMessage());
300 231
         }
301 232
     }
302 233
 
303 234
     @Operation(summary = "发布报表")
304 235
     @PostMapping("/reports/{id}/publish")
305 236
     public R<String> publishReport(@PathVariable Long id) {
306
-        
307 237
         try {
308 238
             reportService.publishReport(id);
309 239
             log.info("报表发布成功: id={}", id);
310 240
             return R.ok("报表发布成功");
311 241
         } catch (Exception e) {
312 242
             log.error("报表发布失败: {}", e.getMessage());
313
-            return R.error("报表发布失败: " + e.getMessage());
243
+            return R.fail("报表发布失败: " + e.getMessage());
244
+        }
245
+    }
246
+
247
+    /**
248
+     * 将 DataReport 转为响应 Map(字段名对齐前端契约,取值来自 DataReport 真实字段)。
249
+     */
250
+    private Map<String, Object> toReportMap(DataReport report) {
251
+        Map<String, Object> m = new LinkedHashMap<>();
252
+        m.put("id", report.getId());
253
+        m.put("reportNo", report.getReportCode());
254
+        m.put("reportType", report.getReportType());
255
+        m.put("period", formatPeriod(report.getPeriodStart(), report.getPeriodEnd()));
256
+        m.put("title", report.getReportName());
257
+        m.put("status", report.getStatus());
258
+        m.put("content", report.getContent());
259
+        m.put("createdTime", report.getCreatedAt());
260
+        return m;
261
+    }
262
+
263
+    private String formatPeriod(LocalDate start, LocalDate end) {
264
+        if (start == null && end == null) {
265
+            return null;
314 266
         }
267
+        return (start != null ? start.toString() : "") + "~" + (end != null ? end.toString() : "");
315 268
     }
316
-}
269
+}

+ 4
- 4
wm-data-engine/src/main/java/com/water/data_engine/service/ReportService.java Целия файл

@@ -347,7 +347,7 @@ public class ReportService {
347 347
         }
348 348
         report.setContent(content.toString());
349 349
         report.setStatus("GENERATED");
350
-        report.setCreatedTime(LocalDateTime.now());
350
+        report.setCreatedAt(LocalDateTime.now());
351 351
 
352 352
         reportMapper.insert(report);
353 353
         return report;
@@ -427,7 +427,7 @@ public class ReportService {
427 427
         LambdaQueryWrapper<DataReport> wrapper = new LambdaQueryWrapper<>();
428 428
         if (reportType != null && !reportType.isBlank()) wrapper.eq(DataReport::getReportType, reportType);
429 429
         if (status != null && !status.isBlank()) wrapper.eq(DataReport::getStatus, status);
430
-        return reportMapper.selectList(wrapper.orderByDesc(DataReport::getCreatedTime));
430
+        return reportMapper.selectList(wrapper.orderByDesc(DataReport::getCreatedAt));
431 431
     }
432 432
 
433 433
     /**
@@ -444,7 +444,7 @@ public class ReportService {
444 444
         DataReport report = reportMapper.selectById(id);
445 445
         if (report == null) throw new RuntimeException("报表不存在");
446 446
         report.setStatus("PUBLISHED");
447
-        report.setPublishedTime(LocalDateTime.now());
447
+/* publishedTime 字段不存在,发布时间用 updatedAt 体现 */ report.setUpdatedAt(LocalDateTime.now());
448 448
         reportMapper.updateById(report);
449 449
     }
450 450
     
@@ -466,7 +466,7 @@ public class ReportService {
466 466
     }
467 467
 
468 468
     public ReportTemplate createTemplate(ReportTemplate template) {
469
-        template.setCreatedTime(LocalDateTime.now());
469
+        template.setCreatedAt(LocalDateTime.now());
470 470
         templateMapper.insert(template);
471 471
         return template;
472 472
     }

+ 122
- 238
wm-data-engine/src/test/java/com/water/data_engine/controller/HistoryDataControllerTest.java Целия файл

@@ -1,291 +1,175 @@
1 1
 package com.water.data_engine.controller;
2 2
 
3
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
4
-import com.water.data_engine.entity.HistoricalQuery;
5
-import com.water.data_engine.entity.WaterQuantity;
6
-import com.water.data_engine.entity.WaterQuality;
7
-import com.water.data_engine.service.HistoryDataService;
8
-import org.junit.jupiter.api.BeforeEach;
9
-import org.junit.jupiter.api.DisplayName;
3
+import com.water.common.core.result.R;
4
+import com.water.data_engine.entity.DataReport;
5
+import com.water.data_engine.service.DataStatisticsService;
6
+import com.water.data_engine.service.ReportService;
10 7
 import org.junit.jupiter.api.Test;
11 8
 import org.junit.jupiter.api.extension.ExtendWith;
12 9
 import org.mockito.InjectMocks;
13 10
 import org.mockito.Mock;
14 11
 import org.mockito.junit.jupiter.MockitoExtension;
15
-import org.springframework.http.HttpStatus;
16
-import org.springframework.http.ResponseEntity;
17 12
 
18
-import java.math.BigDecimal;
19
-import java.time.LocalDateTime;
13
+import java.time.LocalDate;
20 14
 import java.util.List;
15
+import java.util.Map;
21 16
 
22 17
 import static org.junit.jupiter.api.Assertions.*;
23
-import static org.mockito.ArgumentMatchers.any;
18
+import static org.mockito.ArgumentMatchers.*;
24 19
 import static org.mockito.Mockito.*;
25 20
 
26 21
 /**
27
- * 历史数据控制器测试
22
+ * HistoryDataController 单元测试(对应 Issue #71)。
23
+ *
24
+ * 覆盖历史数据回溯、水量/水质/报警报表、报表生成/列表/详情/发布等 API。
25
+ * 真正匹配 HistoryDataController 的实际 API(而非旧版脱节的 HistoryDataService)。
28 26
  */
29 27
 @ExtendWith(MockitoExtension.class)
30 28
 class HistoryDataControllerTest {
31 29
 
32
-    @Mock
33
-    private HistoryDataService historyDataService;
30
+    @Mock DataStatisticsService dataStatisticsService;
31
+    @Mock ReportService reportService;
32
+
33
+    @InjectMocks HistoryDataController controller;
34
+
35
+    private DataReport sampleReport() {
36
+        DataReport r = new DataReport();
37
+        r.setId(1L);
38
+        r.setReportCode("WATER-VOL-1");
39
+        r.setReportType("water_volume");
40
+        r.setReportName("水量汇总报表");
41
+        r.setStatus("GENERATED");
42
+        r.setPeriodStart(LocalDate.of(2026, 6, 1));
43
+        r.setPeriodEnd(LocalDate.of(2026, 6, 7));
44
+        return r;
45
+    }
34 46
 
35
-    @InjectMocks
36
-    private HistoryDataController historyDataController;
47
+    // ---------- 历史数据回溯 ----------
37 48
 
38
-    @BeforeEach
39
-    void setUp() {
40
-        // 重置mock
41
-        reset(historyDataService);
49
+    @Test
50
+    void queryHistoricalData_returnsOk() {
51
+        when(dataStatisticsService.queryHistoricalData(eq("water_volume"), any(), any(), any()))
52
+                .thenReturn(Map.of("totalRecords", 100, "records", List.of()));
53
+        R<Map<String, Object>> r = controller.queryHistoricalData("water_volume", null, null, null);
54
+        assertEquals(200, r.getCode());
55
+        assertEquals(100, r.getData().get("totalRecords"));
42 56
     }
43 57
 
44 58
     @Test
45
-    @DisplayName("查询水量历史数据-正常响应")
46
-    void testQueryQuantityHistory_Success() {
47
-        // Given
48
-        HistoricalQuery query = new HistoricalQuery();
49
-        query.setArea("城东");
50
-        query.setStartTime(LocalDateTime.of(2026, 1, 1, 0, 0));
51
-        query.setEndTime(LocalDateTime.of(2026, 1, 31, 23, 59));
52
-        query.setPageNum(1);
53
-        query.setPageSize(10);
54
-
55
-        WaterQuantity wq = new WaterQuantity();
56
-        wq.setId(1L);
57
-        wq.setMonitorPoint("城东水厂出口");
58
-        wq.setArea("城东");
59
-        wq.setFlowRate(new BigDecimal("120.5"));
60
-        wq.setPressure(new BigDecimal("0.35"));
61
-        wq.setCollectTime(LocalDateTime.of(2026, 1, 15, 10, 0));
62
-
63
-        Page<WaterQuantity> mockPage = new Page<>(1, 10);
64
-        mockPage.setRecords(List.of(wq));
65
-        mockPage.setTotal(1);
66
-
67
-        when(historyDataService.queryQuantityHistory(query)).thenReturn(mockPage);
59
+    void queryHistoricalData_exceptionReturnsFail() {
60
+        when(dataStatisticsService.queryHistoricalData(any(), any(), any(), any()))
61
+                .thenThrow(new RuntimeException("db error"));
62
+        R<Map<String, Object>> r = controller.queryHistoricalData(null, null, null, null);
63
+        assertEquals(500, r.getCode());
64
+        assertTrue(r.getMessage().contains("历史数据查询失败"));
65
+    }
68 66
 
69
-        // When
70
-        ResponseEntity<Page<WaterQuantity>> response = historyDataController.queryQuantityHistory(query);
67
+    // ---------- 报表查询(get)----------
71 68
 
72
-        // Then
73
-        assertNotNull(response);
74
-        assertEquals(HttpStatus.OK, response.getStatusCode());
75
-        assertNotNull(response.getBody());
76
-        assertEquals(1, response.getBody().getRecords().size());
77
-        assertEquals("城东", response.getBody().getRecords().get(0).getArea());
78
-        verify(historyDataService, times(1)).queryQuantityHistory(query);
69
+    @Test
70
+    void getWaterVolumeSummary_delegates() {
71
+        when(dataStatisticsService.getWaterVolumeSummary(any(), any()))
72
+                .thenReturn(Map.of("totalSupply", 5000.0));
73
+        R<Map<String, Object>> r = controller.getWaterVolumeSummary(null, null);
74
+        assertEquals(200, r.getCode());
75
+        assertEquals(5000.0, r.getData().get("totalSupply"));
79 76
     }
80 77
 
81 78
     @Test
82
-    @DisplayName("查询水量历史数据-分页参数为空")
83
-    void testQueryQuantityHistory_EmptyParams() {
84
-        // Given
85
-        HistoricalQuery query = new HistoricalQuery();
86
-        query.setPageNum(1);
87
-        query.setPageSize(20);
88
-
89
-        Page<WaterQuantity> mockPage = new Page<>(1, 20);
90
-        mockPage.setRecords(List.of());
91
-        mockPage.setTotal(0);
92
-
93
-        when(historyDataService.queryQuantityHistory(query)).thenReturn(mockPage);
94
-
95
-        // When
96
-        ResponseEntity<Page<WaterQuantity>> response = historyDataController.queryQuantityHistory(query);
97
-
98
-        // Then
99
-        assertNotNull(response);
100
-        assertEquals(HttpStatus.OK, response.getStatusCode());
101
-        assertNotNull(response.getBody());
102
-        assertEquals(0, response.getBody().getRecords().size());
103
-        verify(historyDataService, times(1)).queryQuantityHistory(query);
79
+    void getWaterQualityRate_delegates() {
80
+        when(dataStatisticsService.getWaterQualityRate(any(), any()))
81
+                .thenReturn(Map.of("passRate", 98.5));
82
+        R<Map<String, Object>> r = controller.getWaterQualityRate(null, null);
83
+        assertEquals(200, r.getCode());
104 84
     }
105 85
 
106 86
     @Test
107
-    @DisplayName("查询水质历史数据-正常响应")
108
-    void testQueryQualityHistory_Success() {
109
-        // Given
110
-        HistoricalQuery query = new HistoricalQuery();
111
-        query.setPointCode("WQ001");
112
-        query.setStartTime(LocalDateTime.of(2026, 1, 1, 0, 0));
113
-        query.setEndTime(LocalDateTime.of(2026, 1, 31, 23, 59));
114
-
115
-        WaterQuality wq = new WaterQuality();
116
-        wq.setId(1L);
117
-        wq.setMonitorPoint("水厂出口");
118
-        wq.setPointCode("WQ001");
119
-        wq.setPh(new BigDecimal("7.2"));
120
-        wq.setTurbidity(new BigDecimal("0.5"));
121
-        wq.setIsQualified(1);
122
-        wq.setCollectTime(LocalDateTime.of(2026, 1, 10, 8, 0));
123
-
124
-        Page<WaterQuality> mockPage = new Page<>(1, 20);
125
-        mockPage.setRecords(List.of(wq));
126
-        mockPage.setTotal(1);
127
-
128
-        when(historyDataService.queryQualityHistory(query)).thenReturn(mockPage);
87
+    void getAlarmStatistics_delegates() {
88
+        when(dataStatisticsService.getAlarmStatistics(any(), any()))
89
+                .thenReturn(Map.of("totalAlarms", 12));
90
+        R<Map<String, Object>> r = controller.getAlarmStatistics(null, null);
91
+        assertEquals(200, r.getCode());
92
+    }
129 93
 
130
-        // When
131
-        ResponseEntity<Page<WaterQuality>> response = historyDataController.queryQualityHistory(query);
94
+    // ---------- 报表生成(generate,校验 toReportMap 字段映射)----------
132 95
 
133
-        // Then
134
-        assertNotNull(response);
135
-        assertEquals(HttpStatus.OK, response.getStatusCode());
136
-        assertNotNull(response.getBody());
137
-        assertEquals(1, response.getBody().getRecords().size());
138
-        assertEquals("WQ001", response.getBody().getRecords().get(0).getPointCode());
139
-        verify(historyDataService, times(1)).queryQualityHistory(query);
96
+    @Test
97
+    void generateWaterVolumeReport_mapsReportFields() {
98
+        when(reportService.generateWaterVolumeReport(any(), any(), any())).thenReturn(sampleReport());
99
+        R<Map<String, Object>> r = controller.generateWaterVolumeReport("周报", null, null);
100
+        assertEquals(200, r.getCode());
101
+        Map<String, Object> data = r.getData();
102
+        // 验证字段映射:reportNo <- reportCode, title <- reportName
103
+        assertEquals("WATER-VOL-1", data.get("reportNo"));
104
+        assertEquals("water_volume", data.get("reportType"));
105
+        assertEquals("水量汇总报表", data.get("title"));
106
+        assertEquals("GENERATED", data.get("status"));
107
+        assertEquals("2026-06-01~2026-06-07", data.get("period"));
140 108
     }
141 109
 
142 110
     @Test
143
-    @DisplayName("查询水质历史数据-无结果返回空列表")
144
-    void testQueryQualityHistory_EmptyResult() {
145
-        // Given
146
-        HistoricalQuery query = new HistoricalQuery();
147
-        query.setStartTime(LocalDateTime.of(2026, 1, 1, 0, 0));
148
-        query.setEndTime(LocalDateTime.of(2026, 1, 31, 23, 59));
149
-
150
-        Page<WaterQuality> mockPage = new Page<>(1, 20);
151
-        mockPage.setRecords(List.of());
152
-        mockPage.setTotal(0);
153
-
154
-        when(historyDataService.queryQualityHistory(query)).thenReturn(mockPage);
155
-
156
-        // When
157
-        ResponseEntity<Page<WaterQuality>> response = historyDataController.queryQualityHistory(query);
158
-
159
-        // Then
160
-        assertNotNull(response);
161
-        assertEquals(HttpStatus.OK, response.getStatusCode());
162
-        assertNotNull(response.getBody());
163
-        assertEquals(0, response.getBody().getRecords().size());
164
-        verify(historyDataService, times(1)).queryQualityHistory(query);
111
+    void generateWaterQualityReport_mapsReportFields() {
112
+        when(reportService.generateWaterQualityReport(any(), any(), any())).thenReturn(sampleReport());
113
+        R<Map<String, Object>> r = controller.generateWaterQualityReport("周报", null, null);
114
+        assertEquals(200, r.getCode());
115
+        assertEquals("WATER-VOL-1", r.getData().get("reportNo"));
165 116
     }
166 117
 
167 118
     @Test
168
-    @DisplayName("区域水量聚合统计-正常响应")
169
-    void testAggregateQuantityByArea_Success() {
170
-        // Given
171
-        LocalDateTime start = LocalDateTime.of(2026, 1, 1, 0, 0);
172
-        LocalDateTime end = LocalDateTime.of(2026, 1, 31, 23, 59);
173
-
174
-        // Mock返回聚合数据
175
-        when(historyDataService.aggregateQuantityByArea(start, end, null))
176
-            .thenReturn(List.of(
177
-                Map.of("area", "城东", "avg_flow_rate", new BigDecimal("120.5"), "record_count", 1000L),
178
-                Map.of("area", "城西", "avg_flow_rate", new BigDecimal("95.8"), "record_count", 850L)
179
-            ));
180
-
181
-        // When
182
-        ResponseEntity<List<Map<String, Object>>> response = historyDataController.aggregateQuantityByArea(start, end, null);
183
-
184
-        // Then
185
-        assertNotNull(response);
186
-        assertEquals(HttpStatus.OK, response.getStatusCode());
187
-        assertNotNull(response.getBody());
188
-        assertEquals(2, response.getBody().size());
189
-        assertEquals("城东", response.getBody().get(0).get("area"));
190
-        assertEquals(1000L, response.getBody().get(0).get("record_count"));
191
-        verify(historyDataService, times(1)).aggregateQuantityByArea(start, end, null);
119
+    void generateAlarmStatisticsReport_mapsReportFields() {
120
+        when(reportService.generateAlarmStatisticsReport(any(), any(), any())).thenReturn(sampleReport());
121
+        R<Map<String, Object>> r = controller.generateAlarmStatisticsReport("周报", null, null);
122
+        assertEquals(200, r.getCode());
192 123
     }
193 124
 
194 125
     @Test
195
-    @DisplayName("区域水量聚合统计-指定区域")
196
-    void testAggregateQuantityByArea_WithSpecificArea() {
197
-        // Given
198
-        LocalDateTime start = LocalDateTime.of(2026, 1, 1, 0, 0);
199
-        LocalDateTime end = LocalDateTime.of(2026, 1, 31, 23, 59);
200
-        String area = "城东";
201
-
202
-        when(historyDataService.aggregateQuantityByArea(start, end, area))
203
-            .thenReturn(List.of(
204
-                Map.of("area", "城东", "avg_flow_rate", new BigDecimal("120.5"), "record_count", 1000L)
205
-            ));
206
-
207
-        // When
208
-        ResponseEntity<List<Map<String, Object>>> response = historyDataController.aggregateQuantityByArea(start, end, area);
209
-
210
-        // Then
211
-        assertNotNull(response);
212
-        assertEquals(HttpStatus.OK, response.getStatusCode());
213
-        assertNotNull(response.getBody());
214
-        assertEquals(1, response.getBody().size());
215
-        assertEquals("城东", response.getBody().get(0).get("area"));
216
-        verify(historyDataService, times(1)).aggregateQuantityByArea(start, end, area);
126
+    void generateComprehensiveReport_mapsReportFields() {
127
+        when(reportService.generateComprehensiveReport(any(), any(), any())).thenReturn(sampleReport());
128
+        R<Map<String, Object>> r = controller.generateComprehensiveReport("周报", null, null);
129
+        assertEquals(200, r.getCode());
217 130
     }
218 131
 
219 132
     @Test
220
-    @DisplayName("导出水量数据-正常响应")
221
-    void testExportQuantityData_Success() {
222
-        // Given
223
-        HistoricalQuery query = new HistoricalQuery();
224
-        query.setDataType("quantity");
225
-        query.setArea("城东");
226
-        query.setStartTime(LocalDateTime.of(2026, 1, 1, 0, 0));
227
-        query.setEndTime(LocalDateTime.of(2026, 1, 31, 23, 59));
228
-
229
-        // Mock返回导出结构
230
-        Map<String, Object> exportData = Map.of(
231
-            "dataType", "quantity",
232
-            "area", "城东",
233
-            "headers", List.of("监测点", "区域", "流量(m³/h)", "压力(MPa)", "采集时间"),
234
-            "data", List.of(
235
-                List.of("城东水厂", "城东", "120.5", "0.35", "2026-01-15 10:00:00")
236
-            ),
237
-            "fileName", "水量数据_20260101_20260131.xlsx"
238
-        );
239
-
240
-        when(historyDataService.queryForExport(query)).thenReturn(exportData);
133
+    void generateReport_exceptionReturnsFail() {
134
+        when(reportService.generateWaterVolumeReport(any(), any(), any()))
135
+                .thenThrow(new RuntimeException("gen error"));
136
+        R<Map<String, Object>> r = controller.generateWaterVolumeReport("周报", null, null);
137
+        assertEquals(500, r.getCode());
138
+        assertTrue(r.getMessage().contains("水量汇总报表生成失败"));
139
+    }
241 140
 
242
-        // When
243
-        ResponseEntity<Map<String, Object>> response = historyDataController.exportQuantityData(query);
141
+    // ---------- 报表列表/详情/发布 ----------
244 142
 
245
-        // Then
246
-        assertNotNull(response);
247
-        assertEquals(HttpStatus.OK, response.getStatusCode());
248
-        assertNotNull(response.getBody());
249
-        assertEquals("quantity", response.getBody().get("dataType"));
250
-        assertEquals("城东", response.getBody().get("area"));
251
-        assertNotNull(response.getBody().get("headers"));
252
-        assertNotNull(response.getBody().get("data"));
253
-        assertNotNull(response.getBody().get("fileName"));
254
-        verify(historyDataService, times(1)).queryForExport(query);
143
+    @Test
144
+    void getReports_mapsList() {
145
+        when(reportService.listReports(any(), any())).thenReturn(List.of(sampleReport(), sampleReport()));
146
+        R<List<Map<String, Object>>> r = controller.getReports("water_volume", null);
147
+        assertEquals(200, r.getCode());
148
+        assertEquals(2, r.getData().size());
149
+        assertEquals("WATER-VOL-1", r.getData().get(0).get("reportNo"));
255 150
     }
256 151
 
257 152
     @Test
258
-    @DisplayName("导出水质数据-正常响应")
259
-    void testExportQualityData_Success() {
260
-        // Given
261
-        HistoricalQuery query = new HistoricalQuery();
262
-        query.setDataType("quality");
263
-        query.setStartTime(LocalDateTime.of(2026, 1, 1, 0, 0));
264
-        query.setEndTime(LocalDateTime.of(2026, 1, 31, 23, 59));
265
-
266
-        // Mock返回导出结构
267
-        Map<String, Object> exportData = Map.of(
268
-            "dataType", "quality",
269
-            "headers", List.of("监测点", "pH值", "浊度(NTU)", "是否合格", "采集时间"),
270
-            "data", List.of(
271
-                List.of("水厂出口", "7.2", "0.5", "合格", "2026-01-15 10:00:00")
272
-            ),
273
-            "fileName", "水质数据_20260101_20260131.xlsx"
274
-        );
275
-
276
-        when(historyDataService.queryForExport(query)).thenReturn(exportData);
153
+    void getReportDetail_found() {
154
+        when(reportService.getReport(1L)).thenReturn(sampleReport());
155
+        R<Map<String, Object>> r = controller.getReportDetail(1L);
156
+        assertEquals(200, r.getCode());
157
+        assertEquals("水量汇总报表", r.getData().get("title"));
158
+    }
277 159
 
278
-        // When
279
-        ResponseEntity<Map<String, Object>> response = historyDataController.exportQualityData(query);
160
+    @Test
161
+    void getReportDetail_notFound() {
162
+        when(reportService.getReport(999L)).thenReturn(null);
163
+        R<Map<String, Object>> r = controller.getReportDetail(999L);
164
+        assertEquals(500, r.getCode());
165
+        assertTrue(r.getMessage().contains("报表不存在"));
166
+    }
280 167
 
281
-        // Then
282
-        assertNotNull(response);
283
-        assertEquals(HttpStatus.OK, response.getStatusCode());
284
-        assertNotNull(response.getBody());
285
-        assertEquals("quality", response.getBody().get("dataType"));
286
-        assertNotNull(response.getBody().get("headers"));
287
-        assertNotNull(response.getBody().get("data"));
288
-        assertNotNull(response.getBody().get("fileName"));
289
-        verify(historyDataService, times(1)).queryForExport(query);
168
+    @Test
169
+    void publishReport_invokesService() {
170
+        doNothing().when(reportService).publishReport(1L);
171
+        R<String> r = controller.publishReport(1L);
172
+        assertEquals(200, r.getCode());
173
+        verify(reportService).publishReport(1L);
290 174
     }
291
-}
175
+}

+ 0
- 396
wm-data-engine/src/test/java/com/water/data_engine/controller/ReportControllerTest.java Целия файл

@@ -1,396 +0,0 @@
1
-package com.water.data_engine.controller;
2
-
3
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
4
-import com.water.data_engine.entity.DataReport;
5
-import com.water.data_engine.entity.ReportTemplate;
6
-import com.water.data_engine.service.ReportService;
7
-import org.junit.jupiter.api.BeforeEach;
8
-import org.junit.jupiter.api.DisplayName;
9
-import org.junit.jupiter.api.Test;
10
-import org.junit.jupiter.api.extension.ExtendWith;
11
-import org.mockito.InjectMocks;
12
-import org.mockito.Mock;
13
-import org.mockito.junit.jupiter.MockitoExtension;
14
-import org.springframework.http.HttpStatus;
15
-import org.springframework.http.ResponseEntity;
16
-
17
-import java.time.LocalDate;
18
-import java.util.List;
19
-import java.util.Map;
20
-
21
-import static org.junit.jupiter.api.Assertions.*;
22
-import static org.mockito.ArgumentMatchers.any;
23
-import static org.mockito.Mockito.*;
24
-
25
-/**
26
- * 报表控制器测试
27
- */
28
-@ExtendWith(MockitoExtension.class)
29
-class ReportControllerTest {
30
-
31
-    @Mock
32
-    private ReportService reportService;
33
-
34
-    @InjectMocks
35
-    private ReportController reportController;
36
-
37
-    @BeforeEach
38
-    void setUp() {
39
-        // 重置mock
40
-        reset(reportService);
41
-    }
42
-
43
-    @Test
44
-    @DisplayName("生成日报-水量-正常响应")
45
-    void testGenerateDailyReport_Quantity_Success() {
46
-        // Given
47
-        DataReport mockReport = new DataReport();
48
-        mockReport.setId(1L);
49
-        mockReport.setReportCode("RPT-20260101-001");
50
-        mockReport.setReportType("daily");
51
-        mockReport.setDataType("quantity");
52
-        mockReport.setStatus("generated");
53
-        mockReport.setPeriodStart(LocalDate.of(2026, 1, 1));
54
-        mockReport.setPeriodEnd(LocalDate.of(2026, 1, 1));
55
-
56
-        when(reportService.generateReport("daily", "quantity", null)).thenReturn(mockReport);
57
-
58
-        // When
59
-        ResponseEntity<DataReport> response = reportController.generateDailyReport("quantity");
60
-
61
-        // Then
62
-        assertNotNull(response);
63
-        assertEquals(HttpStatus.OK, response.getStatusCode());
64
-        assertNotNull(response.getBody());
65
-        assertEquals("daily", response.getBody().getReportType());
66
-        assertEquals("quantity", response.getBody().getDataType());
67
-        assertEquals("generated", response.getBody().getStatus());
68
-        assertTrue(response.getBody().getReportCode().startsWith("RPT-"));
69
-        verify(reportService, times(1)).generateReport("daily", "quantity", null);
70
-    }
71
-
72
-    @Test
73
-    @DisplayName("生成日报-水质-正常响应")
74
-    void testGenerateDailyReport_Quality_Success() {
75
-        // Given
76
-        DataReport mockReport = new DataReport();
77
-        mockReport.setId(2L);
78
-        mockReport.setReportCode("RPT-20260101-002");
79
-        mockReport.setReportType("daily");
80
-        mockReport.setDataType("quality");
81
-        mockReport.setStatus("generated");
82
-
83
-        when(reportService.generateReport("daily", "quality", null)).thenReturn(mockReport);
84
-
85
-        // When
86
-        ResponseEntity<DataReport> response = reportController.generateDailyReport("quality");
87
-
88
-        // Then
89
-        assertNotNull(response);
90
-        assertEquals(HttpStatus.OK, response.getStatusCode());
91
-        assertNotNull(response.getBody());
92
-        assertEquals("quality", response.getBody().getDataType());
93
-        verify(reportService, times(1)).generateReport("daily", "quality", null);
94
-    }
95
-
96
-    @Test
97
-    @DisplayName("生成日报-无效数据类型")
98
-    void testGenerateDailyReport_InvalidDataType() {
99
-        // Given
100
-        when(reportService.generateReport("daily", "invalid", null))
101
-            .thenThrow(new IllegalArgumentException("不支持的数据类型: invalid"));
102
-
103
-        // When & Then
104
-        assertThrows(IllegalArgumentException.class, () ->
105
-            reportController.generateDailyReport("invalid")
106
-        );
107
-
108
-        verify(reportService, times(1)).generateReport("daily", "invalid", null);
109
-    }
110
-
111
-    @Test
112
-    @DisplayName("生成月报-水量-指定时间段-正常响应")
113
-    void testGenerateMonthlyReport_Quantity_WithPeriod_Success() {
114
-        // Given
115
-        LocalDate start = LocalDate.of(2026, 1, 1);
116
-        LocalDate end = LocalDate.of(2026, 1, 31);
117
-        String area = "城东";
118
-
119
-        DataReport mockReport = new DataReport();
120
-        mockReport.setId(3L);
121
-        mockReport.setReportCode("RPT-M202601-001");
122
-        mockReport.setReportType("monthly");
123
-        mockReport.setDataType("quantity");
124
-        mockReport.setArea(area);
125
-        mockReport.setPeriodStart(start);
126
-        mockReport.setPeriodEnd(end);
127
-        mockReport.setStatus("generated");
128
-
129
-        when(reportService.generateReport("monthly", "quantity", area, start, end)).thenReturn(mockReport);
130
-
131
-        // When
132
-        ResponseEntity<DataReport> response = reportController.generateMonthlyReport("quantity", area, start, end);
133
-
134
-        // Then
135
-        assertNotNull(response);
136
-        assertEquals(HttpStatus.OK, response.getStatusCode());
137
-        assertNotNull(response.getBody());
138
-        assertEquals("monthly", response.getBody().getReportType());
139
-        assertEquals("quantity", response.getBody().getDataType());
140
-        assertEquals(area, response.getBody().getArea());
141
-        assertEquals(start, response.getBody().getPeriodStart());
142
-        assertEquals(end, response.getBody().getPeriodEnd());
143
-        verify(reportService, times(1)).generateReport("monthly", "quantity", area, start, end);
144
-    }
145
-
146
-    @Test
147
-    @DisplayName("生成月报-水质-无时间段-默认本月")
148
-    void testGenerateMonthlyReport_Quality_NoPeriod() {
149
-        // Given
150
-        LocalDate currentMonthStart = LocalDate.now().withDayOfMonth(1);
151
-        LocalDate currentMonthEnd = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth());
152
-
153
-        DataReport mockReport = new DataReport();
154
-        mockReport.setId(4L);
155
-        mockReport.setReportCode("RPT-M" + LocalDate.now().getYear() + String.format("%02d", LocalDate.now().getMonthValue()) + "-001");
156
-        mockReport.setReportType("monthly");
157
-        mockReport.setDataType("quality");
158
-        mockReport.setPeriodStart(currentMonthStart);
159
-        mockReport.setPeriodEnd(currentMonthEnd);
160
-        mockReport.setStatus("generated");
161
-
162
-        when(reportService.generateReport("monthly", "quality", null, null, null)).thenReturn(mockReport);
163
-
164
-        // When
165
-        ResponseEntity<DataReport> response = reportController.generateMonthlyReport("quality", null, null, null);
166
-
167
-        // Then
168
-        assertNotNull(response);
169
-        assertEquals(HttpStatus.OK, response.getStatusCode());
170
-        assertNotNull(response.getBody());
171
-        assertEquals("monthly", response.getBody().getReportType());
172
-        assertEquals("quality", response.getBody().getDataType());
173
-        verify(reportService, times(1)).generateReport("monthly", "quality", null, null, null);
174
-    }
175
-
176
-    @Test
177
-    @DisplayName("查询报表列表-分页-正常响应")
178
-    void testListReports_Success() {
179
-        // Given
180
-        Page<DataReport> mockPage = new Page<>(1, 10);
181
-        mockPage.setRecords(List.of(
182
-            createSampleReport(1L, "RPT-20260101-001"),
183
-            createSampleReport(2L, "RPT-20260101-002")
184
-        ));
185
-        mockPage.setTotal(2);
186
-        mockPage.setCurrent(1);
187
-        mockPage.setSize(10);
188
-
189
-        when(reportService.listReports(1, 10, "daily", null)).thenReturn(mockPage);
190
-
191
-        // When
192
-        ResponseEntity<Page<DataReport>> response = reportController.listReports(1, 10, "daily", null);
193
-
194
-        // Then
195
-        assertNotNull(response);
196
-        assertEquals(HttpStatus.OK, response.getStatusCode());
197
-        assertNotNull(response.getBody());
198
-        assertEquals(2, response.getBody().getRecords().size());
199
-        assertEquals(2L, response.getBody().getTotal());
200
-        assertEquals(1, response.getBody().getCurrent());
201
-        assertEquals(10, response.getBody().getSize());
202
-        verify(reportService, times(1)).listReports(1, 10, "daily", null);
203
-    }
204
-
205
-    @Test
206
-    @DisplayName("查询报表列表-按类型过滤")
207
-    void testListReports_ByType() {
208
-        // Given
209
-        Page<DataReport> mockPage = new Page<>(1, 20);
210
-        mockPage.setRecords(List.of(createSampleReport(3L, "RPT-M202601-001")));
211
-        mockPage.setTotal(1);
212
-
213
-        when(reportService.listReports(1, 20, "monthly", "quality")).thenReturn(mockPage);
214
-
215
-        // When
216
-        ResponseEntity<Page<DataReport>> response = reportController.listReports(1, 20, "monthly", "quality");
217
-
218
-        // Then
219
-        assertNotNull(response);
220
-        assertEquals(HttpStatus.OK, response.getStatusCode());
221
-        assertNotNull(response.getBody());
222
-        assertEquals(1, response.getBody().getRecords().size());
223
-        assertEquals("monthly", response.getBody().getRecords().get(0).getReportType());
224
-        verify(reportService, times(1)).listReports(1, 20, "monthly", "quality");
225
-    }
226
-
227
-    @Test
228
-    @DisplayName("获取报表详情-正常响应")
229
-    void testGetReport_Success() {
230
-        // Given
231
-        Long reportId = 1L;
232
-        DataReport mockReport = createSampleReport(reportId, "RPT-20260101-001");
233
-
234
-        when(reportService.getReport(reportId)).thenReturn(mockReport);
235
-
236
-        // When
237
-        ResponseEntity<DataReport> response = reportController.getReport(reportId);
238
-
239
-        // Then
240
-        assertNotNull(response);
241
-        assertEquals(HttpStatus.OK, response.getStatusCode());
242
-        assertNotNull(response.getBody());
243
-        assertEquals(reportId, response.getBody().getId());
244
-        assertEquals("RPT-20260101-001", response.getBody().getReportCode());
245
-        verify(reportService, times(1)).getReport(reportId);
246
-    }
247
-
248
-    @Test
249
-    @DisplayName("获取报表详情-报告不存在")
250
-    void testGetReport_NotFound() {
251
-        // Given
252
-        Long reportId = 999L;
253
-        when(reportService.getReport(reportId)).thenReturn(null);
254
-
255
-        // When & Then
256
-        assertThrows(RuntimeException.class, () ->
257
-            reportController.getReport(reportId)
258
-        );
259
-
260
-        verify(reportService, times(1)).getReport(reportId);
261
-    }
262
-
263
-    @Test
264
-    @DisplayName("发布报表-正常响应")
265
-    void testPublishReport_Success() {
266
-        // Given
267
-        Long reportId = 1L;
268
-        when(reportService.publishReport(reportId)).thenReturn(true);
269
-
270
-        // When
271
-        ResponseEntity<Map<String, Object>> response = reportController.publishReport(reportId);
272
-
273
-        // Then
274
-        assertNotNull(response);
275
-        assertEquals(HttpStatus.OK, response.getStatusCode());
276
-        assertNotNull(response.getBody());
277
-        assertTrue((Boolean) response.getBody().get("success"));
278
-        assertEquals("报表发布成功", response.getBody().get("message"));
279
-        assertEquals(reportId, response.getBody().get("reportId"));
280
-        verify(reportService, times(1)).publishReport(reportId);
281
-    }
282
-
283
-    @Test
284
-    @DisplayName("发布报表-发布失败")
285
-    void testPublishReport_Failure() {
286
-        // Given
287
-        Long reportId = 1L;
288
-        when(reportService.publishReport(reportId)).thenReturn(false);
289
-
290
-        // When
291
-        ResponseEntity<Map<String, Object>> response = reportController.publishReport(reportId);
292
-
293
-        // Then
294
-        assertNotNull(response);
295
-        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
296
-        assertNotNull(response.getBody());
297
-        assertFalse((Boolean) response.getBody().get("success"));
298
-        assertEquals("报表发布失败", response.getBody().get("message"));
299
-        assertEquals(reportId, response.getBody().get("reportId"));
300
-        verify(reportService, times(1)).publishReport(reportId);
301
-    }
302
-
303
-    @Test
304
-    @DisplayName("获取模板列表-正常响应")
305
-    void testGetTemplates_Success() {
306
-        // Given
307
-        List<ReportTemplate> mockTemplates = List.of(
308
-            createSampleTemplate(1L, "水量日报模板", "TPL-QTY-DAY", "daily", "quantity"),
309
-            createSampleTemplate(2L, "水质月报模板", "TPL-QTY-MONTH", "monthly", "quality")
310
-        );
311
-
312
-        when(reportService.getTemplates("daily", "quantity")).thenReturn(mockTemplates);
313
-
314
-        // When
315
-        ResponseEntity<List<ReportTemplate>> response = reportController.getTemplates("daily", "quantity");
316
-
317
-        // Then
318
-        assertNotNull(response);
319
-        assertEquals(HttpStatus.OK, response.getStatusCode());
320
-        assertNotNull(response.getBody());
321
-        assertEquals(2, response.getBody().size());
322
-        assertEquals("TPL-QTY-DAY", response.getBody().get(0).getTemplateCode());
323
-        assertEquals("TPL-QTY-MONTH", response.getBody().get(1).getTemplateCode());
324
-        verify(reportService, times(1)).getTemplates("daily", "quantity");
325
-    }
326
-
327
-    @Test
328
-    @DisplayName("获取最近报表-正常响应")
329
-    void testGetRecentReports_Success() {
330
-        // Given
331
-        List<DataReport> mockRecent = List.of(
332
-            createSampleReport(1L, "RPT-20260101-001"),
333
-            createSampleReport(2L, "RPT-20260102-001")
334
-        );
335
-
336
-        when(reportService.findRecentReports(null, null, 5)).thenReturn(mockRecent);
337
-
338
-        // When
339
-        ResponseEntity<List<DataReport>> response = reportController.getRecentReports(5);
340
-
341
-        // Then
342
-        assertNotNull(response);
343
-        assertEquals(HttpStatus.OK, response.getStatusCode());
344
-        assertNotNull(response.getBody());
345
-        assertEquals(2, response.getBody().size());
346
-        assertEquals("RPT-20260101-001", response.getBody().get(0).getReportCode());
347
-        verify(reportService, times(1)).findRecentReports(null, null, 5);
348
-    }
349
-
350
-    @Test
351
-    @DisplayName("获取报表统计-正常响应")
352
-    void testGetReportStatistics_Success() {
353
-        // Given
354
-        List<Map<String, Object>> mockStats = List.of(
355
-            Map.of("type", "daily", "quantity", 10, "quality", 5),
356
-            Map.of("type", "monthly", "quantity", 3, "quality", 8)
357
-        );
358
-
359
-        when(reportService.countReportsByType()).thenReturn(mockStats);
360
-
361
-        // When
362
-        ResponseEntity<List<Map<String, Object>>> response = reportController.getReportStatistics();
363
-
364
-        // Then
365
-        assertNotNull(response);
366
-        assertEquals(HttpStatus.OK, response.getStatusCode());
367
-        assertNotNull(response.getBody());
368
-        assertEquals(2, response.getBody().size());
369
-        assertEquals("daily", response.getBody().get(0).get("type"));
370
-        verify(reportService, times(1)).countReportsByType();
371
-    }
372
-
373
-    // 辅助方法:创建示例报表
374
-    private DataReport createSampleReport(Long id, String reportCode) {
375
-        DataReport report = new DataReport();
376
-        report.setId(id);
377
-        report.setReportCode(reportCode);
378
-        report.setReportType("daily");
379
-        report.setDataType("quantity");
380
-        report.setStatus("generated");
381
-        report.setCreatedAt(java.time.LocalDateTime.now());
382
-        return report;
383
-    }
384
-
385
-    // 辅助方法:创建示例模板
386
-    private ReportTemplate createSampleTemplate(Long id, String name, String code, String type, String dataType) {
387
-        ReportTemplate template = new ReportTemplate();
388
-        template.setId(id);
389
-        template.setTemplateName(name);
390
-        template.setTemplateCode(code);
391
-        template.setReportType(type);
392
-        template.setDataType(dataType);
393
-        template.setCreatedAt(java.time.LocalDateTime.now());
394
-        return template;
395
-    }
396
-}

+ 124
- 114
wm-data-engine/src/test/java/com/water/data_engine/service/ReportServiceTest.java Целия файл

@@ -1,158 +1,168 @@
1 1
 package com.water.data_engine.service;
2 2
 
3
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
5
-import com.water.data_engine.entity.*;
6
-import com.water.data_engine.mapper.*;
7
-import org.junit.jupiter.api.BeforeEach;
8
-import org.junit.jupiter.api.DisplayName;
3
+import com.water.data_engine.entity.DataReport;
4
+import com.water.data_engine.entity.ReportTemplate;
5
+import com.water.data_engine.mapper.DataReportMapper;
6
+import com.water.data_engine.mapper.ReportTemplateMapper;
9 7
 import org.junit.jupiter.api.Test;
10 8
 import org.junit.jupiter.api.extension.ExtendWith;
9
+import org.mockito.InjectMocks;
11 10
 import org.mockito.Mock;
12 11
 import org.mockito.junit.jupiter.MockitoExtension;
13 12
 
14
-import java.time.LocalDate;
15
-import java.util.*;
13
+import java.util.List;
14
+import java.util.Map;
16 15
 
17 16
 import static org.junit.jupiter.api.Assertions.*;
18
-import static org.mockito.ArgumentMatchers.*;
17
+import static org.mockito.ArgumentMatchers.any;
19 18
 import static org.mockito.Mockito.*;
20 19
 
21 20
 /**
22
- * 报表服务测试
21
+ * ReportService 单元测试(对应 Issue #71)。
22
+ *
23
+ * 覆盖水量/水质/报警/综合报表生成、报表列表/详情/发布、模板管理。
24
+ * 真正匹配 ReportService 实际 API(mock Mapper + DataStatisticsService),
25
+ * 替代 master 上引用不存在方法/类型的旧版测试。
23 26
  */
24 27
 @ExtendWith(MockitoExtension.class)
25 28
 class ReportServiceTest {
26 29
 
27
-    @Mock
28
-    private DataReportMapper dataReportMapper;
30
+    @Mock DataReportMapper reportMapper;
31
+    @Mock ReportTemplateMapper templateMapper;
32
+    @Mock DataStatisticsService dataStatisticsService;
29 33
 
30
-    @Mock
31
-    private ReportTemplateMapper reportTemplateMapper;
34
+    @InjectMocks ReportService service;
32 35
 
33
-    @Mock
34
-    private WaterQuantityMapper waterQuantityMapper;
36
+    private Map<String, Object> summaryWithTrend() {
37
+        // generateWaterVolumeReport 依赖 areaSupplyStats + dailyTrend,给非空数据避免空指针
38
+        return Map.of(
39
+                "totalSupply", 5000.0,
40
+                "areaSupplyStats", List.of(Map.of("area", "A区", "supply", 2500.0)),
41
+                "dailyTrend", List.of(Map.of("date", "2026-06-01", "dailySupply", 800.0))
42
+        );
43
+    }
35 44
 
36
-    @Mock
37
-    private WaterQualityMapper waterQualityMapper;
45
+    private Map<String, Object> qualityStats() {
46
+        return Map.of(
47
+                "totalTests", 100,
48
+                "qualified", 98,
49
+                "areaQualityStats", List.of(Map.of("area", "A区", "rate", 98.0))
50
+        );
51
+    }
52
+
53
+    private Map<String, Object> alarmStats() {
54
+        return Map.of(
55
+                "totalAlarms", 12,
56
+                "levelStats", List.of(Map.of("level", "high", "count", 3)),
57
+                "dailyTrend", List.of(Map.of("date", "2026-06-01", "count", 2))
58
+        );
59
+    }
38 60
 
39
-    private ReportService reportService;
61
+    // ---------- 报表生成 ----------
40 62
 
41
-    @BeforeEach
42
-    void setUp() {
43
-        reportService = new ReportService(dataReportMapper, reportTemplateMapper,
44
-            waterQuantityMapper, waterQualityMapper);
63
+    @Test
64
+    void generateWaterVolumeReport_buildsAndInserts() {
65
+        when(dataStatisticsService.getWaterVolumeSummary(any(), any())).thenReturn(summaryWithTrend());
66
+        when(reportMapper.insert(any())).thenReturn(1);
67
+
68
+        DataReport r = service.generateWaterVolumeReport("周报", null, null);
69
+
70
+        assertNotNull(r);
71
+        assertEquals("water_volume", r.getReportType());
72
+        assertEquals("水量汇总报表", r.getReportName());
73
+        assertEquals("GENERATED", r.getStatus());
74
+        assertTrue(r.getReportCode().startsWith("WATER-VOL-"));
75
+        verify(reportMapper).insert(any());
45 76
     }
46 77
 
47 78
     @Test
48
-    @DisplayName("生成日报-水量")
49
-    void testGenerateDailyReport_Quantity() {
50
-        // Given
51
-        ReportTemplate template = new ReportTemplate();
52
-        template.setId(1L);
53
-        template.setTemplateCode("TPL-QTY-DAY");
54
-        template.setReportType("daily");
55
-        template.setDataType("quantity");
56
-
57
-        when(reportTemplateMapper.findByType("daily", "quantity")).thenReturn(List.of(template));
58
-        when(waterQuantityMapper.aggregateByArea(any(), any(), any())).thenReturn(List.of());
59
-        when(waterQuantityMapper.aggregateDaily(any(), any(), any(), any())).thenReturn(List.of());
60
-        when(dataReportMapper.insert(any(DataReport.class))).thenReturn(1);
61
-
62
-        // When
63
-        DataReport report = reportService.generateReport("daily", "quantity", null);
64
-
65
-        // Then
66
-        assertNotNull(report);
67
-        assertEquals("daily", report.getReportType());
68
-        assertEquals("quantity", report.getDataType());
69
-        assertEquals("generated", report.getStatus());
70
-        assertNotNull(report.getReportCode());
71
-        assertTrue(report.getReportCode().startsWith("RPT-"));
72
-        verify(dataReportMapper).insert(any(DataReport.class));
79
+    void generateWaterQualityReport_buildsAndInserts() {
80
+        when(dataStatisticsService.getWaterQualityRate(any(), any())).thenReturn(qualityStats());
81
+        when(reportMapper.insert(any())).thenReturn(1);
82
+
83
+        DataReport r = service.generateWaterQualityReport("周报", null, null);
84
+
85
+        assertEquals("water_quality", r.getReportType());
86
+        assertEquals("GENERATED", r.getStatus());
87
+        verify(reportMapper).insert(any());
73 88
     }
74 89
 
75 90
     @Test
76
-    @DisplayName("生成月报-水质(指定时间段)")
77
-    void testGenerateMonthlyReport_Quality_WithPeriod() {
78
-        when(reportTemplateMapper.findByType("monthly", "quality")).thenReturn(List.of());
79
-        when(waterQualityMapper.aggregateByArea(any(), any(), any())).thenReturn(List.of());
80
-        when(waterQualityMapper.aggregateDaily(any(), any(), any(), any())).thenReturn(List.of());
81
-        when(dataReportMapper.insert(any(DataReport.class))).thenReturn(1);
82
-
83
-        LocalDate start = LocalDate.of(2026, 1, 1);
84
-        LocalDate end = LocalDate.of(2026, 1, 31);
85
-
86
-        DataReport report = reportService.generateReport("monthly", "quality", "城东", start, end);
87
-
88
-        assertNotNull(report);
89
-        assertEquals("monthly", report.getReportType());
90
-        assertEquals("quality", report.getDataType());
91
-        assertEquals("城东", report.getArea());
92
-        assertEquals(start, report.getPeriodStart());
93
-        assertEquals(end, report.getPeriodEnd());
91
+    void generateAlarmStatisticsReport_buildsAndInserts() {
92
+        when(dataStatisticsService.getAlarmStatistics(any(), any())).thenReturn(alarmStats());
93
+        when(reportMapper.insert(any())).thenReturn(1);
94
+
95
+        DataReport r = service.generateAlarmStatisticsReport("周报", null, null);
96
+
97
+        assertEquals("alarm_statistics", r.getReportType());
98
+        verify(reportMapper).insert(any());
94 99
     }
95 100
 
96 101
     @Test
97
-    @DisplayName("生成报表-不支持的类型抛异常")
98
-    void testGenerateReport_InvalidType() {
99
-        assertThrows(IllegalArgumentException.class, () ->
100
-            reportService.generateReport("invalid", "quantity", null)
101
-        );
102
+    void generateWaterVolumeReport_propagatesException() {
103
+        when(dataStatisticsService.getWaterVolumeSummary(any(), any())).thenThrow(new RuntimeException("db error"));
104
+        assertThrows(RuntimeException.class, () -> service.generateWaterVolumeReport("周报", null, null));
102 105
     }
103 106
 
107
+    // ---------- 报表查询 ----------
108
+
104 109
     @Test
105
-    @DisplayName("查询报表列表-分页")
106
-    void testListReports() {
107
-        Page<DataReport> mockPage = new Page<>(1, 10);
108
-        mockPage.setRecords(List.of());
109
-        mockPage.setTotal(0);
110
+    void getReport_delegatesToMapper() {
111
+        DataReport r = new DataReport();
112
+        r.setId(1L);
113
+        when(reportMapper.selectById(1L)).thenReturn(r);
114
+        assertEquals(1L, service.getReport(1L).getId());
115
+    }
110 116
 
111
-        when(dataReportMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class)))
112
-            .thenReturn(mockPage);
117
+    @Test
118
+    void getReport_nullWhenAbsent() {
119
+        when(reportMapper.selectById(999L)).thenReturn(null);
120
+        assertNull(service.getReport(999L));
121
+    }
113 122
 
114
-        Page<DataReport> result = reportService.listReports(1, 10, "daily", null);
123
+    @Test
124
+    void listReports_returnsAll() {
125
+        when(reportMapper.selectList(any())).thenReturn(List.of(new DataReport()));
126
+        assertEquals(1, service.listReports(null, null).size());
127
+        verify(reportMapper).selectList(any());
128
+    }
129
+
130
+    // ---------- 报表发布 ----------
115 131
 
116
-        assertNotNull(result);
117
-        verify(dataReportMapper).selectPage(any(Page.class), any(LambdaQueryWrapper.class));
132
+    @Test
133
+    void publishReport_updatesStatusToPublished() {
134
+        DataReport r = new DataReport();
135
+        r.setId(1L);
136
+        r.setStatus("GENERATED");
137
+        when(reportMapper.selectById(1L)).thenReturn(r);
138
+
139
+        service.publishReport(1L);
140
+
141
+        assertEquals("PUBLISHED", r.getStatus());
142
+        verify(reportMapper).updateById(r);
143
+    }
144
+
145
+    @Test
146
+    void publishReport_throwsWhenAbsent() {
147
+        when(reportMapper.selectById(1L)).thenReturn(null);
148
+        assertThrows(RuntimeException.class, () -> service.publishReport(1L));
118 149
     }
119 150
 
151
+    // ---------- 模板管理 ----------
152
+
120 153
     @Test
121
-    @DisplayName("模板CRUD操作")
122
-    void testTemplateOperations() {
123
-        // Create
124
-        ReportTemplate template = new ReportTemplate();
125
-        template.setTemplateName("测试模板");
126
-        template.setTemplateCode("TPL-TEST");
127
-        template.setReportType("daily");
128
-        template.setDataType("quantity");
129
-
130
-        when(reportTemplateMapper.insert(any(ReportTemplate.class))).thenReturn(1);
131
-        ReportTemplate created = reportService.createTemplate(template);
132
-        assertEquals("测试模板", created.getTemplateName());
133
-
134
-        // Get
135
-        when(reportTemplateMapper.selectById(1L)).thenReturn(template);
136
-        ReportTemplate found = reportService.getTemplate(1L);
137
-        assertNotNull(found);
138
-
139
-        // Not found
140
-        when(reportTemplateMapper.selectById(999L)).thenReturn(null);
141
-        assertThrows(RuntimeException.class, () -> reportService.getTemplate(999L));
154
+    void listTemplates_delegates() {
155
+        when(templateMapper.selectList(any())).thenReturn(List.of(new ReportTemplate()));
156
+        assertEquals(1, service.listTemplates().size());
142 157
     }
143 158
 
144 159
     @Test
145
-    @DisplayName("查询最近报表和统计")
146
-    void testRecentAndStatistics() {
147
-        when(dataReportMapper.findRecent(any(), any(), eq(5))).thenReturn(List.of());
148
-        when(dataReportMapper.countByType()).thenReturn(List.of());
149
-
150
-        List<DataReport> recent = reportService.findRecentReports(null, null, 5);
151
-        List<Map<String, Object>> stats = reportService.countReportsByType();
152
-
153
-        assertNotNull(recent);
154
-        assertNotNull(stats);
155
-        verify(dataReportMapper).findRecent(any(), any(), eq(5));
156
-        verify(dataReportMapper).countByType();
160
+    void createTemplate_insertsAndReturns() {
161
+        ReportTemplate t = new ReportTemplate();
162
+        t.setTemplateName("t1");
163
+        when(templateMapper.insert(any())).thenReturn(1);
164
+        ReportTemplate result = service.createTemplate(t);
165
+        assertEquals("t1", result.getTemplateName());
166
+        verify(templateMapper).insert(any());
157 167
     }
158 168
 }