|
|
@@ -1,586 +1,399 @@
|
|
1
|
1
|
package com.water.production.service;
|
|
2
|
2
|
|
|
|
3
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
3
|
4
|
import com.water.production.dto.QualityQueryRequest;
|
|
4
|
5
|
import com.water.production.dto.QualityStatVO;
|
|
5
|
6
|
import com.water.production.entity.QualityStandard;
|
|
6
|
7
|
import com.water.production.entity.QualityTestPlan;
|
|
7
|
8
|
import com.water.production.entity.QualityTestRecord;
|
|
|
9
|
+import com.water.production.mapper.QualityStandardMapper;
|
|
|
10
|
+import com.water.production.mapper.QualityTestPlanMapper;
|
|
|
11
|
+import com.water.production.mapper.QualityTestRecordMapper;
|
|
|
12
|
+import org.junit.jupiter.api.BeforeEach;
|
|
8
|
13
|
import org.junit.jupiter.api.DisplayName;
|
|
9
|
14
|
import org.junit.jupiter.api.Test;
|
|
|
15
|
+import org.junit.jupiter.api.extension.ExtendWith;
|
|
|
16
|
+import org.mockito.InjectMocks;
|
|
|
17
|
+import org.mockito.Mock;
|
|
|
18
|
+import org.mockito.junit.jupiter.MockitoExtension;
|
|
10
|
19
|
|
|
11
|
|
-import java.math.BigDecimal;
|
|
12
|
|
-import java.math.RoundingMode;
|
|
13
|
20
|
import java.time.LocalDate;
|
|
14
|
21
|
import java.time.LocalTime;
|
|
15
|
22
|
import java.util.*;
|
|
16
|
|
-import java.util.stream.Collectors;
|
|
17
|
23
|
|
|
18
|
24
|
import static org.junit.jupiter.api.Assertions.*;
|
|
|
25
|
+import static org.mockito.ArgumentMatchers.*;
|
|
|
26
|
+import static org.mockito.Mockito.*;
|
|
19
|
27
|
|
|
20
|
|
-/**
|
|
21
|
|
- * 水质检测台账单元测试
|
|
22
|
|
- * 覆盖实体构建、合格判定、统计计算、计划调度、查询筛选、CSV转义等
|
|
23
|
|
- */
|
|
|
28
|
+@ExtendWith(MockitoExtension.class)
|
|
24
|
29
|
class QualityLedgerServiceTest {
|
|
25
|
30
|
|
|
26
|
|
- // ========== 1. 实体完整性测试 ==========
|
|
|
31
|
+ @Mock
|
|
|
32
|
+ private QualityTestRecordMapper recordMapper;
|
|
|
33
|
+
|
|
|
34
|
+ @Mock
|
|
|
35
|
+ private QualityStandardService standardService;
|
|
|
36
|
+
|
|
|
37
|
+ @Mock
|
|
|
38
|
+ private QualityTestPlanMapper planMapper;
|
|
|
39
|
+
|
|
|
40
|
+ @Mock
|
|
|
41
|
+ private QualityStandardMapper standardMapper;
|
|
|
42
|
+
|
|
|
43
|
+ @InjectMocks
|
|
|
44
|
+ private QualityLedgerService ledgerService;
|
|
|
45
|
+
|
|
|
46
|
+ private QualityTestPlanService planService;
|
|
|
47
|
+
|
|
|
48
|
+ private QualityTestRecord sampleRecord;
|
|
|
49
|
+
|
|
|
50
|
+ @BeforeEach
|
|
|
51
|
+ void setUp() {
|
|
|
52
|
+ planService = new QualityTestPlanService(planMapper);
|
|
|
53
|
+
|
|
|
54
|
+ sampleRecord = new QualityTestRecord();
|
|
|
55
|
+ sampleRecord.setId(1L);
|
|
|
56
|
+ sampleRecord.setTestType("routine");
|
|
|
57
|
+ sampleRecord.setWaterType("factoryWater");
|
|
|
58
|
+ sampleRecord.setSamplingPoint("出厂水采样点A");
|
|
|
59
|
+ sampleRecord.setArea("城区A");
|
|
|
60
|
+ sampleRecord.setTestDate(LocalDate.now());
|
|
|
61
|
+ sampleRecord.setTestTime(LocalTime.of(10, 0));
|
|
|
62
|
+ sampleRecord.setTester("张三");
|
|
|
63
|
+ sampleRecord.setTurbidity(0.5);
|
|
|
64
|
+ sampleRecord.setPh(7.2);
|
|
|
65
|
+ sampleRecord.setResidualChlorine(0.8);
|
|
|
66
|
+ sampleRecord.setColor(5.0);
|
|
|
67
|
+ sampleRecord.setOdor("无");
|
|
|
68
|
+ sampleRecord.setEcoli(0.0);
|
|
|
69
|
+ sampleRecord.setColonyCount(20.0);
|
|
|
70
|
+ }
|
|
|
71
|
+
|
|
|
72
|
+ // ==================== 记录 CRUD 测试 ====================
|
|
27
|
73
|
|
|
28
|
74
|
@Test
|
|
29
|
|
- @DisplayName("QualityTestRecord 实体字段完整性")
|
|
30
|
|
- void testQualityTestRecordEntity() {
|
|
31
|
|
- QualityTestRecord record = new QualityTestRecord();
|
|
32
|
|
- record.setId(1L);
|
|
33
|
|
- record.setTestType("routine");
|
|
34
|
|
- record.setWaterType("treated");
|
|
35
|
|
- record.setSamplingPoint("出厂水口");
|
|
36
|
|
- record.setArea("一体化水厂");
|
|
37
|
|
- record.setTestDate(LocalDate.of(2026, 6, 14));
|
|
38
|
|
- record.setTestTime(LocalTime.of(9, 30));
|
|
39
|
|
- record.setTester("张三");
|
|
40
|
|
- record.setTurbidity(new BigDecimal("0.5"));
|
|
41
|
|
- record.setPh(new BigDecimal("7.2"));
|
|
42
|
|
- record.setResidualChlorine(new BigDecimal("0.5"));
|
|
43
|
|
- record.setColor(new BigDecimal("5"));
|
|
44
|
|
- record.setOdor(new BigDecimal("0"));
|
|
45
|
|
- record.setEcoli(BigDecimal.ZERO);
|
|
46
|
|
- record.setColonyCount(new BigDecimal("12"));
|
|
47
|
|
- record.setComplianceStatus("qualified");
|
|
48
|
|
- record.setRemark("正常");
|
|
49
|
|
-
|
|
50
|
|
- assertEquals(1L, record.getId());
|
|
51
|
|
- assertEquals("routine", record.getTestType());
|
|
52
|
|
- assertEquals("treated", record.getWaterType());
|
|
53
|
|
- assertEquals("出厂水口", record.getSamplingPoint());
|
|
54
|
|
- assertEquals(new BigDecimal("0.5"), record.getTurbidity());
|
|
55
|
|
- assertEquals(new BigDecimal("7.2"), record.getPh());
|
|
56
|
|
- assertEquals(new BigDecimal("0.5"), record.getResidualChlorine());
|
|
57
|
|
- assertEquals("qualified", record.getComplianceStatus());
|
|
58
|
|
- assertNotNull(record.getTestDate());
|
|
59
|
|
- assertNotNull(record.getTestTime());
|
|
|
75
|
+ @DisplayName("1. 创建检测记录-自动判定合格")
|
|
|
76
|
+ void testCreateRecord_Qualified() {
|
|
|
77
|
+ when(standardService.getStandard("factoryWater", "turbidity"))
|
|
|
78
|
+ .thenReturn(buildStandard("turbidity", null, 1.0));
|
|
|
79
|
+ when(standardService.getStandard("factoryWater", "ph"))
|
|
|
80
|
+ .thenReturn(buildStandard("ph", 6.5, 8.5));
|
|
|
81
|
+ when(standardService.getStandard("factoryWater", "residualChlorine"))
|
|
|
82
|
+ .thenReturn(buildStandard("residualChlorine", 0.3, 4.0));
|
|
|
83
|
+ when(standardService.getStandard("factoryWater", "color"))
|
|
|
84
|
+ .thenReturn(buildStandard("color", null, 15.0));
|
|
|
85
|
+ when(standardService.getStandard("factoryWater", "ecoli"))
|
|
|
86
|
+ .thenReturn(buildStandard("ecoli", null, 0.0));
|
|
|
87
|
+ when(standardService.getStandard("factoryWater", "colonyCount"))
|
|
|
88
|
+ .thenReturn(buildStandard("colonyCount", null, 100.0));
|
|
|
89
|
+ when(recordMapper.insert(any())).thenReturn(1);
|
|
|
90
|
+
|
|
|
91
|
+ QualityTestRecord result = ledgerService.create(sampleRecord);
|
|
|
92
|
+
|
|
|
93
|
+ assertEquals("qualified", result.getComplianceStatus());
|
|
|
94
|
+ assertNull(result.getUnqualifiedItems());
|
|
|
95
|
+ verify(recordMapper).insert(sampleRecord);
|
|
60
|
96
|
}
|
|
61
|
97
|
|
|
62
|
98
|
@Test
|
|
63
|
|
- @DisplayName("QualityStandard 实体字段完整性")
|
|
64
|
|
- void testQualityStandardEntity() {
|
|
65
|
|
- QualityStandard standard = new QualityStandard();
|
|
66
|
|
- standard.setId(1L);
|
|
67
|
|
- standard.setStandardName("生活饮用水卫生标准");
|
|
68
|
|
- standard.setStandardCode("GB5749-2022");
|
|
69
|
|
- standard.setParamName("turbidity");
|
|
70
|
|
- standard.setParamLabel("浊度");
|
|
71
|
|
- standard.setParamUnit("NTU");
|
|
72
|
|
- standard.setMinValue(null);
|
|
73
|
|
- standard.setMaxValue(new BigDecimal("1.0"));
|
|
74
|
|
- standard.setWaterType("treated");
|
|
75
|
|
- standard.setEnabled(1);
|
|
76
|
|
-
|
|
77
|
|
- assertEquals("GB5749-2022", standard.getStandardCode());
|
|
78
|
|
- assertEquals("turbidity", standard.getParamName());
|
|
79
|
|
- assertNull(standard.getMinValue());
|
|
80
|
|
- assertEquals(new BigDecimal("1.0"), standard.getMaxValue());
|
|
81
|
|
- assertEquals("treated", standard.getWaterType());
|
|
82
|
|
- assertEquals(1, standard.getEnabled());
|
|
|
99
|
+ @DisplayName("2. 创建检测记录-自动判定不合格")
|
|
|
100
|
+ void testCreateRecord_Unqualified() {
|
|
|
101
|
+ sampleRecord.setTurbidity(2.5); // 超标
|
|
|
102
|
+ sampleRecord.setEcoli(10.0); // 超标
|
|
|
103
|
+
|
|
|
104
|
+ when(standardService.getStandard("factoryWater", "turbidity"))
|
|
|
105
|
+ .thenReturn(buildStandard("turbidity", null, 1.0));
|
|
|
106
|
+ when(standardService.getStandard("factoryWater", "ph"))
|
|
|
107
|
+ .thenReturn(buildStandard("ph", 6.5, 8.5));
|
|
|
108
|
+ when(standardService.getStandard("factoryWater", "residualChlorine"))
|
|
|
109
|
+ .thenReturn(buildStandard("residualChlorine", 0.3, 4.0));
|
|
|
110
|
+ when(standardService.getStandard("factoryWater", "color"))
|
|
|
111
|
+ .thenReturn(buildStandard("color", null, 15.0));
|
|
|
112
|
+ when(standardService.getStandard("factoryWater", "ecoli"))
|
|
|
113
|
+ .thenReturn(buildStandard("ecoli", null, 0.0));
|
|
|
114
|
+ when(standardService.getStandard("factoryWater", "colonyCount"))
|
|
|
115
|
+ .thenReturn(buildStandard("colonyCount", null, 100.0));
|
|
|
116
|
+ when(recordMapper.insert(any())).thenReturn(1);
|
|
|
117
|
+
|
|
|
118
|
+ QualityTestRecord result = ledgerService.create(sampleRecord);
|
|
|
119
|
+
|
|
|
120
|
+ assertEquals("unqualified", result.getComplianceStatus());
|
|
|
121
|
+ assertNotNull(result.getUnqualifiedItems());
|
|
|
122
|
+ assertTrue(result.getUnqualifiedItems().contains("浊度"));
|
|
|
123
|
+ assertTrue(result.getUnqualifiedItems().contains("大肠杆菌"));
|
|
83
|
124
|
}
|
|
84
|
125
|
|
|
85
|
126
|
@Test
|
|
86
|
|
- @DisplayName("QualityTestPlan 实体字段完整性")
|
|
87
|
|
- void testQualityTestPlanEntity() {
|
|
88
|
|
- QualityTestPlan plan = new QualityTestPlan();
|
|
89
|
|
- plan.setId(1L);
|
|
90
|
|
- plan.setPlanName("出厂水日检计划");
|
|
91
|
|
- plan.setTestType("routine");
|
|
92
|
|
- plan.setWaterType("treated");
|
|
93
|
|
- plan.setSamplingPoint("出厂水口");
|
|
94
|
|
- plan.setArea("一体化水厂");
|
|
95
|
|
- plan.setFrequency("daily");
|
|
96
|
|
- plan.setTestParams("turbidity,ph,residual_chlorine");
|
|
97
|
|
- plan.setStartDate(LocalDate.of(2026, 6, 1));
|
|
98
|
|
- plan.setEndDate(null);
|
|
99
|
|
- plan.setNextTestDate(LocalDate.of(2026, 6, 14));
|
|
100
|
|
- plan.setStatus("active");
|
|
101
|
|
- plan.setExecutionCount(13);
|
|
102
|
|
-
|
|
103
|
|
- assertEquals("daily", plan.getFrequency());
|
|
104
|
|
- assertEquals("active", plan.getStatus());
|
|
105
|
|
- assertEquals(13, plan.getExecutionCount());
|
|
106
|
|
- assertNull(plan.getEndDate());
|
|
107
|
|
- assertNotNull(plan.getNextTestDate());
|
|
108
|
|
- }
|
|
|
127
|
+ @DisplayName("3. 创建检测记录-waterType为空时pending")
|
|
|
128
|
+ void testCreateRecord_PendingWhenNoWaterType() {
|
|
|
129
|
+ sampleRecord.setWaterType(null);
|
|
|
130
|
+ when(recordMapper.insert(any())).thenReturn(1);
|
|
109
|
131
|
|
|
110
|
|
- // ========== 2. 合格判定逻辑测试 ==========
|
|
|
132
|
+ QualityTestRecord result = ledgerService.create(sampleRecord);
|
|
111
|
133
|
|
|
112
|
|
- @Test
|
|
113
|
|
- @DisplayName("GB5749-2022 合格判定逻辑 - 全部合格")
|
|
114
|
|
- void testComplianceAllQualified() {
|
|
115
|
|
- // 模拟 GB5749-2022 标准
|
|
116
|
|
- List<QualityStandard> standards = buildDefaultStandards();
|
|
117
|
|
-
|
|
118
|
|
- // 构建合格记录
|
|
119
|
|
- QualityTestRecord record = new QualityTestRecord();
|
|
120
|
|
- record.setWaterType("treated");
|
|
121
|
|
- record.setTurbidity(new BigDecimal("0.5")); // ≤1.0 ✓
|
|
122
|
|
- record.setPh(new BigDecimal("7.2")); // 6.5~8.5 ✓
|
|
123
|
|
- record.setResidualChlorine(new BigDecimal("0.5")); // 0.3~2.0 ✓
|
|
124
|
|
- record.setColor(new BigDecimal("5")); // ≤15 ✓
|
|
125
|
|
- record.setOdor(new BigDecimal("0")); // ≤2 ✓
|
|
126
|
|
- record.setEcoli(BigDecimal.ZERO); // =0 ✓
|
|
127
|
|
- record.setColonyCount(new BigDecimal("12")); // ≤100 ✓
|
|
128
|
|
-
|
|
129
|
|
- List<String> unqualified = evaluateCompliance(record, standards);
|
|
130
|
|
- assertTrue(unqualified.isEmpty(), "所有指标在标准范围内应合格");
|
|
|
134
|
+ assertEquals("pending", result.getComplianceStatus());
|
|
131
|
135
|
}
|
|
132
|
136
|
|
|
133
|
137
|
@Test
|
|
134
|
|
- @DisplayName("GB5749-2022 合格判定逻辑 - 多项超标")
|
|
135
|
|
- void testComplianceMultipleUnqualified() {
|
|
136
|
|
- List<QualityStandard> standards = buildDefaultStandards();
|
|
137
|
|
-
|
|
138
|
|
- QualityTestRecord record = new QualityTestRecord();
|
|
139
|
|
- record.setWaterType("treated");
|
|
140
|
|
- record.setTurbidity(new BigDecimal("2.5")); // >1.0 ✗
|
|
141
|
|
- record.setPh(new BigDecimal("9.0")); // >8.5 ✗
|
|
142
|
|
- record.setResidualChlorine(new BigDecimal("0.1")); // <0.3 ✗
|
|
143
|
|
- record.setColor(new BigDecimal("5")); // ≤15 ✓
|
|
144
|
|
- record.setOdor(new BigDecimal("0")); // ≤2 ✓
|
|
145
|
|
- record.setEcoli(BigDecimal.ZERO); // =0 ✓
|
|
146
|
|
- record.setColonyCount(new BigDecimal("12")); // ≤100 ✓
|
|
147
|
|
-
|
|
148
|
|
- List<String> unqualified = evaluateCompliance(record, standards);
|
|
149
|
|
- assertEquals(3, unqualified.size(), "应有3项不合格: 浊度、pH、余氯");
|
|
150
|
|
- assertTrue(unqualified.stream().anyMatch(s -> s.contains("turbidity")));
|
|
151
|
|
- assertTrue(unqualified.stream().anyMatch(s -> s.contains("ph")));
|
|
152
|
|
- assertTrue(unqualified.stream().anyMatch(s -> s.contains("residual_chlorine")));
|
|
|
138
|
+ @DisplayName("4. 查询检测记录分页")
|
|
|
139
|
+ void testQueryRecords() {
|
|
|
140
|
+ QualityQueryRequest request = new QualityQueryRequest();
|
|
|
141
|
+ request.setPageNum(1);
|
|
|
142
|
+ request.setPageSize(10);
|
|
|
143
|
+
|
|
|
144
|
+ List<Map<String, Object>> mockRecords = List.of(
|
|
|
145
|
+ Map.of("id", 1L, "testType", "routine"),
|
|
|
146
|
+ Map.of("id", 2L, "testType", "emergency")
|
|
|
147
|
+ );
|
|
|
148
|
+ when(recordMapper.selectRecordPage(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(0), eq(10)))
|
|
|
149
|
+ .thenReturn(mockRecords);
|
|
|
150
|
+ when(recordMapper.countRecords(any(), any(), any(), any(), any(), any(), any(), any(), any()))
|
|
|
151
|
+ .thenReturn(2L);
|
|
|
152
|
+
|
|
|
153
|
+ Map<String, Object> result = ledgerService.queryRecords(request);
|
|
|
154
|
+
|
|
|
155
|
+ assertNotNull(result);
|
|
|
156
|
+ assertEquals(2L, result.get("total"));
|
|
|
157
|
+ assertEquals(1, result.get("pageNum"));
|
|
|
158
|
+ assertEquals(1L, result.get("totalPages"));
|
|
153
|
159
|
}
|
|
154
|
160
|
|
|
155
|
161
|
@Test
|
|
156
|
|
- @DisplayName("GB5749-2022 合格判定 - 管网末梢水余氯标准不同")
|
|
157
|
|
- void testComplianceNetworkWaterType() {
|
|
158
|
|
- List<QualityStandard> standards = buildDefaultStandards();
|
|
159
|
|
-
|
|
160
|
|
- QualityTestRecord record = new QualityTestRecord();
|
|
161
|
|
- record.setWaterType("network");
|
|
162
|
|
- record.setTurbidity(new BigDecimal("2.0")); // ≤3.0 (管网标准) ✓
|
|
163
|
|
- record.setPh(new BigDecimal("7.0")); // 6.5~8.5 ✓
|
|
164
|
|
- record.setResidualChlorine(new BigDecimal("0.1")); // 0.05~2.0 (管网标准) ✓
|
|
165
|
|
- record.setColor(new BigDecimal("5")); // ≤15 ✓
|
|
166
|
|
- record.setOdor(new BigDecimal("0")); // ≤2 ✓
|
|
167
|
|
- record.setEcoli(BigDecimal.ZERO);
|
|
168
|
|
- record.setColonyCount(new BigDecimal("50"));
|
|
169
|
|
-
|
|
170
|
|
- List<String> unqualified = evaluateCompliance(record, standards);
|
|
171
|
|
- assertTrue(unqualified.isEmpty(),
|
|
172
|
|
- "管网末梢水浊度2.0应合格(标准≤3.0),余氯0.1应合格(标准≥0.05)");
|
|
|
162
|
+ @DisplayName("5. 获取记录详情")
|
|
|
163
|
+ void testGetById() {
|
|
|
164
|
+ when(recordMapper.selectById(1L)).thenReturn(sampleRecord);
|
|
|
165
|
+
|
|
|
166
|
+ QualityTestRecord result = ledgerService.getById(1L);
|
|
|
167
|
+
|
|
|
168
|
+ assertNotNull(result);
|
|
|
169
|
+ assertEquals(1L, result.getId());
|
|
|
170
|
+ assertEquals("routine", result.getTestType());
|
|
173
|
171
|
}
|
|
174
|
172
|
|
|
175
|
173
|
@Test
|
|
176
|
|
- @DisplayName("合格判定 - null 值不参与判定")
|
|
177
|
|
- void testComplianceNullValuesSkipped() {
|
|
178
|
|
- List<QualityStandard> standards = buildDefaultStandards();
|
|
179
|
|
-
|
|
180
|
|
- QualityTestRecord record = new QualityTestRecord();
|
|
181
|
|
- record.setWaterType("treated");
|
|
182
|
|
- record.setTurbidity(new BigDecimal("0.5"));
|
|
183
|
|
- // 其他参数为 null
|
|
184
|
|
- record.setPh(null);
|
|
185
|
|
- record.setResidualChlorine(null);
|
|
186
|
|
-
|
|
187
|
|
- List<String> unqualified = evaluateCompliance(record, standards);
|
|
188
|
|
- assertTrue(unqualified.isEmpty(), "null值不应参与判定");
|
|
189
|
|
- }
|
|
|
174
|
+ @DisplayName("6. 获取不存在记录抛异常")
|
|
|
175
|
+ void testGetById_NotFound() {
|
|
|
176
|
+ when(recordMapper.selectById(999L)).thenReturn(null);
|
|
190
|
177
|
|
|
191
|
|
- // ========== 3. 统计计算逻辑测试 ==========
|
|
|
178
|
+ assertThrows(Exception.class, () -> ledgerService.getById(999L));
|
|
|
179
|
+ }
|
|
192
|
180
|
|
|
193
|
181
|
@Test
|
|
194
|
|
- @DisplayName("合格率计算")
|
|
195
|
|
- void testQualifiedRateCalculation() {
|
|
196
|
|
- long total = 100;
|
|
197
|
|
- long qualified = 95;
|
|
198
|
|
- long unqualified = 3;
|
|
199
|
|
- long pending = 2;
|
|
200
|
|
-
|
|
201
|
|
- BigDecimal rate = total > 0
|
|
202
|
|
- ? BigDecimal.valueOf(qualified * 100.0 / total).setScale(1, RoundingMode.HALF_UP)
|
|
203
|
|
- : BigDecimal.ZERO;
|
|
204
|
|
-
|
|
205
|
|
- assertEquals(new BigDecimal("95.0"), rate);
|
|
206
|
|
- assertEquals(total, qualified + unqualified + pending);
|
|
|
182
|
+ @DisplayName("7. 更新检测记录")
|
|
|
183
|
+ void testUpdateRecord() {
|
|
|
184
|
+ when(recordMapper.selectById(1L)).thenReturn(sampleRecord);
|
|
|
185
|
+ when(standardService.getStandard(anyString(), anyString())).thenReturn(null);
|
|
|
186
|
+ when(recordMapper.updateById(any())).thenReturn(1);
|
|
|
187
|
+ when(recordMapper.selectById(1L)).thenReturn(sampleRecord);
|
|
|
188
|
+
|
|
|
189
|
+ sampleRecord.setRemark("updated");
|
|
|
190
|
+ QualityTestRecord result = ledgerService.update(1L, sampleRecord);
|
|
|
191
|
+
|
|
|
192
|
+ assertNotNull(result);
|
|
|
193
|
+ verify(recordMapper).updateById(any());
|
|
207
|
194
|
}
|
|
208
|
195
|
|
|
209
|
196
|
@Test
|
|
210
|
|
- @DisplayName("QualityStatVO 数据结构完整性")
|
|
211
|
|
- void testStatVOStructure() {
|
|
212
|
|
- QualityStatVO stat = new QualityStatVO();
|
|
213
|
|
- stat.setTotalCount(200L);
|
|
214
|
|
- stat.setQualifiedCount(190L);
|
|
215
|
|
- stat.setUnqualifiedCount(8L);
|
|
216
|
|
- stat.setPendingCount(2L);
|
|
217
|
|
- stat.setQualifiedRate(new BigDecimal("95.0"));
|
|
218
|
|
-
|
|
219
|
|
- Map<String, BigDecimal> rateByWaterType = new LinkedHashMap<>();
|
|
220
|
|
- rateByWaterType.put("treated", new BigDecimal("97.5"));
|
|
221
|
|
- rateByWaterType.put("network", new BigDecimal("92.3"));
|
|
222
|
|
- stat.setRateByWaterType(rateByWaterType);
|
|
223
|
|
-
|
|
224
|
|
- Map<String, Long> unqByParam = new LinkedHashMap<>();
|
|
225
|
|
- unqByParam.put("turbidity", 5L);
|
|
226
|
|
- unqByParam.put("residual_chlorine", 3L);
|
|
227
|
|
- stat.setUnqualifiedByParam(unqByParam);
|
|
228
|
|
-
|
|
229
|
|
- List<Map<String, Object>> trend = new ArrayList<>();
|
|
230
|
|
- trend.add(Map.of("month", "2026-05", "rate", new BigDecimal("94.0")));
|
|
231
|
|
- trend.add(Map.of("month", "2026-06", "rate", new BigDecimal("96.0")));
|
|
232
|
|
- stat.setMonthlyTrend(trend);
|
|
233
|
|
-
|
|
234
|
|
- assertEquals(200L, stat.getTotalCount());
|
|
235
|
|
- assertEquals(2, stat.getRateByWaterType().size());
|
|
236
|
|
- assertEquals(2, stat.getUnqualifiedByParam().size());
|
|
237
|
|
- assertEquals(2, stat.getMonthlyTrend().size());
|
|
238
|
|
- assertEquals("turbidity", stat.getUnqualifiedByParam().keySet().iterator().next());
|
|
239
|
|
- }
|
|
|
197
|
+ @DisplayName("8. 删除检测记录")
|
|
|
198
|
+ void testDeleteRecord() {
|
|
|
199
|
+ when(recordMapper.selectById(1L)).thenReturn(sampleRecord);
|
|
|
200
|
+ when(recordMapper.deleteById(1L)).thenReturn(1);
|
|
240
|
201
|
|
|
241
|
|
- // ========== 4. 查询筛选逻辑测试 ==========
|
|
|
202
|
+ ledgerService.delete(1L);
|
|
242
|
203
|
|
|
243
|
|
- @Test
|
|
244
|
|
- @DisplayName("QualityQueryRequest 默认值和筛选")
|
|
245
|
|
- void testQueryRequestDefaults() {
|
|
246
|
|
- QualityQueryRequest req = new QualityQueryRequest();
|
|
247
|
|
- assertEquals(1, req.getPageNum());
|
|
248
|
|
- assertEquals(20, req.getPageSize());
|
|
249
|
|
- assertNull(req.getTestType());
|
|
250
|
|
- assertNull(req.getWaterType());
|
|
251
|
|
- assertNull(req.getArea());
|
|
252
|
|
- assertNull(req.getComplianceStatus());
|
|
253
|
|
- assertNull(req.getStartDate());
|
|
254
|
|
- assertNull(req.getEndDate());
|
|
255
|
|
- assertNull(req.getKeyword());
|
|
|
204
|
+ verify(recordMapper).deleteById(1L);
|
|
256
|
205
|
}
|
|
257
|
206
|
|
|
258
|
207
|
@Test
|
|
259
|
|
- @DisplayName("多维度筛选逻辑")
|
|
260
|
|
- void testMultiDimensionFilter() {
|
|
261
|
|
- List<QualityTestRecord> records = buildMockRecords();
|
|
262
|
|
-
|
|
263
|
|
- // 按水样类型筛选
|
|
264
|
|
- List<QualityTestRecord> filtered = records.stream()
|
|
265
|
|
- .filter(r -> "treated".equals(r.getWaterType()))
|
|
266
|
|
- .collect(Collectors.toList());
|
|
267
|
|
- assertEquals(3, filtered.size());
|
|
268
|
|
-
|
|
269
|
|
- // 按合格状态筛选
|
|
270
|
|
- filtered = records.stream()
|
|
271
|
|
- .filter(r -> "qualified".equals(r.getComplianceStatus()))
|
|
272
|
|
- .collect(Collectors.toList());
|
|
273
|
|
- assertEquals(3, filtered.size());
|
|
274
|
|
-
|
|
275
|
|
- // 按区域筛选
|
|
276
|
|
- filtered = records.stream()
|
|
277
|
|
- .filter(r -> "一体化水厂".equals(r.getArea()))
|
|
278
|
|
- .collect(Collectors.toList());
|
|
279
|
|
- assertEquals(2, filtered.size());
|
|
280
|
|
-
|
|
281
|
|
- // 组合筛选: 出厂水 + 合格
|
|
282
|
|
- filtered = records.stream()
|
|
283
|
|
- .filter(r -> "treated".equals(r.getWaterType()) && "qualified".equals(r.getComplianceStatus()))
|
|
284
|
|
- .collect(Collectors.toList());
|
|
285
|
|
- assertEquals(2, filtered.size());
|
|
286
|
|
-
|
|
287
|
|
- // 关键词搜索 (采样点)
|
|
288
|
|
- String keyword = "出厂";
|
|
289
|
|
- filtered = records.stream()
|
|
290
|
|
- .filter(r -> r.getSamplingPoint() != null && r.getSamplingPoint().contains(keyword))
|
|
291
|
|
- .collect(Collectors.toList());
|
|
292
|
|
- assertEquals(2, filtered.size());
|
|
293
|
|
- }
|
|
|
208
|
+ @DisplayName("9. 批量删除记录")
|
|
|
209
|
+ void testBatchDelete() {
|
|
|
210
|
+ List<Long> ids = List.of(1L, 2L, 3L);
|
|
|
211
|
+ when(recordMapper.deleteBatchIds(ids)).thenReturn(3);
|
|
294
|
212
|
|
|
295
|
|
- @Test
|
|
296
|
|
- @DisplayName("分页参数计算")
|
|
297
|
|
- void testPaginationCalculation() {
|
|
298
|
|
- int total = 55;
|
|
299
|
|
- int pageSize = 20;
|
|
300
|
|
- int pages = (int) Math.ceil((double) total / pageSize);
|
|
301
|
|
- assertEquals(3, pages);
|
|
302
|
|
-
|
|
303
|
|
- // 第2页偏移量
|
|
304
|
|
- int offset = (2 - 1) * pageSize;
|
|
305
|
|
- assertEquals(20, offset);
|
|
|
213
|
+ ledgerService.batchDelete(ids);
|
|
|
214
|
+
|
|
|
215
|
+ verify(recordMapper).deleteBatchIds(ids);
|
|
306
|
216
|
}
|
|
307
|
217
|
|
|
308
|
|
- // ========== 5. 检测计划调度测试 ==========
|
|
|
218
|
+ // ==================== 合格判定测试 ====================
|
|
309
|
219
|
|
|
310
|
220
|
@Test
|
|
311
|
|
- @DisplayName("检测计划频率计算 - 日检/周检/月检")
|
|
312
|
|
- void testPlanFrequencyCalculation() {
|
|
313
|
|
- LocalDate baseDate = LocalDate.of(2026, 6, 14);
|
|
314
|
|
-
|
|
315
|
|
- // 日检
|
|
316
|
|
- assertEquals(baseDate.plusDays(1), calculateNextDate(baseDate, "daily"));
|
|
317
|
|
- // 周检
|
|
318
|
|
- assertEquals(baseDate.plusWeeks(1), calculateNextDate(baseDate, "weekly"));
|
|
319
|
|
- // 月检
|
|
320
|
|
- assertEquals(baseDate.plusMonths(1), calculateNextDate(baseDate, "monthly"));
|
|
|
221
|
+ @DisplayName("10. 合格判定-pH偏低")
|
|
|
222
|
+ void testEvaluateCompliance_PhLow() {
|
|
|
223
|
+ sampleRecord.setPh(5.0);
|
|
|
224
|
+ when(standardService.getStandard("factoryWater", "turbidity")).thenReturn(buildStandard("turbidity", null, 1.0));
|
|
|
225
|
+ when(standardService.getStandard("factoryWater", "ph")).thenReturn(buildStandard("ph", 6.5, 8.5));
|
|
|
226
|
+ when(standardService.getStandard("factoryWater", "residualChlorine")).thenReturn(buildStandard("residualChlorine", 0.3, 4.0));
|
|
|
227
|
+ when(standardService.getStandard("factoryWater", "color")).thenReturn(buildStandard("color", null, 15.0));
|
|
|
228
|
+ when(standardService.getStandard("factoryWater", "ecoli")).thenReturn(buildStandard("ecoli", null, 0.0));
|
|
|
229
|
+ when(standardService.getStandard("factoryWater", "colonyCount")).thenReturn(buildStandard("colonyCount", null, 100.0));
|
|
|
230
|
+
|
|
|
231
|
+ ledgerService.evaluateCompliance(sampleRecord);
|
|
|
232
|
+
|
|
|
233
|
+ assertEquals("unqualified", sampleRecord.getComplianceStatus());
|
|
|
234
|
+ assertTrue(sampleRecord.getUnqualifiedItems().contains("pH"));
|
|
|
235
|
+ assertTrue(sampleRecord.getUnqualifiedItems().contains("偏低"));
|
|
321
|
236
|
}
|
|
322
|
237
|
|
|
323
|
238
|
@Test
|
|
324
|
|
- @DisplayName("检测计划过期判定")
|
|
325
|
|
- void testPlanExpiration() {
|
|
326
|
|
- QualityTestPlan plan = new QualityTestPlan();
|
|
327
|
|
- plan.setStartDate(LocalDate.of(2026, 6, 1));
|
|
328
|
|
- plan.setEndDate(LocalDate.of(2026, 6, 30));
|
|
329
|
|
- plan.setFrequency("daily");
|
|
330
|
|
- plan.setStatus("active");
|
|
331
|
|
-
|
|
332
|
|
- // 下次检测日期超出结束日期
|
|
333
|
|
- LocalDate nextDate = LocalDate.of(2026, 7, 1);
|
|
334
|
|
- boolean isExpired = plan.getEndDate() != null && nextDate.isAfter(plan.getEndDate());
|
|
335
|
|
- assertTrue(isExpired, "下次检测日期超出结束日期应判定为过期");
|
|
336
|
|
-
|
|
337
|
|
- // 正常范围内
|
|
338
|
|
- nextDate = LocalDate.of(2026, 6, 15);
|
|
339
|
|
- isExpired = plan.getEndDate() != null && nextDate.isAfter(plan.getEndDate());
|
|
340
|
|
- assertFalse(isExpired, "正常范围内不应过期");
|
|
|
239
|
+ @DisplayName("11. 合格判定-余氯偏高")
|
|
|
240
|
+ void testEvaluateCompliance_ChlorineHigh() {
|
|
|
241
|
+ sampleRecord.setResidualChlorine(5.0);
|
|
|
242
|
+ when(standardService.getStandard("factoryWater", "turbidity")).thenReturn(buildStandard("turbidity", null, 1.0));
|
|
|
243
|
+ when(standardService.getStandard("factoryWater", "ph")).thenReturn(buildStandard("ph", 6.5, 8.5));
|
|
|
244
|
+ when(standardService.getStandard("factoryWater", "residualChlorine")).thenReturn(buildStandard("residualChlorine", 0.3, 4.0));
|
|
|
245
|
+ when(standardService.getStandard("factoryWater", "color")).thenReturn(buildStandard("color", null, 15.0));
|
|
|
246
|
+ when(standardService.getStandard("factoryWater", "ecoli")).thenReturn(buildStandard("ecoli", null, 0.0));
|
|
|
247
|
+ when(standardService.getStandard("factoryWater", "colonyCount")).thenReturn(buildStandard("colonyCount", null, 100.0));
|
|
|
248
|
+
|
|
|
249
|
+ ledgerService.evaluateCompliance(sampleRecord);
|
|
|
250
|
+
|
|
|
251
|
+ assertEquals("unqualified", sampleRecord.getComplianceStatus());
|
|
|
252
|
+ assertTrue(sampleRecord.getUnqualifiedItems().contains("余氯"));
|
|
|
253
|
+ assertTrue(sampleRecord.getUnqualifiedItems().contains("偏高"));
|
|
341
|
254
|
}
|
|
342
|
255
|
|
|
343
|
256
|
@Test
|
|
344
|
|
- @DisplayName("检测计划到期判定")
|
|
345
|
|
- void testPlanDueCheck() {
|
|
346
|
|
- LocalDate today = LocalDate.of(2026, 6, 14);
|
|
347
|
|
-
|
|
348
|
|
- QualityTestPlan duePlan = new QualityTestPlan();
|
|
349
|
|
- duePlan.setStatus("active");
|
|
350
|
|
- duePlan.setNextTestDate(LocalDate.of(2026, 6, 14));
|
|
351
|
|
- duePlan.setEndDate(null); // 长期
|
|
352
|
|
-
|
|
353
|
|
- QualityTestPlan futurePlan = new QualityTestPlan();
|
|
354
|
|
- futurePlan.setStatus("active");
|
|
355
|
|
- futurePlan.setNextTestDate(LocalDate.of(2026, 6, 20));
|
|
356
|
|
- futurePlan.setEndDate(null);
|
|
357
|
|
-
|
|
358
|
|
- QualityTestPlan expiredPlan = new QualityTestPlan();
|
|
359
|
|
- expiredPlan.setStatus("active");
|
|
360
|
|
- expiredPlan.setNextTestDate(LocalDate.of(2026, 6, 10));
|
|
361
|
|
- expiredPlan.setEndDate(LocalDate.of(2026, 6, 12)); // 已过期
|
|
362
|
|
-
|
|
363
|
|
- List<QualityTestPlan> plans = List.of(duePlan, futurePlan, expiredPlan);
|
|
364
|
|
-
|
|
365
|
|
- // 筛选到期计划: nextTestDate <= today AND (endDate IS NULL OR endDate >= today)
|
|
366
|
|
- List<QualityTestPlan> duePlans = plans.stream()
|
|
367
|
|
- .filter(p -> "active".equals(p.getStatus()))
|
|
368
|
|
- .filter(p -> !p.getNextTestDate().isAfter(today))
|
|
369
|
|
- .filter(p -> p.getEndDate() == null || !p.getEndDate().isBefore(today))
|
|
370
|
|
- .collect(Collectors.toList());
|
|
371
|
|
-
|
|
372
|
|
- assertEquals(1, duePlans.size(), "只有1个计划到期");
|
|
373
|
|
- assertEquals(duePlan, duePlans.get(0));
|
|
|
257
|
+ @DisplayName("12. 合格判定-无标准时不判定")
|
|
|
258
|
+ void testEvaluateCompliance_NoStandard() {
|
|
|
259
|
+ sampleRecord.setWaterType("unknownType");
|
|
|
260
|
+ when(standardService.getStandard(eq("unknownType"), anyString())).thenReturn(null);
|
|
|
261
|
+
|
|
|
262
|
+ ledgerService.evaluateCompliance(sampleRecord);
|
|
|
263
|
+
|
|
|
264
|
+ assertEquals("qualified", sampleRecord.getComplianceStatus());
|
|
374
|
265
|
}
|
|
375
|
266
|
|
|
376
|
|
- // ========== 6. 数据导出格式测试 ==========
|
|
|
267
|
+ // ==================== 统计测试 ====================
|
|
377
|
268
|
|
|
378
|
269
|
@Test
|
|
379
|
|
- @DisplayName("检测类型格式化")
|
|
380
|
|
- void testTestTypeFormatting() {
|
|
381
|
|
- assertEquals("常规检测", formatTestType("routine"));
|
|
382
|
|
- assertEquals("专项检测", formatTestType("special"));
|
|
383
|
|
- assertEquals("投诉检测", formatTestType("complaint"));
|
|
384
|
|
- assertEquals("", formatTestType(null));
|
|
|
270
|
+ @DisplayName("13. 获取统计数据")
|
|
|
271
|
+ void testGetStatistics() {
|
|
|
272
|
+ List<Map<String, Object>> statusStats = List.of(
|
|
|
273
|
+ Map.of("status", "qualified", "count", 80L),
|
|
|
274
|
+ Map.of("status", "unqualified", "count", 15L),
|
|
|
275
|
+ Map.of("status", "pending", "count", 5L)
|
|
|
276
|
+ );
|
|
|
277
|
+ when(recordMapper.statByComplianceStatus(any(), any())).thenReturn(statusStats);
|
|
|
278
|
+ when(recordMapper.statRateByWaterType(any(), any())).thenReturn(Collections.emptyList());
|
|
|
279
|
+ when(recordMapper.statRateByArea(any(), any())).thenReturn(Collections.emptyList());
|
|
|
280
|
+ when(recordMapper.statMonthlyTrend(any(), any())).thenReturn(Collections.emptyList());
|
|
|
281
|
+ when(recordMapper.statUnqualifiedByParam(any(), any())).thenReturn(Collections.emptyList());
|
|
|
282
|
+ when(recordMapper.statParamAvg()).thenReturn(Map.of(
|
|
|
283
|
+ "avgTurbidity", 0.45, "avgPh", 7.1, "avgResidualChlorine", 0.6
|
|
|
284
|
+ ));
|
|
|
285
|
+
|
|
|
286
|
+ QualityStatVO vo = ledgerService.getStatistics(null, null);
|
|
|
287
|
+
|
|
|
288
|
+ assertNotNull(vo);
|
|
|
289
|
+ assertEquals(100L, vo.getTotalRecords());
|
|
|
290
|
+ assertEquals(80L, vo.getQualifiedCount());
|
|
|
291
|
+ assertEquals(15L, vo.getUnqualifiedCount());
|
|
|
292
|
+ assertEquals(5L, vo.getPendingCount());
|
|
|
293
|
+ assertEquals(80.0, vo.getQualifiedRate());
|
|
385
|
294
|
}
|
|
386
|
295
|
|
|
387
|
296
|
@Test
|
|
388
|
|
- @DisplayName("水样类型格式化")
|
|
389
|
|
- void testWaterTypeFormatting() {
|
|
390
|
|
- assertEquals("原水", formatWaterType("raw"));
|
|
391
|
|
- assertEquals("出厂水", formatWaterType("treated"));
|
|
392
|
|
- assertEquals("管网末梢水", formatWaterType("network"));
|
|
393
|
|
- assertEquals("", formatWaterType(null));
|
|
|
297
|
+ @DisplayName("14. 统计-空数据")
|
|
|
298
|
+ void testGetStatistics_Empty() {
|
|
|
299
|
+ when(recordMapper.statByComplianceStatus(any(), any())).thenReturn(Collections.emptyList());
|
|
|
300
|
+ when(recordMapper.statRateByWaterType(any(), any())).thenReturn(Collections.emptyList());
|
|
|
301
|
+ when(recordMapper.statRateByArea(any(), any())).thenReturn(Collections.emptyList());
|
|
|
302
|
+ when(recordMapper.statMonthlyTrend(any(), any())).thenReturn(Collections.emptyList());
|
|
|
303
|
+ when(recordMapper.statUnqualifiedByParam(any(), any())).thenReturn(Collections.emptyList());
|
|
|
304
|
+ when(recordMapper.statParamAvg()).thenReturn(null);
|
|
|
305
|
+
|
|
|
306
|
+ QualityStatVO vo = ledgerService.getStatistics(null, null);
|
|
|
307
|
+
|
|
|
308
|
+ assertEquals(0L, vo.getTotalRecords());
|
|
|
309
|
+ assertEquals(0.0, vo.getQualifiedRate());
|
|
394
|
310
|
}
|
|
395
|
311
|
|
|
|
312
|
+ // ==================== 辅助方法测试 ====================
|
|
|
313
|
+
|
|
396
|
314
|
@Test
|
|
397
|
|
- @DisplayName("合格状态格式化")
|
|
398
|
|
- void testComplianceFormatting() {
|
|
399
|
|
- assertEquals("合格", formatCompliance("qualified"));
|
|
400
|
|
- assertEquals("不合格", formatCompliance("unqualified"));
|
|
401
|
|
- assertEquals("待判定", formatCompliance("pending"));
|
|
402
|
|
- assertEquals("待判定", formatCompliance(null));
|
|
403
|
|
- }
|
|
|
315
|
+ @DisplayName("15. 获取区域列表")
|
|
|
316
|
+ void testGetAreaList() {
|
|
|
317
|
+ QualityTestRecord r1 = new QualityTestRecord();
|
|
|
318
|
+ r1.setArea("城区A");
|
|
|
319
|
+ QualityTestRecord r2 = new QualityTestRecord();
|
|
|
320
|
+ r2.setArea("城区B");
|
|
|
321
|
+ when(recordMapper.selectList(any())).thenReturn(List.of(r1, r2));
|
|
404
|
322
|
|
|
405
|
|
- // ========== Helper Methods ==========
|
|
406
|
|
-
|
|
407
|
|
- private List<QualityStandard> buildDefaultStandards() {
|
|
408
|
|
- List<QualityStandard> standards = new ArrayList<>();
|
|
409
|
|
-
|
|
410
|
|
- // 浊度 - 出厂水 ≤1.0
|
|
411
|
|
- QualityStandard s1 = new QualityStandard();
|
|
412
|
|
- s1.setParamName("turbidity"); s1.setMinValue(null); s1.setMaxValue(new BigDecimal("1.0"));
|
|
413
|
|
- s1.setWaterType("treated");
|
|
414
|
|
- standards.add(s1);
|
|
415
|
|
-
|
|
416
|
|
- // 浊度 - 管网 ≤3.0
|
|
417
|
|
- QualityStandard s1n = new QualityStandard();
|
|
418
|
|
- s1n.setParamName("turbidity"); s1n.setMinValue(null); s1n.setMaxValue(new BigDecimal("3.0"));
|
|
419
|
|
- s1n.setWaterType("network");
|
|
420
|
|
- standards.add(s1n);
|
|
421
|
|
-
|
|
422
|
|
- // pH - 6.5~8.5
|
|
423
|
|
- QualityStandard s2 = new QualityStandard();
|
|
424
|
|
- s2.setParamName("ph"); s2.setMinValue(new BigDecimal("6.5")); s2.setMaxValue(new BigDecimal("8.5"));
|
|
425
|
|
- s2.setWaterType("all");
|
|
426
|
|
- standards.add(s2);
|
|
427
|
|
-
|
|
428
|
|
- // 余氯 - 出厂水 0.3~2.0
|
|
429
|
|
- QualityStandard s3 = new QualityStandard();
|
|
430
|
|
- s3.setParamName("residual_chlorine"); s3.setMinValue(new BigDecimal("0.3"));
|
|
431
|
|
- s3.setMaxValue(new BigDecimal("2.0")); s3.setWaterType("treated");
|
|
432
|
|
- standards.add(s3);
|
|
433
|
|
-
|
|
434
|
|
- // 余氯 - 管网 0.05~2.0
|
|
435
|
|
- QualityStandard s3n = new QualityStandard();
|
|
436
|
|
- s3n.setParamName("residual_chlorine"); s3n.setMinValue(new BigDecimal("0.05"));
|
|
437
|
|
- s3n.setMaxValue(new BigDecimal("2.0")); s3n.setWaterType("network");
|
|
438
|
|
- standards.add(s3n);
|
|
439
|
|
-
|
|
440
|
|
- // 色度 ≤15
|
|
441
|
|
- QualityStandard s4 = new QualityStandard();
|
|
442
|
|
- s4.setParamName("color"); s4.setMinValue(null); s4.setMaxValue(new BigDecimal("15"));
|
|
443
|
|
- s4.setWaterType("all");
|
|
444
|
|
- standards.add(s4);
|
|
445
|
|
-
|
|
446
|
|
- // 嗅味 ≤2
|
|
447
|
|
- QualityStandard s5 = new QualityStandard();
|
|
448
|
|
- s5.setParamName("odor"); s5.setMinValue(null); s5.setMaxValue(new BigDecimal("2"));
|
|
449
|
|
- s5.setWaterType("all");
|
|
450
|
|
- standards.add(s5);
|
|
451
|
|
-
|
|
452
|
|
- // 大肠杆菌 =0
|
|
453
|
|
- QualityStandard s6 = new QualityStandard();
|
|
454
|
|
- s6.setParamName("ecoli"); s6.setMinValue(null); s6.setMaxValue(BigDecimal.ZERO);
|
|
455
|
|
- s6.setWaterType("all");
|
|
456
|
|
- standards.add(s6);
|
|
457
|
|
-
|
|
458
|
|
- // 菌落总数 ≤100
|
|
459
|
|
- QualityStandard s7 = new QualityStandard();
|
|
460
|
|
- s7.setParamName("colony_count"); s7.setMinValue(null); s7.setMaxValue(new BigDecimal("100"));
|
|
461
|
|
- s7.setWaterType("all");
|
|
462
|
|
- standards.add(s7);
|
|
463
|
|
-
|
|
464
|
|
- return standards;
|
|
465
|
|
- }
|
|
|
323
|
+ List<String> areas = ledgerService.getAreaList();
|
|
466
|
324
|
|
|
467
|
|
- /**
|
|
468
|
|
- * 模拟合格判定逻辑 (不依赖 Spring 容器)
|
|
469
|
|
- */
|
|
470
|
|
- private List<String> evaluateCompliance(QualityTestRecord record, List<QualityStandard> standards) {
|
|
471
|
|
- String waterType = record.getWaterType();
|
|
472
|
|
- if (waterType == null) waterType = "treated";
|
|
473
|
|
-
|
|
474
|
|
- List<String> unqualified = new ArrayList<>();
|
|
475
|
|
- String wt = waterType;
|
|
476
|
|
-
|
|
477
|
|
- checkParam("turbidity", record.getTurbidity(), wt, standards, unqualified);
|
|
478
|
|
- checkParam("ph", record.getPh(), wt, standards, unqualified);
|
|
479
|
|
- checkParam("residual_chlorine", record.getResidualChlorine(), wt, standards, unqualified);
|
|
480
|
|
- checkParam("color", record.getColor(), wt, standards, unqualified);
|
|
481
|
|
- checkParam("odor", record.getOdor(), wt, standards, unqualified);
|
|
482
|
|
- checkParam("ecoli", record.getEcoli(), wt, standards, unqualified);
|
|
483
|
|
- checkParam("colony_count", record.getColonyCount(), wt, standards, unqualified);
|
|
484
|
|
-
|
|
485
|
|
- return unqualified;
|
|
|
325
|
+ assertNotNull(areas);
|
|
|
326
|
+ assertEquals(2, areas.size());
|
|
|
327
|
+ assertTrue(areas.contains("城区A"));
|
|
|
328
|
+ assertTrue(areas.contains("城区B"));
|
|
486
|
329
|
}
|
|
487
|
330
|
|
|
488
|
|
- private void checkParam(String paramName, BigDecimal value, String waterType,
|
|
489
|
|
- List<QualityStandard> standards, List<String> unqualified) {
|
|
490
|
|
- if (value == null) return;
|
|
|
331
|
+ @Test
|
|
|
332
|
+ @DisplayName("16. 获取采样点列表")
|
|
|
333
|
+ void testGetSamplingPointList() {
|
|
|
334
|
+ QualityTestRecord r1 = new QualityTestRecord();
|
|
|
335
|
+ r1.setSamplingPoint("采样点A");
|
|
|
336
|
+ QualityTestRecord r2 = new QualityTestRecord();
|
|
|
337
|
+ r2.setSamplingPoint("采样点B");
|
|
|
338
|
+ when(recordMapper.selectList(any())).thenReturn(List.of(r1, r2));
|
|
491
|
339
|
|
|
492
|
|
- QualityStandard standard = standards.stream()
|
|
493
|
|
- .filter(s -> paramName.equals(s.getParamName()))
|
|
494
|
|
- .filter(s -> waterType.equals(s.getWaterType()) || "all".equals(s.getWaterType()))
|
|
495
|
|
- .findFirst().orElse(null);
|
|
|
340
|
+ List<String> points = ledgerService.getSamplingPointList();
|
|
496
|
341
|
|
|
497
|
|
- if (standard == null) return;
|
|
|
342
|
+ assertNotNull(points);
|
|
|
343
|
+ assertEquals(2, points.size());
|
|
|
344
|
+ }
|
|
498
|
345
|
|
|
499
|
|
- boolean isUnqualified = false;
|
|
500
|
|
- if (standard.getMinValue() != null && value.compareTo(standard.getMinValue()) < 0) {
|
|
501
|
|
- isUnqualified = true;
|
|
502
|
|
- }
|
|
503
|
|
- if (standard.getMaxValue() != null && value.compareTo(standard.getMaxValue()) > 0) {
|
|
504
|
|
- isUnqualified = true;
|
|
505
|
|
- }
|
|
|
346
|
+ // ==================== 计划测试 ====================
|
|
506
|
347
|
|
|
507
|
|
- if (isUnqualified) {
|
|
508
|
|
- unqualified.add("{\"param\":\"" + paramName + "\"}");
|
|
509
|
|
- }
|
|
510
|
|
- }
|
|
|
348
|
+ @Test
|
|
|
349
|
+ @DisplayName("17. 创建检测计划")
|
|
|
350
|
+ void testCreatePlan() {
|
|
|
351
|
+ QualityTestPlan plan = buildPlan();
|
|
|
352
|
+ when(planMapper.insert(any())).thenReturn(1);
|
|
|
353
|
+
|
|
|
354
|
+ QualityTestPlan result = planService.create(plan);
|
|
511
|
355
|
|
|
512
|
|
- private LocalDate calculateNextDate(LocalDate current, String frequency) {
|
|
513
|
|
- return switch (frequency) {
|
|
514
|
|
- case "daily" -> current.plusDays(1);
|
|
515
|
|
- case "weekly" -> current.plusWeeks(1);
|
|
516
|
|
- case "monthly" -> current.plusMonths(1);
|
|
517
|
|
- default -> current;
|
|
518
|
|
- };
|
|
|
356
|
+ assertEquals("active", result.getStatus());
|
|
|
357
|
+ assertEquals(0, result.getExecutionCount());
|
|
|
358
|
+ verify(planMapper).insert(plan);
|
|
519
|
359
|
}
|
|
520
|
360
|
|
|
521
|
|
- private List<QualityTestRecord> buildMockRecords() {
|
|
522
|
|
- List<QualityTestRecord> records = new ArrayList<>();
|
|
|
361
|
+ @Test
|
|
|
362
|
+ @DisplayName("18. 标记计划已执行-日检+1天")
|
|
|
363
|
+ void testMarkPlanExecuted() {
|
|
|
364
|
+ QualityTestPlan plan = buildPlan();
|
|
|
365
|
+ plan.setId(1L);
|
|
|
366
|
+ plan.setNextTestDate(LocalDate.of(2026, 6, 14));
|
|
|
367
|
+ plan.setExecutionCount(5);
|
|
|
368
|
+ when(planMapper.selectById(1L)).thenReturn(plan);
|
|
|
369
|
+ when(planMapper.updateById(any())).thenReturn(1);
|
|
523
|
370
|
|
|
524
|
|
- QualityTestRecord r1 = new QualityTestRecord();
|
|
525
|
|
- r1.setId(1L); r1.setWaterType("treated"); r1.setArea("一体化水厂");
|
|
526
|
|
- r1.setSamplingPoint("出厂水口"); r1.setComplianceStatus("qualified");
|
|
527
|
|
- r1.setTestDate(LocalDate.of(2026, 6, 14));
|
|
528
|
|
- records.add(r1);
|
|
|
371
|
+ QualityTestPlan result = planService.markExecuted(1L);
|
|
529
|
372
|
|
|
530
|
|
- QualityTestRecord r2 = new QualityTestRecord();
|
|
531
|
|
- r2.setId(2L); r2.setWaterType("treated"); r2.setArea("一体化水厂");
|
|
532
|
|
- r2.setSamplingPoint("出厂水口"); r2.setComplianceStatus("qualified");
|
|
533
|
|
- r2.setTestDate(LocalDate.of(2026, 6, 13));
|
|
534
|
|
- records.add(r2);
|
|
535
|
|
-
|
|
536
|
|
- QualityTestRecord r3 = new QualityTestRecord();
|
|
537
|
|
- r3.setId(3L); r3.setWaterType("network"); r3.setArea("管网一区");
|
|
538
|
|
- r3.setSamplingPoint("末梢点A"); r3.setComplianceStatus("unqualified");
|
|
539
|
|
- r3.setTestDate(LocalDate.of(2026, 6, 14));
|
|
540
|
|
- records.add(r3);
|
|
541
|
|
-
|
|
542
|
|
- QualityTestRecord r4 = new QualityTestRecord();
|
|
543
|
|
- r4.setId(4L); r4.setWaterType("treated"); r4.setArea("二水厂");
|
|
544
|
|
- r4.setSamplingPoint("出厂水口"); r4.setComplianceStatus("qualified");
|
|
545
|
|
- r4.setTestDate(LocalDate.of(2026, 6, 12));
|
|
546
|
|
- records.add(r4);
|
|
547
|
|
-
|
|
548
|
|
- QualityTestRecord r5 = new QualityTestRecord();
|
|
549
|
|
- r5.setId(5L); r5.setWaterType("network"); r5.setArea("管网一区");
|
|
550
|
|
- r5.setSamplingPoint("末梢点B"); r5.setComplianceStatus("unqualified");
|
|
551
|
|
- r5.setTestDate(LocalDate.of(2026, 6, 11));
|
|
552
|
|
- records.add(r5);
|
|
553
|
|
-
|
|
554
|
|
- return records;
|
|
|
373
|
+ assertEquals(6, result.getExecutionCount());
|
|
|
374
|
+ assertEquals(LocalDate.of(2026, 6, 15), result.getNextTestDate());
|
|
555
|
375
|
}
|
|
556
|
376
|
|
|
557
|
|
- private String formatTestType(String type) {
|
|
558
|
|
- if (type == null) return "";
|
|
559
|
|
- return switch (type) {
|
|
560
|
|
- case "routine" -> "常规检测";
|
|
561
|
|
- case "special" -> "专项检测";
|
|
562
|
|
- case "complaint" -> "投诉检测";
|
|
563
|
|
- default -> type;
|
|
564
|
|
- };
|
|
565
|
|
- }
|
|
|
377
|
+ // ==================== 辅助方法 ====================
|
|
566
|
378
|
|
|
567
|
|
- private String formatWaterType(String type) {
|
|
568
|
|
- if (type == null) return "";
|
|
569
|
|
- return switch (type) {
|
|
570
|
|
- case "raw" -> "原水";
|
|
571
|
|
- case "treated" -> "出厂水";
|
|
572
|
|
- case "network" -> "管网末梢水";
|
|
573
|
|
- default -> type;
|
|
574
|
|
- };
|
|
|
379
|
+ private QualityStandard buildStandard(String paramName, Double min, Double max) {
|
|
|
380
|
+ QualityStandard s = new QualityStandard();
|
|
|
381
|
+ s.setParamName(paramName);
|
|
|
382
|
+ s.setMinValue(min);
|
|
|
383
|
+ s.setMaxValue(max);
|
|
|
384
|
+ s.setEnabled(true);
|
|
|
385
|
+ return s;
|
|
575
|
386
|
}
|
|
576
|
387
|
|
|
577
|
|
- private String formatCompliance(String status) {
|
|
578
|
|
- if (status == null) return "待判定";
|
|
579
|
|
- return switch (status) {
|
|
580
|
|
- case "qualified" -> "合格";
|
|
581
|
|
- case "unqualified" -> "不合格";
|
|
582
|
|
- case "pending" -> "待判定";
|
|
583
|
|
- default -> status;
|
|
584
|
|
- };
|
|
|
388
|
+ private QualityTestPlan buildPlan() {
|
|
|
389
|
+ QualityTestPlan plan = new QualityTestPlan();
|
|
|
390
|
+ plan.setPlanName("日检计划");
|
|
|
391
|
+ plan.setTestType("routine");
|
|
|
392
|
+ plan.setWaterType("factoryWater");
|
|
|
393
|
+ plan.setSamplingPoint("出厂水采样点A");
|
|
|
394
|
+ plan.setArea("城区A");
|
|
|
395
|
+ plan.setFrequency("daily");
|
|
|
396
|
+ plan.setStartDate(LocalDate.now());
|
|
|
397
|
+ return plan;
|
|
585
|
398
|
}
|
|
586
|
399
|
}
|