Procházet zdrojové kódy

fix(issue-15): 修复巡检管理系统7类代码质量问题

A. 表名不一致(致命): PatrolCoreService/PatrolWoService/PatrolService使用patrol_*前缀但DDL定义pat_*前缀
   - PatrolCoreService: patrol_task→pat_task, patrol_work_order→pat_work_order, patrol_track_point→pat_track_point, patrol_device→pat_device, patrol_route→pat_route_setup
   - PatrolWoService: patrol_work_order→pat_work_order
   - PatrolService: patrol_route→pat_route_setup, patrol_task→pat_task, patrol_record→pat_record
   - PatrolProblemMapper.xml: patrol_problem→pat_problem
   - WorkOrderMapper.xml: work_order→pat_wo_detail
   - WorkOrderProcessMapper.xml: work_order_process→pat_wo_process

B. 缺失DDL表(致命): 新增V89__patrol_fix.sql
   - pat_record(巡检记录), pat_problem(巡检问题), pat_wo_detail(工单详情), pat_wo_process(工单处理记录)
   - pat_task补列: task_date, assignee_id, plan_start, plan_end, actual_start, actual_end
   - pat_route_setup补列: route_points, estim_duration, area
   - pat_track_point补列: recorded_at; pat_device补列: area
   - 创建序列: seq_patrol_problem, seq_pat_wo_detail

C. WorkOrderServiceImpl NPE Bug(高): selectByProblemId().setWorkOrderId()不持久化且可能NPE
   - 改为注入PatrolProblemMapper并调用updateStatus(id, status, workOrderId)

D. WorkOrderMapper.xml updateStatus(高): 引用#{updatedAt}但接口未传该参数
   - 改为NOW(); 同理修复updateAssignee/updateCompletion/PatrolProblemMapper.xml updateStatus

E. PatrolApplication缺@MapperScan(中): 添加@MapperScan('com.water.patrol.mapper')

F. 误放测试文件(中): 删除src/main/java/patrol/下2个无效测试(引用不存在的PatrolManager类)

G. 缺测试(中): 新增PatrolProblemWorkOrderTest(20个测试) + PatrolCoreWoServiceTest(25个测试)
   覆盖: ProblemService CRUD/统计/自动工单, WorkOrderService创建/分派/处理/完成/统计,
   PatrolCoreService总览/轨迹/台账/设备, PatrolWoService CRUD/列表/统计,
   PatrolService路线/任务/记录/问题上报/统计, PatrolOverviewService总览/月报

H. PatrolService.getRoutes: status=1(int)改为status='active'(varchar)对齐DDL
bot_qa před 2 dny
rodič
revize
b022c21a26

+ 2
- 0
wm-patrol/src/main/java/com/water/patrol/PatrolApplication.java Zobrazit soubor

@@ -1,9 +1,11 @@
1 1
 package com.water.patrol;
2 2
 
3
+import org.mybatis.spring.annotation.MapperScan;
3 4
 import org.springframework.boot.SpringApplication;
4 5
 import org.springframework.boot.autoconfigure.SpringBootApplication;
5 6
 
6 7
 @SpringBootApplication
8
+@MapperScan("com.water.patrol.mapper")
7 9
 public class PatrolApplication {
8 10
     public static void main(String[] args) {
9 11
         SpringApplication.run(PatrolApplication.class, args);

+ 17
- 17
wm-patrol/src/main/java/com/water/patrol/service/PatrolCoreService.java Zobrazit soubor

@@ -26,13 +26,13 @@ public class PatrolCoreService {
26 26
         LocalDate today = LocalDate.now();
27 27
         LocalDate monthStart = today.withDayOfMonth(1);
28 28
 
29
-        long todayTotal = countQuery("SELECT COUNT(*) FROM patrol_task WHERE task_date = ?", today);
30
-        long todayCompleted = countQuery("SELECT COUNT(*) FROM patrol_task WHERE task_date = ? AND status = 'completed'", today);
31
-        long monthTotal = countQuery("SELECT COUNT(*) FROM patrol_task WHERE task_date >= ?", monthStart);
32
-        long monthCompleted = countQuery("SELECT COUNT(*) FROM patrol_task WHERE task_date >= ? AND status = 'completed'", monthStart);
29
+        long todayTotal = countQuery("SELECT COUNT(*) FROM pat_task WHERE task_date = ?", today);
30
+        long todayCompleted = countQuery("SELECT COUNT(*) FROM pat_task WHERE task_date = ? AND status = 'completed'", today);
31
+        long monthTotal = countQuery("SELECT COUNT(*) FROM pat_task WHERE task_date >= ?", monthStart);
32
+        long monthCompleted = countQuery("SELECT COUNT(*) FROM pat_task WHERE task_date >= ? AND status = 'completed'", monthStart);
33 33
 
34
-        long pendingIssues = countQuery("SELECT COUNT(*) FROM patrol_work_order WHERE status IN ('pending','assigned','processing')");
35
-        long resolvedIssues = countQuery("SELECT COUNT(*) FROM patrol_work_order WHERE status = 'resolved'");
34
+        long pendingIssues = countQuery("SELECT COUNT(*) FROM pat_work_order WHERE status IN ('pending','assigned','processing')");
35
+        long resolvedIssues = countQuery("SELECT COUNT(*) FROM pat_work_order WHERE status = 'resolved'");
36 36
 
37 37
         long coverageRate = monthTotal > 0 ? Math.round(monthCompleted * 100.0 / monthTotal) : 0;
38 38
 
@@ -49,14 +49,14 @@ public class PatrolCoreService {
49 49
 
50 50
     public List<Map<String, Object>> getTodayTasks() {
51 51
         return jdbc.queryForList(
52
-            "SELECT pt.*, pr.route_name, pr.area FROM patrol_task pt " +
53
-            "LEFT JOIN patrol_route pr ON pt.route_id = pr.id " +
52
+            "SELECT pt.*, pr.route_name, pr.area FROM pat_task pt " +
53
+            "LEFT JOIN pat_route_setup pr ON pt.route_id = pr.id " +
54 54
             "WHERE pt.task_date = CURRENT_DATE ORDER BY pt.plan_start");
55 55
     }
56 56
 
57 57
     public List<Map<String, Object>> getRecentIssues(int limit) {
58 58
         return jdbc.queryForList(
59
-            "SELECT * FROM patrol_work_order ORDER BY created_at DESC LIMIT ?", limit);
59
+            "SELECT * FROM pat_work_order ORDER BY created_at DESC LIMIT ?", limit);
60 60
     }
61 61
 
62 62
     // ========== PAT-02 任务轨迹(GPS记录与回放) ==========
@@ -65,7 +65,7 @@ public class PatrolCoreService {
65 65
                                                  double lng, double lat,
66 66
                                                  double speed, double accuracy) {
67 67
         jdbc.update(
68
-            "INSERT INTO patrol_track_point (task_id, worker_id, lng, lat, speed, accuracy) " +
68
+            "INSERT INTO pat_track_point (task_id, worker_id, lng, lat, speed, accuracy) " +
69 69
             "VALUES (?,?,?,?,?,?)",
70 70
             taskId, workerId, lng, lat, speed, accuracy);
71 71
         Map<String, Object> result = new LinkedHashMap<>();
@@ -79,7 +79,7 @@ public class PatrolCoreService {
79 79
 
80 80
     public List<Map<String, Object>> getTrack(Long taskId) {
81 81
         return jdbc.queryForList(
82
-            "SELECT * FROM patrol_track_point WHERE task_id = ? ORDER BY recorded_at", taskId);
82
+            "SELECT * FROM pat_track_point WHERE task_id = ? ORDER BY recorded_at", taskId);
83 83
     }
84 84
 
85 85
     public Map<String, Object> replayTrack(Long taskId) {
@@ -130,7 +130,7 @@ public class PatrolCoreService {
130 130
     // ========== PAT-03 任务台账 ==========
131 131
 
132 132
     public Map<String, Object> getTaskLedger(String status, Long workerId, int page, int size) {
133
-        StringBuilder sql = new StringBuilder("SELECT * FROM patrol_task WHERE 1=1");
133
+        StringBuilder sql = new StringBuilder("SELECT * FROM pat_task WHERE 1=1");
134 134
         List<Object> params = new ArrayList<>();
135 135
         if (status != null && !status.isEmpty()) {
136 136
             sql.append(" AND status = ?");
@@ -146,7 +146,7 @@ public class PatrolCoreService {
146 146
 
147 147
         List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
148 148
 
149
-        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM patrol_task WHERE 1=1");
149
+        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM pat_task WHERE 1=1");
150 150
         List<Object> countParams = new ArrayList<>();
151 151
         if (status != null && !status.isEmpty()) {
152 152
             countSql.append(" AND status = ?");
@@ -170,7 +170,7 @@ public class PatrolCoreService {
170 170
 
171 171
     public Map<String, Object> getDeviceLedger(String deviceType, String status, String area,
172 172
                                                 int page, int size) {
173
-        StringBuilder sql = new StringBuilder("SELECT * FROM patrol_device WHERE 1=1");
173
+        StringBuilder sql = new StringBuilder("SELECT * FROM pat_device WHERE 1=1");
174 174
         List<Object> params = new ArrayList<>();
175 175
         if (deviceType != null && !deviceType.isEmpty()) {
176 176
             sql.append(" AND device_type = ?");
@@ -190,7 +190,7 @@ public class PatrolCoreService {
190 190
 
191 191
         List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
192 192
 
193
-        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM patrol_device WHERE 1=1");
193
+        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM pat_device WHERE 1=1");
194 194
         List<Object> countParams = new ArrayList<>();
195 195
         if (deviceType != null && !deviceType.isEmpty()) {
196 196
             countSql.append(" AND device_type = ?");
@@ -217,7 +217,7 @@ public class PatrolCoreService {
217 217
     public Map<String, Object> createDevice(String deviceNo, String deviceName, String deviceType,
218 218
                                              String location, double lng, double lat, String area) {
219 219
         jdbc.update(
220
-            "INSERT INTO patrol_device (device_no, device_name, device_type, location, lng, lat, area) " +
220
+            "INSERT INTO pat_device (device_no, device_name, device_type, location, lng, lat, area) " +
221 221
             "VALUES (?,?,?,?,?,?,?)",
222 222
             deviceNo, deviceName, deviceType, location, lng, lat, area);
223 223
         Map<String, Object> result = new LinkedHashMap<>();
@@ -228,7 +228,7 @@ public class PatrolCoreService {
228 228
     }
229 229
 
230 230
     public Map<String, Object> updateDeviceStatus(Long deviceId, String status) {
231
-        jdbc.update("UPDATE patrol_device SET status = ?, updated_at = NOW() WHERE id = ?",
231
+        jdbc.update("UPDATE pat_device SET status = ?, updated_at = NOW() WHERE id = ?",
232 232
             status, deviceId);
233 233
         return Map.of("deviceId", deviceId, "status", status, "updated", true);
234 234
     }

+ 16
- 16
wm-patrol/src/main/java/com/water/patrol/service/PatrolService.java Zobrazit soubor

@@ -17,41 +17,41 @@ public class PatrolService {
17 17
 
18 18
     // ========== 路线管理 ==========
19 19
     public Map<String, Object> createRoute(String routeName, String area, List<Map<String, Object>> points, int estimDuration) {
20
-        jdbc.update("INSERT INTO patrol_route (route_name, area, route_points, estim_duration) VALUES (?,?,?::jsonb,?)",
20
+        jdbc.update("INSERT INTO pat_route_setup (route_name, area, route_points, estim_duration) VALUES (?,?,?::jsonb,?)",
21 21
             routeName, area, points.toString(), estimDuration);
22 22
         return Map.of("routeName", routeName, "area", area, "points", points.size());
23 23
     }
24 24
 
25 25
     public List<Map<String, Object>> getRoutes(String area) {
26
-        return jdbc.queryForList("SELECT * FROM patrol_route WHERE area = ? AND status = 1", area);
26
+        return jdbc.queryForList("SELECT * FROM pat_route_setup WHERE area = ? AND status = 'active'", area);
27 27
     }
28 28
 
29 29
     // ========== 任务管理 ==========
30 30
     public Map<String, Object> createTask(Long routeId, Long assigneeId, String taskDate) {
31 31
         jdbc.update(
32
-            "INSERT INTO patrol_task (route_id, assignee_id, task_name, task_date, plan_start, plan_end, status) " +
32
+            "INSERT INTO pat_task (route_id, assignee_id, task_name, task_date, plan_start, plan_end, status) " +
33 33
             "SELECT ?, ?, route_name, ?, CAST(? AS TIMESTAMP), CAST(? AS TIMESTAMP) + (estim_duration || ' minutes')::INTERVAL, 'pending' " +
34
-            "FROM patrol_route WHERE id = ?",
34
+            "FROM pat_route_setup WHERE id = ?",
35 35
             routeId, assigneeId, taskDate, taskDate + " 09:00:00", taskDate + " 09:00:00", routeId);
36 36
         return Map.of("routeId", routeId, "assigneeId", assigneeId, "date", taskDate, "status", "created");
37 37
     }
38 38
 
39 39
     public List<Map<String, Object>> getTodayTasks(Long userId) {
40 40
         return jdbc.queryForList(
41
-            "SELECT pt.*, pr.route_name, pr.area FROM patrol_task pt " +
42
-            "LEFT JOIN patrol_route pr ON pt.route_id = pr.id " +
41
+            "SELECT pt.*, pr.route_name, pr.area FROM pat_task pt " +
42
+            "LEFT JOIN pat_route_setup pr ON pt.route_id = pr.id " +
43 43
             "WHERE pt.task_date = CURRENT_DATE AND pt.assignee_id = ? " +
44 44
             "ORDER BY pt.plan_start", userId);
45 45
     }
46 46
 
47 47
     public Map<String, Object> startTask(Long taskId) {
48
-        jdbc.update("UPDATE patrol_task SET status = 'in_progress', actual_start = NOW() WHERE id = ?", taskId);
48
+        jdbc.update("UPDATE pat_task SET status = 'in_progress', actual_start = NOW() WHERE id = ?", taskId);
49 49
         return Map.of("taskId", taskId, "status", "in_progress", "startedAt", new Date());
50 50
     }
51 51
 
52 52
     public Map<String, Object> completeTask(Long taskId, double distance) {
53 53
         jdbc.update(
54
-            "UPDATE patrol_task SET status = 'completed', actual_end = NOW(), distance = ? WHERE id = ?",
54
+            "UPDATE pat_task SET status = 'completed', actual_end = NOW(), distance = ? WHERE id = ?",
55 55
             distance, taskId);
56 56
         return Map.of("taskId", taskId, "status", "completed", "distance", distance);
57 57
     }
@@ -61,7 +61,7 @@ public class PatrolService {
61 61
                                             List<Map<String, Object>> checkItems,
62 62
                                             double lng, double lat) {
63 63
         jdbc.update(
64
-            "INSERT INTO patrol_record (task_id, point_seq, device_id, check_items, gps_lng, gps_lat, record_time) " +
64
+            "INSERT INTO pat_record (task_id, point_seq, device_id, check_items, gps_lng, gps_lat, record_time) " +
65 65
             "VALUES (?,?,?,?::jsonb,?,?,NOW())",
66 66
             taskId, pointSeq, deviceId, checkItems.toString(), lng, lat);
67 67
         return Map.of("taskId", taskId, "pointSeq", pointSeq, "recorded", true);
@@ -69,7 +69,7 @@ public class PatrolService {
69 69
 
70 70
     public List<Map<String, Object>> getTaskRecords(Long taskId) {
71 71
         return jdbc.queryForList(
72
-            "SELECT * FROM patrol_record WHERE task_id = ? ORDER BY point_seq", taskId);
72
+            "SELECT * FROM pat_record WHERE task_id = ? ORDER BY point_seq", taskId);
73 73
     }
74 74
 
75 75
     // ========== 问题上报(巡检APP) ==========
@@ -78,8 +78,8 @@ public class PatrolService {
78 78
                                             double lng, double lat) {
79 79
         // 自动创建工单
80 80
         jdbc.update(
81
-            "INSERT INTO patrol_task (task_name, assignee_id, task_date, status) " +
82
-            "SELECT CONCAT('问题处理: ', ?), assignee_id, CURRENT_DATE, 'pending' FROM patrol_task WHERE id = ?",
81
+            "INSERT INTO pat_task (task_name, assignee_id, task_date, status) " +
82
+            "SELECT CONCAT('问题处理: ', ?), assignee_id, CURRENT_DATE, 'pending' FROM pat_task WHERE id = ?",
83 83
             issueType + ": " + description.substring(0, Math.min(description.length(), 50)), taskId);
84 84
 
85 85
         log.info("Issue reported: type={} desc={}", issueType, description);
@@ -93,24 +93,24 @@ public class PatrolService {
93 93
         // 任务执行率
94 94
         stats.put("completionRate", jdbc.queryForMap(
95 95
             "SELECT COUNT(*) as total, SUM(CASE WHEN status='completed' THEN 1 ELSE 0 END) as completed " +
96
-            "FROM patrol_task WHERE task_date BETWEEN ? AND ?", start, end));
96
+            "FROM pat_task WHERE task_date BETWEEN ? AND ?", start, end));
97 97
 
98 98
         // 人员里程
99 99
         stats.put("personDistance", jdbc.queryForList(
100 100
             "SELECT u.real_name, SUM(pt.distance) as total_km " +
101
-            "FROM patrol_task pt JOIN sys_user u ON pt.assignee_id = u.id " +
101
+            "FROM pat_task pt JOIN sys_user u ON pt.assignee_id = u.id " +
102 102
             "WHERE pt.task_date BETWEEN ? AND ? GROUP BY u.id, u.real_name", start, end));
103 103
 
104 104
         // 巡检工作量
105 105
         stats.put("workload", jdbc.queryForList(
106 106
             "SELECT task_date, COUNT(*) as tasks, SUM(distance) as total_km " +
107
-            "FROM patrol_task WHERE task_date BETWEEN ? AND ? GROUP BY task_date ORDER BY task_date",
107
+            "FROM pat_task WHERE task_date BETWEEN ? AND ? GROUP BY task_date ORDER BY task_date",
108 108
             start, end));
109 109
 
110 110
         // 问题分类统计
111 111
         stats.put("issueStats", jdbc.queryForList(
112 112
             "SELECT SUBSTRING(task_name FROM '^[^:]+') as issue_type, COUNT(*) as count " +
113
-            "FROM patrol_task WHERE task_name LIKE '%问题处理:%' AND task_date BETWEEN ? AND ? GROUP BY 1",
113
+            "FROM pat_task WHERE task_name LIKE '%问题处理:%' AND task_date BETWEEN ? AND ? GROUP BY 1",
114 114
             start, end));
115 115
 
116 116
         return stats;

+ 8
- 8
wm-patrol/src/main/java/com/water/patrol/service/PatrolWoService.java Zobrazit soubor

@@ -23,7 +23,7 @@ public class PatrolWoService {
23 23
                                        String location, Double lng, Double lat, Long taskId) {
24 24
         String orderNo = "PWO-" + System.currentTimeMillis();
25 25
         jdbc.update(
26
-            "INSERT INTO patrol_work_order (order_no, task_id, issue_type, description, severity, " +
26
+            "INSERT INTO pat_work_order (order_no, task_id, issue_type, description, severity, " +
27 27
             "location, lng, lat, status) VALUES (?,?,?,?,?,?,?,?,?)",
28 28
             orderNo, taskId, issueType, description,
29 29
             severity != null ? severity : "medium",
@@ -39,7 +39,7 @@ public class PatrolWoService {
39 39
     /** 分派工单 */
40 40
     public Map<String, Object> assign(Long woId, Long assigneeId, String assigneeName) {
41 41
         int rows = jdbc.update(
42
-            "UPDATE patrol_work_order SET assignee_id = ?, assignee_name = ?, status = 'assigned' " +
42
+            "UPDATE pat_work_order SET assignee_id = ?, assignee_name = ?, status = 'assigned' " +
43 43
             "WHERE id = ?", assigneeId, assigneeName, woId);
44 44
         return Map.of("success", rows > 0, "woId", woId);
45 45
     }
@@ -47,7 +47,7 @@ public class PatrolWoService {
47 47
     /** 处理工单 */
48 48
     public Map<String, Object> process(Long woId, String resolution) {
49 49
         int rows = jdbc.update(
50
-            "UPDATE patrol_work_order SET status = 'processing', resolution = ? WHERE id = ?",
50
+            "UPDATE pat_work_order SET status = 'processing', resolution = ? WHERE id = ?",
51 51
             resolution, woId);
52 52
         return Map.of("success", rows > 0, "woId", woId);
53 53
     }
@@ -55,20 +55,20 @@ public class PatrolWoService {
55 55
     /** 解决工单 */
56 56
     public Map<String, Object> resolve(Long woId) {
57 57
         int rows = jdbc.update(
58
-            "UPDATE patrol_work_order SET status = 'resolved', resolved_at = NOW() WHERE id = ?", woId);
58
+            "UPDATE pat_work_order SET status = 'resolved', resolved_at = NOW() WHERE id = ?", woId);
59 59
         return Map.of("success", rows > 0, "woId", woId);
60 60
     }
61 61
 
62 62
     /** 工单详情 */
63 63
     public Map<String, Object> getDetail(Long woId) {
64 64
         List<Map<String, Object>> rows = jdbc.queryForList(
65
-            "SELECT * FROM patrol_work_order WHERE id = ?", woId);
65
+            "SELECT * FROM pat_work_order WHERE id = ?", woId);
66 66
         return rows.isEmpty() ? null : rows.get(0);
67 67
     }
68 68
 
69 69
     /** 工单列表 */
70 70
     public Map<String, Object> list(String status, String severity, int page, int size) {
71
-        StringBuilder sql = new StringBuilder("SELECT * FROM patrol_work_order WHERE 1=1");
71
+        StringBuilder sql = new StringBuilder("SELECT * FROM pat_work_order WHERE 1=1");
72 72
         List<Object> params = new ArrayList<>();
73 73
         if (status != null && !status.isEmpty()) {
74 74
             sql.append(" AND status = ?");
@@ -84,7 +84,7 @@ public class PatrolWoService {
84 84
 
85 85
         List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
86 86
 
87
-        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM patrol_work_order WHERE 1=1");
87
+        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM pat_work_order WHERE 1=1");
88 88
         List<Object> countParams = new ArrayList<>();
89 89
         if (status != null && !status.isEmpty()) {
90 90
             countSql.append(" AND status = ?");
@@ -107,7 +107,7 @@ public class PatrolWoService {
107 107
     /** 工单统计 */
108 108
     public Map<String, Object> stats() {
109 109
         List<Map<String, Object>> rows = jdbc.queryForList(
110
-            "SELECT status, COUNT(*) as count FROM patrol_work_order GROUP BY status");
110
+            "SELECT status, COUNT(*) as count FROM pat_work_order GROUP BY status");
111 111
         Map<String, Object> result = new LinkedHashMap<>();
112 112
         for (Map<String, Object> row : rows) {
113 113
             result.put(String.valueOf(row.get("status")), ((Number) row.get("count")).longValue());

+ 6
- 2
wm-patrol/src/main/java/com/water/patrol/service/impl/WorkOrderServiceImpl.java Zobrazit soubor

@@ -3,6 +3,7 @@ package com.water.patrol.service.impl;
3 3
 import com.water.patrol.entity.PatrolProblem;
4 4
 import com.water.patrol.entity.WorkOrder;
5 5
 import com.water.patrol.entity.WorkOrderProcess;
6
+import com.water.patrol.mapper.PatrolProblemMapper;
6 7
 import com.water.patrol.mapper.WorkOrderMapper;
7 8
 import com.water.patrol.mapper.WorkOrderProcessMapper;
8 9
 import com.water.patrol.service.WorkOrderService;
@@ -22,6 +23,9 @@ public class WorkOrderServiceImpl implements WorkOrderService {
22 23
     @Autowired
23 24
     private WorkOrderProcessMapper workOrderProcessMapper;
24 25
 
26
+    @Autowired
27
+    private PatrolProblemMapper patrolProblemMapper;
28
+
25 29
     @Override
26 30
     @Transactional
27 31
     public boolean createFromProblem(PatrolProblem problem) {
@@ -55,8 +59,8 @@ public class WorkOrderServiceImpl implements WorkOrderService {
55 59
             process.setCreatedAt(LocalDateTime.now());
56 60
             workOrderProcessMapper.insert(process);
57 61
             
58
-            // 更新问题状态
59
-            workOrderMapper.selectByProblemId(problem.getId()).setWorkOrderId(workOrder.getId());
62
+            // 更新问题状态 - 关联工单ID
63
+            patrolProblemMapper.updateStatus(problem.getId(), "processing", workOrder.getId());
60 64
             return true;
61 65
         }
62 66
         

+ 0
- 40
wm-patrol/src/main/java/patrol/PatrolCoreTest.java Zobrazit soubor

@@ -1,40 +0,0 @@
1
-package com.water.patrol;
2
-
3
-import org.junit.jupiter.api.Test;
4
-import org.springframework.boot.test.context.SpringBootTest;
5
-import org.springframework.beans.factory.annotation.Autowired;
6
-import static org.junit.jupiter.api.Assertions.*;
7
-
8
-/**
9
- * 巡检管理核心流程测试
10
- */
11
-@SpringBootTest
12
-public class PatrolCoreTest {
13
-
14
-    @Autowired
15
-    private PatrolCoreService patrolCoreService;
16
-
17
-    @Test
18
-    public void testPatrolTaskCreation() {
19
-        // 测试巡检任务创建
20
-        assertTrue(true, "巡检任务创建测试通过");
21
-    }
22
-
23
-    @Test
24
-    public void testPatrolTaskExecution() {
25
-        // 测试巡检任务执行
26
-        assertTrue(true, "巡检任务执行测试通过");
27
-    }
28
-
29
-    @Test
30
-    public void testPatrolTaskReporting() {
31
-        // 测试巡检任务上报
32
-        assertTrue(true, "巡检任务上报测试通过");
33
-    }
34
-
35
-    @Test
36
-    public void testPatrolRouteOptimization() {
37
-        // 测试巡检路线优化
38
-        assertTrue(true, "巡检路线优化测试通过");
39
-    }
40
-}

+ 0
- 135
wm-patrol/src/main/java/patrol/src/test/java/com/water/patrol/PatrolCoreTest.java Zobrazit soubor

@@ -1,135 +0,0 @@
1
-package com.water.patrol;
2
-
3
-import org.junit.jupiter.api.Test;
4
-import org.junit.jupiter.api.BeforeEach;
5
-import static org.junit.jupiter.api.Assertions.*;
6
-import java.util.List;
7
-import java.util.Map;
8
-
9
-/**
10
- * 巡检管理核心流程测试
11
- * 测试任务创建、执行、上报和流程管理
12
- */
13
-public class PatrolCoreTest {
14
-    
15
-    private PatrolManager patrolManager;
16
-    
17
-    @BeforeEach
18
-    void setUp() {
19
-        patrolManager = new PatrolManager();
20
-    }
21
-    
22
-    @Test
23
-    void testPatrolTaskCreation() {
24
-        Map<String, Object> taskData = Map.of(
25
-            "task_id", "patrol-001",
26
-            "task_name", "常规巡检",
27
-            "patrol_route", "route-001",
28
-            "assigned_user", "user-001",
29
-            "scheduled_time", "2026-06-16T10:00:00Z",
30
-            "priority", "high"
31
-        );
32
-        
33
-        String taskId = patrolManager.createTask(taskData);
34
-        assertNotNull(taskId, "任务创建应该返回ID");
35
-        assertFalse(taskId.isEmpty(), "ID不应该为空");
36
-    }
37
-    
38
-    @Test
39
-    void testTaskAssignment() {
40
-        // 先创建任务
41
-        Map<String, Object> taskData = Map.of(
42
-            "task_id", "patrol-002",
43
-            "task_name", "紧急巡检",
44
-            "patrol_route", "route-002"
45
-        );
46
-        String taskId = patrolManager.createTask(taskData);
47
-        
48
-        // 分配任务
49
-        boolean assigned = patrolManager.assignTask(taskId, "user-002");
50
-        assertTrue(assigned, "任务分配应该成功");
51
-        
52
-        // 验证分配状态
53
-        Map<String, Object> task = patrolManager.getTask(taskId);
54
-        assertEquals("user-002", task.get("assigned_user"));
55
-        assertEquals("assigned", task.get("status"));
56
-    }
57
-    
58
-    @Test
59
-    void testPatrolExecution() {
60
-        // 创建并分配任务
61
-        Map<String, Object> taskData = Map.of(
62
-            "task_id", "patrol-003",
63
-            "task_name", "执行巡检",
64
-            "patrol_route", "route-003"
65
-        );
66
-        String taskId = patrolManager.createTask(taskData);
67
-        patrolManager.assignTask(taskId, "user-003");
68
-        
69
-        // 执行巡检
70
-        Map<String, Object> executionData = Map.of(
71
-            "task_id", taskId,
72
-            "start_time", "2026-06-16T10:00:00Z",
73
-            "actual_route", "route-003",
74
-            "weather", "sunny"
75
-        );
76
-        
77
-        boolean executed = patrolManager.executePatrol(executionData);
78
-        assertTrue(executed, "巡检执行应该成功");
79
-        
80
-        // 验证执行状态
81
-        Map<String, Object> task = patrolManager.getTask(taskId);
82
-        assertEquals("in_progress", task.get("status"));
83
-    }
84
-    
85
-    @Test
86
-    void testPatrolReport() {
87
-        // 先执行巡检
88
-        Map<String, Object> taskData = Map.of(
89
-            "task_id", "patrol-004",
90
-            "task_name", "上报巡检"
91
-        );
92
-        String taskId = patrolManager.createTask(taskData);
93
-        patrolManager.assignTask(taskId, "user-004");
94
-        
95
-        Map<String, Object> executionData = Map.of(
96
-            "task_id", taskId,
97
-            "start_time", "2026-06-16T10:00:00Z"
98
-        );
99
-        patrolManager.executePatrol(executionData);
100
-        
101
-        // 上报巡检结果
102
-        Map<String, Object> reportData = Map.of(
103
-            "task_id", taskId,
104
-            "end_time", "2026-06-16T11:00:00Z",
105
-            "findings", List.of("设备正常", "无异常发现"),
106
-            "issues_found", 0,
107
-            "distance_covered", 5.2
108
-        );
109
-        
110
-        boolean reported = patrolManager.submitReport(reportData);
111
-        assertTrue(reported, "巡检上报应该成功");
112
-        
113
-        // 验证完成状态
114
-        Map<String, Object> task = patrolManager.getTask(taskId);
115
-        assertEquals("completed", task.get("status"));
116
-    }
117
-    
118
-    @Test
119
-    void testTaskPriority() {
120
-        Map<String, Object> highPriorityTask = Map.of(
121
-            "task_id", "patrol-005",
122
-            "task_name", "高优先级任务",
123
-            "priority", "high"
124
-        );
125
-        
126
-        String taskId = patrolManager.createTask(highPriorityTask);
127
-        Map<String, Object> task = patrolManager.getTask(taskId);
128
-        assertEquals("high", task.get("priority"));
129
-        
130
-        // 验证高优先级任务在队列中的位置
131
-        List<Map<String, Object>> queue = patrolManager.getTaskQueue();
132
-        assertTrue(queue.stream().anyMatch(t -> t.get("task_id").equals(taskId)), 
133
-                  "高优先级任务应该在队列中");
134
-    }
135
-}

+ 14
- 14
wm-patrol/src/main/resources/mapper/PatrolProblemMapper.xml Zobrazit soubor

@@ -27,7 +27,7 @@
27 27
     </resultMap>
28 28
 
29 29
     <insert id="insert" parameterType="com.water.patrol.entity.PatrolProblem">
30
-        INSERT INTO patrol_problem (
30
+        INSERT INTO pat_problem (
31 31
             problem_no, task_id, point_seq, device_id, device_name,
32 32
             problem_type, problem_level, problem_title, problem_description,
33 33
             location, lng, lat, photo_urls, reporter_id, reporter_name,
@@ -41,27 +41,27 @@
41 41
     </insert>
42 42
 
43 43
     <select id="selectById" resultMap="BaseResultMap">
44
-        SELECT * FROM patrol_problem WHERE id = #{id}
44
+        SELECT * FROM pat_problem WHERE id = #{id}
45 45
     </select>
46 46
 
47 47
     <select id="selectByProblemNo" resultMap="BaseResultMap">
48
-        SELECT * FROM patrol_problem WHERE problem_no = #{problemNo}
48
+        SELECT * FROM pat_problem WHERE problem_no = #{problemNo}
49 49
     </select>
50 50
 
51 51
     <select id="selectByTaskId" resultMap="BaseResultMap">
52
-        SELECT * FROM patrol_problem WHERE task_id = #{taskId} ORDER BY created_at DESC
52
+        SELECT * FROM pat_problem WHERE task_id = #{taskId} ORDER BY created_at DESC
53 53
     </select>
54 54
 
55 55
     <select id="selectByStatus" resultMap="BaseResultMap">
56
-        SELECT * FROM patrol_problem WHERE status = #{status} ORDER BY created_at DESC
56
+        SELECT * FROM pat_problem WHERE status = #{status} ORDER BY created_at DESC
57 57
     </select>
58 58
 
59 59
     <select id="selectByDeviceId" resultMap="BaseResultMap">
60
-        SELECT * FROM patrol_problem WHERE device_id = #{deviceId} ORDER BY created_at DESC
60
+        SELECT * FROM pat_problem WHERE device_id = #{deviceId} ORDER BY created_at DESC
61 61
     </select>
62 62
 
63 63
     <update id="update" parameterType="com.water.patrol.entity.PatrolProblem">
64
-        UPDATE patrol_problem SET
64
+        UPDATE pat_problem SET
65 65
             problem_type = #{problemType},
66 66
             problem_level = #{problemLevel},
67 67
             problem_title = #{problemTitle},
@@ -72,32 +72,32 @@
72 72
             photo_urls = #{photoUrls},
73 73
             status = #{status},
74 74
             work_order_id = #{workOrderId},
75
-            updated_at = #{updatedAt}
75
+            updated_at = NOW()
76 76
         WHERE id = #{id}
77 77
     </update>
78 78
 
79 79
     <update id="updateStatus">
80
-        UPDATE patrol_problem SET
80
+        UPDATE pat_problem SET
81 81
             status = #{status},
82 82
             work_order_id = #{workOrderId},
83
-            updated_at = #{updatedAt}
83
+            updated_at = NOW()
84 84
         WHERE id = #{id}
85 85
     </update>
86 86
 
87 87
     <delete id="deleteById">
88
-        DELETE FROM patrol_problem WHERE id = #{id}
88
+        DELETE FROM pat_problem WHERE id = #{id}
89 89
     </delete>
90 90
 
91 91
     <select id="generateProblemNo" resultType="string">
92
-        SELECT 'WQ' || to_char(NOW(), 'YYYY') || '-' || LPAD(nextval('seq_patrol_problem')::text, 3, '0')
92
+        SELECT 'WQ' || to_char(NOW(), 'YYYY') || '-' || LPAD(nextval('seq_pat_problem')::text, 3, '0')
93 93
     </select>
94 94
 
95 95
     <select id="countAll" resultType="int">
96
-        SELECT COUNT(*) FROM patrol_problem
96
+        SELECT COUNT(*) FROM pat_problem
97 97
     </select>
98 98
 
99 99
     <select id="countByStatus" resultType="int">
100
-        SELECT COUNT(*) FROM patrol_problem WHERE status = #{status}
100
+        SELECT COUNT(*) FROM pat_problem WHERE status = #{status}
101 101
     </select>
102 102
 
103 103
 </mapper>

+ 20
- 20
wm-patrol/src/main/resources/mapper/WorkOrderMapper.xml Zobrazit soubor

@@ -33,7 +33,7 @@
33 33
     </resultMap>
34 34
 
35 35
     <insert id="insert" parameterType="com.water.patrol.entity.WorkOrder">
36
-        INSERT INTO work_order (
36
+        INSERT INTO pat_wo_detail (
37 37
             order_no, problem_id, order_type, priority, title, description,
38 38
             location, contact_person, contact_phone, reporter_id, reporter_name,
39 39
             assignee_id, assignee_name, status, process_status,
@@ -51,31 +51,31 @@
51 51
     </insert>
52 52
 
53 53
     <select id="selectById" resultMap="BaseResultMap">
54
-        SELECT * FROM work_order WHERE id = #{id}
54
+        SELECT * FROM pat_wo_detail WHERE id = #{id}
55 55
     </select>
56 56
 
57 57
     <select id="selectByOrderNo" resultMap="BaseResultMap">
58
-        SELECT * FROM work_order WHERE order_no = #{orderNo}
58
+        SELECT * FROM pat_wo_detail WHERE order_no = #{orderNo}
59 59
     </select>
60 60
 
61 61
     <select id="selectByProblemId" resultMap="BaseResultMap">
62
-        SELECT * FROM work_order WHERE problem_id = #{problemId}
62
+        SELECT * FROM pat_wo_detail WHERE problem_id = #{problemId}
63 63
     </select>
64 64
 
65 65
     <select id="selectByStatus" resultMap="BaseResultMap">
66
-        SELECT * FROM work_order WHERE status = #{status} ORDER BY created_at DESC
66
+        SELECT * FROM pat_wo_detail WHERE status = #{status} ORDER BY created_at DESC
67 67
     </select>
68 68
 
69 69
     <select id="selectByAssigneeId" resultMap="BaseResultMap">
70
-        SELECT * FROM work_order WHERE assignee_id = #{assigneeId} ORDER BY created_at DESC
70
+        SELECT * FROM pat_wo_detail WHERE assignee_id = #{assigneeId} ORDER BY created_at DESC
71 71
     </select>
72 72
 
73 73
     <select id="selectByProcessStatus" resultMap="BaseResultMap">
74
-        SELECT * FROM work_order WHERE process_status = #{processStatus} ORDER BY created_at DESC
74
+        SELECT * FROM pat_wo_detail WHERE process_status = #{processStatus} ORDER BY created_at DESC
75 75
     </select>
76 76
 
77 77
     <update id="update" parameterType="com.water.patrol.entity.WorkOrder">
78
-        UPDATE work_order SET
78
+        UPDATE pat_wo_detail SET
79 79
             order_type = #{orderType},
80 80
             priority = #{priority},
81 81
             title = #{title},
@@ -96,53 +96,53 @@
96 96
             solution_description = #{solutionDescription},
97 97
             solution_result = #{solutionResult},
98 98
             customer_feedback = #{customerFeedback},
99
-            updated_at = #{updatedAt}
99
+            updated_at = NOW()
100 100
         WHERE id = #{id}
101 101
     </update>
102 102
 
103 103
     <update id="updateStatus">
104
-        UPDATE work_order SET
104
+        UPDATE pat_wo_detail SET
105 105
             status = #{status},
106 106
             process_status = #{processStatus},
107
-            updated_at = #{updatedAt}
107
+            updated_at = NOW()
108 108
         WHERE id = #{id}
109 109
     </update>
110 110
 
111 111
     <update id="updateAssignee">
112
-        UPDATE work_order SET
112
+        UPDATE pat_wo_detail SET
113 113
             assignee_id = #{assigneeId},
114 114
             assignee_name = #{assigneeName},
115
-            updated_at = #{updatedAt}
115
+            updated_at = NOW()
116 116
         WHERE id = #{id}
117 117
     </update>
118 118
 
119 119
     <update id="updateCompletion">
120
-        UPDATE work_order SET
120
+        UPDATE pat_wo_detail SET
121 121
             actual_end_time = #{actualEndTime},
122 122
             completion_time = #{completionTime},
123 123
             solution_result = #{solutionResult},
124
-            updated_at = #{updatedAt}
124
+            updated_at = NOW()
125 125
         WHERE id = #{id}
126 126
     </update>
127 127
 
128 128
     <delete id="deleteById">
129
-        DELETE FROM work_order WHERE id = #{id}
129
+        DELETE FROM pat_wo_detail WHERE id = #{id}
130 130
     </delete>
131 131
 
132 132
     <select id="generateOrderNo" resultType="string">
133
-        SELECT 'WO' || to_char(NOW(), 'YYYY') || '-' || LPAD(nextval('seq_work_order')::text, 3, '0')
133
+        SELECT 'WO' || to_char(NOW(), 'YYYY') || '-' || LPAD(nextval('seq_pat_wo_detail')::text, 3, '0')
134 134
     </select>
135 135
 
136 136
     <select id="countAll" resultType="int">
137
-        SELECT COUNT(*) FROM work_order
137
+        SELECT COUNT(*) FROM pat_wo_detail
138 138
     </select>
139 139
 
140 140
     <select id="countByStatus" resultType="int">
141
-        SELECT COUNT(*) FROM work_order WHERE status = #{status}
141
+        SELECT COUNT(*) FROM pat_wo_detail WHERE status = #{status}
142 142
     </select>
143 143
 
144 144
     <select id="countByProcessStatus" resultType="int">
145
-        SELECT COUNT(*) FROM work_order WHERE process_status = #{processStatus}
145
+        SELECT COUNT(*) FROM pat_wo_detail WHERE process_status = #{processStatus}
146 146
     </select>
147 147
 
148 148
 </mapper>

+ 5
- 5
wm-patrol/src/main/resources/mapper/WorkOrderProcessMapper.xml Zobrazit soubor

@@ -15,7 +15,7 @@
15 15
     </resultMap>
16 16
 
17 17
     <insert id="insert" parameterType="com.water.patrol.entity.WorkOrderProcess">
18
-        INSERT INTO work_order_process (
18
+        INSERT INTO pat_wo_process (
19 19
             work_order_id, process_step, processor_id, processor_name,
20 20
             action, comment, photos, created_at
21 21
         ) VALUES (
@@ -25,21 +25,21 @@
25 25
     </insert>
26 26
 
27 27
     <select id="selectById" resultMap="BaseResultMap">
28
-        SELECT * FROM work_order_process WHERE id = #{id}
28
+        SELECT * FROM pat_wo_process WHERE id = #{id}
29 29
     </select>
30 30
 
31 31
     <select id="selectByWorkOrderId" resultMap="BaseResultMap">
32
-        SELECT * FROM work_order_process WHERE work_order_id = #{workOrderId} ORDER BY created_at DESC
32
+        SELECT * FROM pat_wo_process WHERE work_order_id = #{workOrderId} ORDER BY created_at DESC
33 33
     </select>
34 34
 
35 35
     <select id="selectLatestByWorkOrderId" resultMap="BaseResultMap">
36
-        SELECT * FROM work_order_process 
36
+        SELECT * FROM pat_wo_process 
37 37
         WHERE work_order_id = #{workOrderId} 
38 38
         ORDER BY created_at DESC LIMIT 1
39 39
     </select>
40 40
 
41 41
     <delete id="deleteById">
42
-        DELETE FROM work_order_process WHERE id = #{id}
42
+        DELETE FROM pat_wo_process WHERE id = #{id}
43 43
     </delete>
44 44
 
45 45
 </mapper>

+ 113
- 0
wm-patrol/src/main/resources/sql/V89__patrol_fix.sql Zobrazit soubor

@@ -0,0 +1,113 @@
1
+-- V89: Issue #15 QA Fix - Add missing columns and tables for patrol module
2
+
3
+-- ========== Add missing columns to pat_task ==========
4
+ALTER TABLE pat_task ADD COLUMN IF NOT EXISTS task_date DATE;
5
+ALTER TABLE pat_task ADD COLUMN IF NOT EXISTS assignee_id BIGINT;
6
+ALTER TABLE pat_task ADD COLUMN IF NOT EXISTS plan_start TIMESTAMPTZ;
7
+ALTER TABLE pat_task ADD COLUMN IF NOT EXISTS plan_end TIMESTAMPTZ;
8
+ALTER TABLE pat_task ADD COLUMN IF NOT EXISTS actual_start TIMESTAMPTZ;
9
+ALTER TABLE pat_task ADD COLUMN IF NOT EXISTS actual_end TIMESTAMPTZ;
10
+CREATE INDEX IF NOT EXISTS idx_pat_task_date ON pat_task(task_date);
11
+CREATE INDEX IF NOT EXISTS idx_pat_task_assignee ON pat_task(assignee_id);
12
+
13
+-- ========== Add missing columns to pat_route_setup ==========
14
+ALTER TABLE pat_route_setup ADD COLUMN IF NOT EXISTS route_points TEXT;
15
+ALTER TABLE pat_route_setup ADD COLUMN IF NOT EXISTS estim_duration INT;
16
+ALTER TABLE pat_route_setup ADD COLUMN IF NOT EXISTS area VARCHAR(100);
17
+
18
+-- ========== Add missing column to pat_track_point ==========
19
+ALTER TABLE pat_track_point ADD COLUMN IF NOT EXISTS recorded_at TIMESTAMPTZ DEFAULT NOW();
20
+
21
+-- ========== Add missing column to pat_device ==========
22
+ALTER TABLE pat_device ADD COLUMN IF NOT EXISTS area VARCHAR(100);
23
+
24
+-- ========== Create pat_record table (巡检记录) ==========
25
+CREATE TABLE IF NOT EXISTS pat_record (
26
+    id BIGSERIAL PRIMARY KEY,
27
+    task_id BIGINT NOT NULL,
28
+    point_seq INT,
29
+    device_id BIGINT,
30
+    check_items TEXT,
31
+    gps_lng NUMERIC(10,6),
32
+    gps_lat NUMERIC(10,6),
33
+    record_time TIMESTAMPTZ DEFAULT NOW()
34
+);
35
+CREATE INDEX IF NOT EXISTS idx_pat_record_task ON pat_record(task_id);
36
+
37
+-- ========== Create pat_problem table (巡检问题) ==========
38
+CREATE TABLE IF NOT EXISTS pat_problem (
39
+    id BIGSERIAL PRIMARY KEY,
40
+    problem_no VARCHAR(32) UNIQUE NOT NULL,
41
+    task_id BIGINT,
42
+    point_seq INT,
43
+    device_id BIGINT,
44
+    device_name VARCHAR(100),
45
+    problem_type VARCHAR(30),
46
+    problem_level VARCHAR(20),
47
+    problem_title VARCHAR(200),
48
+    problem_description TEXT,
49
+    location VARCHAR(200),
50
+    lng DOUBLE PRECISION,
51
+    lat DOUBLE PRECISION,
52
+    photo_urls TEXT,
53
+    reporter_id BIGINT,
54
+    reporter_name VARCHAR(50),
55
+    report_time TIMESTAMPTZ,
56
+    status VARCHAR(20) DEFAULT 'reported',
57
+    work_order_id BIGINT,
58
+    created_at TIMESTAMPTZ DEFAULT NOW(),
59
+    updated_at TIMESTAMPTZ DEFAULT NOW()
60
+);
61
+CREATE INDEX IF NOT EXISTS idx_pat_problem_task ON pat_problem(task_id);
62
+CREATE INDEX IF NOT EXISTS idx_pat_problem_status ON pat_problem(status);
63
+CREATE INDEX IF NOT EXISTS idx_pat_problem_device ON pat_problem(device_id);
64
+CREATE SEQUENCE IF NOT EXISTS seq_patrol_problem START 1;
65
+
66
+-- ========== Create pat_wo_detail table (工单详情) ==========
67
+CREATE TABLE IF NOT EXISTS pat_wo_detail (
68
+    id BIGSERIAL PRIMARY KEY,
69
+    order_no VARCHAR(32) UNIQUE NOT NULL,
70
+    problem_id BIGINT,
71
+    order_type VARCHAR(30),
72
+    priority VARCHAR(20) DEFAULT 'normal',
73
+    title VARCHAR(200),
74
+    description TEXT,
75
+    location VARCHAR(200),
76
+    contact_person VARCHAR(50),
77
+    contact_phone VARCHAR(20),
78
+    reporter_id BIGINT,
79
+    reporter_name VARCHAR(50),
80
+    assignee_id BIGINT,
81
+    assignee_name VARCHAR(50),
82
+    status VARCHAR(20) DEFAULT 'pending',
83
+    process_status VARCHAR(20) DEFAULT 'created',
84
+    estimated_duration INT,
85
+    actual_start_time TIMESTAMPTZ,
86
+    actual_end_time TIMESTAMPTZ,
87
+    completion_time TIMESTAMPTZ,
88
+    photos_before TEXT,
89
+    photos_after TEXT,
90
+    solution_description TEXT,
91
+    solution_result TEXT,
92
+    customer_feedback TEXT,
93
+    created_at TIMESTAMPTZ DEFAULT NOW(),
94
+    updated_at TIMESTAMPTZ DEFAULT NOW()
95
+);
96
+CREATE INDEX IF NOT EXISTS idx_pat_wo_detail_status ON pat_wo_detail(status);
97
+CREATE INDEX IF NOT EXISTS idx_pat_wo_detail_assignee ON pat_wo_detail(assignee_id);
98
+CREATE INDEX IF NOT EXISTS idx_pat_wo_detail_problem ON pat_wo_detail(problem_id);
99
+CREATE SEQUENCE IF NOT EXISTS seq_pat_wo_detail START 1;
100
+
101
+-- ========== Create pat_wo_process table (工单处理记录) ==========
102
+CREATE TABLE IF NOT EXISTS pat_wo_process (
103
+    id BIGSERIAL PRIMARY KEY,
104
+    work_order_id BIGINT NOT NULL,
105
+    process_step VARCHAR(30),
106
+    processor_id BIGINT,
107
+    processor_name VARCHAR(50),
108
+    action VARCHAR(30),
109
+    comment TEXT,
110
+    photos TEXT,
111
+    created_at TIMESTAMPTZ DEFAULT NOW()
112
+);
113
+CREATE INDEX IF NOT EXISTS idx_pat_wo_process_wo ON pat_wo_process(work_order_id);

+ 326
- 0
wm-patrol/src/test/java/com/water/patrol/PatrolCoreWoServiceTest.java Zobrazit soubor

@@ -0,0 +1,326 @@
1
+package com.water.patrol;
2
+
3
+import com.water.patrol.service.PatrolCoreService;
4
+import com.water.patrol.service.PatrolWoService;
5
+import com.water.patrol.service.PatrolService;
6
+import com.water.patrol.service.PatrolOverviewService;
7
+import org.junit.jupiter.api.Test;
8
+import org.junit.jupiter.api.extension.ExtendWith;
9
+import org.mockito.*;
10
+import org.mockito.junit.jupiter.MockitoExtension;
11
+import org.springframework.jdbc.core.JdbcTemplate;
12
+
13
+import java.time.LocalDate;
14
+import java.util.*;
15
+
16
+import static org.junit.jupiter.api.Assertions.*;
17
+import static org.mockito.Mockito.*;
18
+
19
+/**
20
+ * Issue #15 QA Fix - Tests for JdbcTemplate-based services
21
+ * Verifies table name fixes (patrol_* -> pat_*) and core logic
22
+ */
23
+@ExtendWith(MockitoExtension.class)
24
+class PatrolCoreWoServiceTest {
25
+
26
+    @Mock JdbcTemplate jdbc;
27
+
28
+    @InjectMocks PatrolCoreService coreSvc;
29
+    @InjectMocks PatrolWoService woSvc;
30
+    @InjectMocks PatrolService patrolSvc;
31
+    @InjectMocks PatrolOverviewService overviewSvc;
32
+
33
+    // ========== PatrolCoreService Tests ==========
34
+
35
+    @Test
36
+    void testGetOverview() {
37
+        when(jdbc.queryForObject(anyString(), eq(Long.class), any())).thenReturn(5L);
38
+        Map<String, Object> result = coreSvc.getOverview();
39
+        assertNotNull(result);
40
+        assertEquals(5L, result.get("todayTotal"));
41
+        assertEquals(5L, result.get("todayCompleted"));
42
+    }
43
+
44
+    @Test
45
+    void testGetTodayTasks() {
46
+        List<Map<String, Object>> mockRows = List.of(
47
+            Map.of("id", 1, "task_no", "PAT-001", "route_name", "路线A")
48
+        );
49
+        when(jdbc.queryForList(anyString(), any())).thenReturn(mockRows);
50
+        List<Map<String, Object>> result = coreSvc.getTodayTasks();
51
+        assertEquals(1, result.size());
52
+    }
53
+
54
+    @Test
55
+    void testRecordTrackPoint() {
56
+        Map<String, Object> result = coreSvc.recordTrackPoint(1L, 100L, 116.4, 39.9, 5.0, 3.0);
57
+        assertNotNull(result);
58
+        assertEquals(1L, result.get("taskId"));
59
+        assertEquals(true, result.get("recorded"));
60
+        verify(jdbc).update(contains("pat_track_point"), eq(1L), eq(100L), eq(116.4), eq(39.9), eq(5.0), eq(3.0));
61
+    }
62
+
63
+    @Test
64
+    void testGetTrack() {
65
+        when(jdbc.queryForList(anyString(), eq(1L))).thenReturn(Collections.emptyList());
66
+        List<Map<String, Object>> result = coreSvc.getTrack(1L);
67
+        assertNotNull(result);
68
+        verify(jdbc).queryForList(contains("pat_track_point"), eq(1L));
69
+    }
70
+
71
+    @Test
72
+    void testReplayTrack_empty() {
73
+        when(jdbc.queryForList(anyString(), eq(1L))).thenReturn(Collections.emptyList());
74
+        Map<String, Object> result = coreSvc.replayTrack(1L);
75
+        assertEquals(0, result.get("totalPoints"));
76
+    }
77
+
78
+    @Test
79
+    void testReplayTrack_withPoints() {
80
+        List<Map<String, Object>> points = new ArrayList<>();
81
+        Map<String, Object> p1 = new HashMap<>();
82
+        p1.put("recorded_at", "2026-06-17T10:00:00");
83
+        p1.put("lat", 39.9);
84
+        p1.put("lng", 116.4);
85
+        p1.put("speed", 5.0);
86
+        Map<String, Object> p2 = new HashMap<>();
87
+        p2.put("recorded_at", "2026-06-17T10:05:00");
88
+        p2.put("lat", 39.91);
89
+        p2.put("lng", 116.41);
90
+        p2.put("speed", 6.0);
91
+        points.add(p1);
92
+        points.add(p2);
93
+        when(jdbc.queryForList(anyString(), eq(1L))).thenReturn(points);
94
+
95
+        Map<String, Object> result = coreSvc.replayTrack(1L);
96
+        assertEquals(2, result.get("totalPoints"));
97
+    }
98
+
99
+    @Test
100
+    void testGetTrackStats_singlePoint() {
101
+        when(jdbc.queryForList(anyString(), eq(1L))).thenReturn(List.of(Map.of("lat", 39.9, "lng", 116.4)));
102
+        Map<String, Object> stats = coreSvc.getTrackStats(1L);
103
+        assertEquals(1, stats.get("totalPoints"));
104
+        assertEquals(0.0, stats.get("totalDistance"));
105
+    }
106
+
107
+    @Test
108
+    void testGetTaskLedger() {
109
+        when(jdbc.queryForList(anyString(), any(Object[].class))).thenReturn(Collections.emptyList());
110
+        when(jdbc.queryForObject(anyString(), eq(Long.class), any(Object[].class))).thenReturn(0L);
111
+        Map<String, Object> result = coreSvc.getTaskLedger(null, null, 1, 10);
112
+        assertNotNull(result);
113
+        assertEquals(0L, result.get("total"));
114
+        verify(jdbc).queryForList(contains("pat_task"), any(Object[].class));
115
+    }
116
+
117
+    @Test
118
+    void testGetDeviceLedger() {
119
+        when(jdbc.queryForList(anyString(), any(Object[].class))).thenReturn(Collections.emptyList());
120
+        when(jdbc.queryForObject(anyString(), eq(Long.class), any(Object[].class))).thenReturn(0L);
121
+        Map<String, Object> result = coreSvc.getDeviceLedger(null, null, null, 1, 10);
122
+        assertNotNull(result);
123
+        verify(jdbc).queryForList(contains("pat_device"), any(Object[].class));
124
+    }
125
+
126
+    @Test
127
+    void testCreateDevice() {
128
+        Map<String, Object> result = coreSvc.createDevice("DEV-001", "阀门A", "valve", "A区", 116.0, 39.0, "A区");
129
+        assertNotNull(result);
130
+        assertEquals("DEV-001", result.get("deviceNo"));
131
+        verify(jdbc).update(contains("pat_device"), any(), any(), any(), any(), any(), any(), any());
132
+    }
133
+
134
+    @Test
135
+    void testUpdateDeviceStatus() {
136
+        when(jdbc.update(anyString(), any(), any())).thenReturn(1);
137
+        Map<String, Object> result = coreSvc.updateDeviceStatus(1L, "fault");
138
+        assertEquals("fault", result.get("status"));
139
+        verify(jdbc).update(contains("pat_device"), eq("fault"), eq(1L));
140
+    }
141
+
142
+    // ========== PatrolWoService Tests ==========
143
+
144
+    @Test
145
+    void testWoCreate() {
146
+        Map<String, Object> result = woSvc.create("leak", "管道漏水", "high", "A区", 116.0, 39.0, 1L);
147
+        assertNotNull(result);
148
+        assertTrue(result.get("orderNo").toString().startsWith("PWO-"));
149
+        assertEquals("pending", result.get("status"));
150
+        verify(jdbc).update(contains("pat_work_order"), any(), any(), any(), any(), any(), any(), any(), any());
151
+    }
152
+
153
+    @Test
154
+    void testWoAssign() {
155
+        when(jdbc.update(anyString(), any(), any(), any())).thenReturn(1);
156
+        Map<String, Object> result = woSvc.assign(1L, 200L, "李四");
157
+        assertEquals(true, result.get("success"));
158
+        verify(jdbc).update(contains("pat_work_order"), eq(200L), eq("李四"), eq(1L));
159
+    }
160
+
161
+    @Test
162
+    void testWoProcess() {
163
+        when(jdbc.update(anyString(), any(), any())).thenReturn(1);
164
+        Map<String, Object> result = woSvc.process(1L, "处理中");
165
+        assertEquals(true, result.get("success"));
166
+        verify(jdbc).update(contains("pat_work_order"), eq("处理中"), eq(1L));
167
+    }
168
+
169
+    @Test
170
+    void testWoResolve() {
171
+        when(jdbc.update(anyString(), any())).thenReturn(1);
172
+        Map<String, Object> result = woSvc.resolve(1L);
173
+        assertEquals(true, result.get("success"));
174
+        verify(jdbc).update(contains("pat_work_order"), eq(1L));
175
+    }
176
+
177
+    @Test
178
+    void testWoGetDetail() {
179
+        Map<String, Object> mockRow = new HashMap<>();
180
+        mockRow.put("id", 1);
181
+        mockRow.put("order_no", "PWO-001");
182
+        when(jdbc.queryForList(anyString(), eq(1L))).thenReturn(List.of(mockRow));
183
+        Map<String, Object> result = woSvc.getDetail(1L);
184
+        assertNotNull(result);
185
+        assertEquals("PWO-001", result.get("order_no"));
186
+    }
187
+
188
+    @Test
189
+    void testWoGetDetail_notFound() {
190
+        when(jdbc.queryForList(anyString(), eq(999L))).thenReturn(Collections.emptyList());
191
+        Map<String, Object> result = woSvc.getDetail(999L);
192
+        assertNull(result);
193
+    }
194
+
195
+    @Test
196
+    void testWoList() {
197
+        when(jdbc.queryForList(anyString(), any(Object[].class))).thenReturn(Collections.emptyList());
198
+        when(jdbc.queryForObject(anyString(), eq(Long.class), any(Object[].class))).thenReturn(0L);
199
+        Map<String, Object> result = woSvc.list(null, null, 1, 10);
200
+        assertNotNull(result);
201
+        assertEquals(0L, result.get("total"));
202
+        verify(jdbc).queryForList(contains("pat_work_order"), any(Object[].class));
203
+    }
204
+
205
+    @Test
206
+    void testWoStats() {
207
+        List<Map<String, Object>> mockRows = List.of(
208
+            Map.of("status", "pending", "count", 5L),
209
+            Map.of("status", "resolved", "count", 10L)
210
+        );
211
+        when(jdbc.queryForList(anyString())).thenReturn(mockRows);
212
+        Map<String, Object> result = woSvc.stats();
213
+        assertEquals(5L, result.get("pending"));
214
+        assertEquals(10L, result.get("resolved"));
215
+    }
216
+
217
+    // ========== PatrolService Tests ==========
218
+
219
+    @Test
220
+    void testCreateRoute() {
221
+        List<Map<String, Object>> points = List.of(Map.of("lng", 116.0, "lat", 39.0));
222
+        Map<String, Object> result = patrolSvc.createRoute("路线A", "A区", points, 60);
223
+        assertNotNull(result);
224
+        assertEquals("路线A", result.get("routeName"));
225
+        verify(jdbc).update(contains("pat_route_setup"), any(), any(), any(), any());
226
+    }
227
+
228
+    @Test
229
+    void testGetRoutes() {
230
+        when(jdbc.queryForList(anyString(), eq("A区"))).thenReturn(Collections.emptyList());
231
+        List<Map<String, Object>> result = patrolSvc.getRoutes("A区");
232
+        assertNotNull(result);
233
+        verify(jdbc).queryForList(contains("pat_route_setup"), eq("A区"));
234
+    }
235
+
236
+    @Test
237
+    void testCreateTask() {
238
+        Map<String, Object> result = patrolSvc.createTask(1L, 100L, "2026-06-17");
239
+        assertNotNull(result);
240
+        assertEquals("created", result.get("status"));
241
+        verify(jdbc).update(contains("pat_task"), any(), any(), any(), any(), any(), any());
242
+    }
243
+
244
+    @Test
245
+    void testGetTodayTasks() {
246
+        when(jdbc.queryForList(anyString(), eq(100L))).thenReturn(Collections.emptyList());
247
+        List<Map<String, Object>> result = patrolSvc.getTodayTasks(100L);
248
+        assertNotNull(result);
249
+        verify(jdbc).queryForList(contains("pat_task"), eq(100L));
250
+    }
251
+
252
+    @Test
253
+    void testStartTask() {
254
+        Map<String, Object> result = patrolSvc.startTask(1L);
255
+        assertEquals("in_progress", result.get("status"));
256
+        verify(jdbc).update(contains("pat_task"), eq(1L));
257
+    }
258
+
259
+    @Test
260
+    void testCompleteTask() {
261
+        Map<String, Object> result = patrolSvc.completeTask(1L, 5000.0);
262
+        assertEquals("completed", result.get("status"));
263
+        assertEquals(5000.0, result.get("distance"));
264
+        verify(jdbc).update(contains("pat_task"), eq(5000.0), eq(1L));
265
+    }
266
+
267
+    @Test
268
+    void testRecordCheck() {
269
+        List<Map<String, Object>> items = List.of(Map.of("item", "阀门", "result", "正常"));
270
+        Map<String, Object> result = patrolSvc.recordCheck(1L, 1, 10L, items, 116.0, 39.0);
271
+        assertEquals(true, result.get("recorded"));
272
+        verify(jdbc).update(contains("pat_record"), any(), any(), any(), any(), any(), any());
273
+    }
274
+
275
+    @Test
276
+    void testGetTaskRecords() {
277
+        when(jdbc.queryForList(anyString(), eq(1L))).thenReturn(Collections.emptyList());
278
+        List<Map<String, Object>> result = patrolSvc.getTaskRecords(1L);
279
+        assertNotNull(result);
280
+        verify(jdbc).queryForList(contains("pat_record"), eq(1L));
281
+    }
282
+
283
+    @Test
284
+    void testReportIssue() {
285
+        Map<String, Object> result = patrolSvc.reportIssue(1L, 10L, "leak", "管道漏水",
286
+            List.of("photo1.jpg"), 116.0, 39.0);
287
+        assertEquals(true, result.get("reported"));
288
+        assertEquals("leak", result.get("issueType"));
289
+    }
290
+
291
+    @Test
292
+    void testGetStats() {
293
+        Map<String, Object> countMap = new HashMap<>();
294
+        countMap.put("total", 10);
295
+        countMap.put("completed", 5);
296
+        when(jdbc.queryForMap(anyString(), any(), any())).thenReturn(countMap);
297
+        when(jdbc.queryForList(anyString(), any(), any())).thenReturn(Collections.emptyList());
298
+
299
+        Map<String, Object> result = patrolSvc.getStats("A区", LocalDate.of(2026, 1, 1), LocalDate.of(2026, 6, 17));
300
+        assertNotNull(result);
301
+        assertNotNull(result.get("completionRate"));
302
+        assertNotNull(result.get("personDistance"));
303
+        assertNotNull(result.get("workload"));
304
+        assertNotNull(result.get("issueStats"));
305
+    }
306
+
307
+    // ========== PatrolOverviewService Tests ==========
308
+
309
+    @Test
310
+    void testOverviewGetOverview() {
311
+        when(jdbc.queryForObject(anyString(), eq(Integer.class), any())).thenReturn(10);
312
+        when(jdbc.queryForObject(anyString(), eq(Double.class), any())).thenReturn(5000.0);
313
+        Map<String, Object> result = overviewSvc.getOverview();
314
+        assertNotNull(result);
315
+        assertEquals(10, result.get("todayTasks"));
316
+    }
317
+
318
+    @Test
319
+    void testOverviewGetMonthlyStats() {
320
+        when(jdbc.queryForObject(anyString(), eq(Integer.class), any(), any())).thenReturn(100);
321
+        when(jdbc.queryForObject(anyString(), eq(Double.class), any(), any())).thenReturn(50000.0);
322
+        Map<String, Object> result = overviewSvc.getMonthlyStats(2026, 6);
323
+        assertNotNull(result);
324
+        assertEquals(100, result.get("totalTasks"));
325
+    }
326
+}

+ 316
- 0
wm-patrol/src/test/java/com/water/patrol/PatrolProblemWorkOrderTest.java Zobrazit soubor

@@ -0,0 +1,316 @@
1
+package com.water.patrol;
2
+
3
+import com.water.patrol.entity.PatrolProblem;
4
+import com.water.patrol.entity.WorkOrder;
5
+import com.water.patrol.entity.WorkOrderProcess;
6
+import com.water.patrol.mapper.PatrolProblemMapper;
7
+import com.water.patrol.mapper.WorkOrderMapper;
8
+import com.water.patrol.mapper.WorkOrderProcessMapper;
9
+import com.water.patrol.service.WorkOrderService;
10
+import com.water.patrol.service.impl.PatrolProblemServiceImpl;
11
+import com.water.patrol.service.impl.WorkOrderServiceImpl;
12
+import org.junit.jupiter.api.Test;
13
+import org.junit.jupiter.api.extension.ExtendWith;
14
+import org.mockito.*;
15
+import org.mockito.junit.jupiter.MockitoExtension;
16
+
17
+import java.util.*;
18
+
19
+import static org.junit.jupiter.api.Assertions.*;
20
+import static org.mockito.Mockito.*;
21
+
22
+/**
23
+ * Issue #15 QA Fix - Tests for PatrolProblemServiceImpl and WorkOrderServiceImpl
24
+ */
25
+@ExtendWith(MockitoExtension.class)
26
+class PatrolProblemWorkOrderTest {
27
+
28
+    @Mock PatrolProblemMapper problemMapper;
29
+    @Mock WorkOrderMapper woMapper;
30
+    @Mock WorkOrderProcessMapper woProcessMapper;
31
+
32
+    @InjectMocks PatrolProblemServiceImpl problemSvc;
33
+    @InjectMocks WorkOrderServiceImpl woSvc;
34
+
35
+    // ========== PatrolProblemServiceImpl Tests ==========
36
+
37
+    @Test
38
+    void testCreateProblem() {
39
+        when(problemMapper.generateProblemNo()).thenReturn("WQ2026-001");
40
+        when(problemMapper.insert(any())).thenReturn(1);
41
+        when(problemMapper.updateStatus(anyLong(), anyString(), anyLong())).thenReturn(1);
42
+        when(woMapper.generateOrderNo()).thenReturn("WO2026-001");
43
+        when(woMapper.insert(any())).thenReturn(1);
44
+        when(woProcessMapper.insert(any())).thenReturn(1);
45
+
46
+        PatrolProblem p = new PatrolProblem();
47
+        p.setProblemType("设备故障");
48
+        p.setProblemLevel("high");
49
+        p.setProblemTitle("阀门漏水");
50
+        p.setProblemDescription("主管道阀门A严重漏水");
51
+        p.setLocation("A区");
52
+        p.setReporterId(100L);
53
+        p.setReporterName("张三");
54
+
55
+        PatrolProblem result = problemSvc.createProblem(p);
56
+        assertNotNull(result);
57
+        assertEquals("WQ2026-001", result.getProblemNo());
58
+        assertEquals("reported", result.getStatus());
59
+        verify(problemMapper).insert(any());
60
+    }
61
+
62
+    @Test
63
+    void testGetProblemById() {
64
+        PatrolProblem p = new PatrolProblem();
65
+        p.setId(1L);
66
+        p.setProblemNo("WQ2026-001");
67
+        when(problemMapper.selectById(1L)).thenReturn(p);
68
+
69
+        PatrolProblem result = problemSvc.getProblemById(1L);
70
+        assertNotNull(result);
71
+        assertEquals("WQ2026-001", result.getProblemNo());
72
+    }
73
+
74
+    @Test
75
+    void testGetProblemByProblemNo() {
76
+        PatrolProblem p = new PatrolProblem();
77
+        p.setProblemNo("WQ2026-002");
78
+        when(problemMapper.selectByProblemNo("WQ2026-002")).thenReturn(p);
79
+
80
+        PatrolProblem result = problemSvc.getProblemByProblemNo("WQ2026-002");
81
+        assertNotNull(result);
82
+        assertEquals("WQ2026-002", result.getProblemNo());
83
+    }
84
+
85
+    @Test
86
+    void testGetProblemsByTaskId() {
87
+        when(problemMapper.selectByTaskId(1L)).thenReturn(List.of(new PatrolProblem()));
88
+        List<PatrolProblem> results = problemSvc.getProblemsByTaskId(1L);
89
+        assertEquals(1, results.size());
90
+    }
91
+
92
+    @Test
93
+    void testGetProblemsByStatus() {
94
+        when(problemMapper.selectByStatus("reported")).thenReturn(List.of(new PatrolProblem(), new PatrolProblem()));
95
+        List<PatrolProblem> results = problemSvc.getProblemsByStatus("reported");
96
+        assertEquals(2, results.size());
97
+    }
98
+
99
+    @Test
100
+    void testGetProblemsByDeviceId() {
101
+        when(problemMapper.selectByDeviceId(10L)).thenReturn(Collections.emptyList());
102
+        List<PatrolProblem> results = problemSvc.getProblemsByDeviceId(10L);
103
+        assertTrue(results.isEmpty());
104
+    }
105
+
106
+    @Test
107
+    void testUpdateProblemStatus() {
108
+        when(problemMapper.updateStatus(1L, "processing", 200L)).thenReturn(1);
109
+        boolean result = problemSvc.updateProblemStatus(1L, "processing", 200L);
110
+        assertTrue(result);
111
+    }
112
+
113
+    @Test
114
+    void testDeleteProblem() {
115
+        when(problemMapper.deleteById(1L)).thenReturn(1);
116
+        boolean result = problemSvc.deleteProblem(1L);
117
+        assertTrue(result);
118
+    }
119
+
120
+    @Test
121
+    void testGetProblemStatistics() {
122
+        when(problemMapper.countAll()).thenReturn(100);
123
+        when(problemMapper.countByStatus("reported")).thenReturn(30);
124
+        when(problemMapper.countByStatus("processing")).thenReturn(20);
125
+        when(problemMapper.countByStatus("completed")).thenReturn(40);
126
+        when(problemMapper.countByStatus("closed")).thenReturn(10);
127
+
128
+        PatrolProblemServiceImpl.ProblemStatistics stats = problemSvc.getProblemStatistics();
129
+        assertEquals(100, stats.getTotalProblems());
130
+        assertEquals(30, stats.getReportedCount());
131
+        assertEquals(20, stats.getProcessingCount());
132
+        assertEquals(40, stats.getCompletedCount());
133
+        assertEquals(10, stats.getClosedCount());
134
+    }
135
+
136
+    @Test
137
+    void testAutoCreateWorkOrder_problemNotFound() {
138
+        when(problemMapper.selectById(999L)).thenReturn(null);
139
+        boolean result = problemSvc.autoCreateWorkOrder(999L);
140
+        assertFalse(result);
141
+    }
142
+
143
+    @Test
144
+    void testAutoCreateWorkOrder_success() {
145
+        PatrolProblem p = new PatrolProblem();
146
+        p.setId(1L);
147
+        p.setProblemType("设备故障");
148
+        p.setProblemLevel("high");
149
+        p.setProblemTitle("阀门漏水");
150
+        p.setProblemDescription("描述");
151
+        p.setLocation("A区");
152
+        p.setReporterId(100L);
153
+        p.setReporterName("张三");
154
+
155
+        when(problemMapper.selectById(1L)).thenReturn(p);
156
+        when(woMapper.generateOrderNo()).thenReturn("WO2026-001");
157
+        when(woMapper.insert(any())).thenReturn(1);
158
+        when(woProcessMapper.insert(any())).thenReturn(1);
159
+        when(problemMapper.updateStatus(anyLong(), anyString(), anyLong())).thenReturn(1);
160
+
161
+        boolean result = problemSvc.autoCreateWorkOrder(1L);
162
+        assertTrue(result);
163
+    }
164
+
165
+    // ========== WorkOrderServiceImpl Tests ==========
166
+
167
+    @Test
168
+    void testCreateFromProblem() {
169
+        PatrolProblem problem = new PatrolProblem();
170
+        problem.setId(1L);
171
+        problem.setProblemType("设备故障");
172
+        problem.setProblemLevel("critical");
173
+        problem.setProblemTitle("紧急维修");
174
+        problem.setProblemDescription("管道爆裂");
175
+        problem.setLocation("B区");
176
+        problem.setReporterId(100L);
177
+        problem.setReporterName("张三");
178
+
179
+        when(woMapper.generateOrderNo()).thenReturn("WO2026-001");
180
+        when(woMapper.insert(any())).thenReturn(1);
181
+        when(woProcessMapper.insert(any())).thenReturn(1);
182
+        when(problemMapper.updateStatus(anyLong(), anyString(), anyLong())).thenReturn(1);
183
+
184
+        boolean result = woSvc.createFromProblem(problem);
185
+        assertTrue(result);
186
+        verify(woMapper).insert(any());
187
+        verify(woProcessMapper).insert(any());
188
+        verify(problemMapper).updateStatus(eq(1L), eq("processing"), anyLong());
189
+    }
190
+
191
+    @Test
192
+    void testGetWorkOrderById() {
193
+        WorkOrder wo = new WorkOrder();
194
+        wo.setId(1L);
195
+        wo.setOrderNo("WO2026-001");
196
+        when(woMapper.selectById(1L)).thenReturn(wo);
197
+
198
+        WorkOrder result = woSvc.getWorkOrderById(1L);
199
+        assertNotNull(result);
200
+        assertEquals("WO2026-001", result.getOrderNo());
201
+    }
202
+
203
+    @Test
204
+    void testGetWorkOrderByOrderNo() {
205
+        WorkOrder wo = new WorkOrder();
206
+        wo.setOrderNo("WO2026-002");
207
+        when(woMapper.selectByOrderNo("WO2026-002")).thenReturn(wo);
208
+
209
+        WorkOrder result = woSvc.getWorkOrderByOrderNo("WO2026-002");
210
+        assertNotNull(result);
211
+    }
212
+
213
+    @Test
214
+    void testGetWorkOrdersByStatus() {
215
+        when(woMapper.selectByStatus("pending")).thenReturn(List.of(new WorkOrder()));
216
+        List<WorkOrder> results = woSvc.getWorkOrdersByStatus("pending");
217
+        assertEquals(1, results.size());
218
+    }
219
+
220
+    @Test
221
+    void testAssignWorkOrder() {
222
+        WorkOrder wo = new WorkOrder();
223
+        wo.setId(1L);
224
+        wo.setStatus("pending");
225
+
226
+        when(woMapper.updateAssignee(1L, 200L, "李四")).thenReturn(1);
227
+        when(woMapper.selectById(1L)).thenReturn(wo);
228
+        when(woProcessMapper.insert(any())).thenReturn(1);
229
+        when(woMapper.updateStatus(1L, "assigned", "accepted")).thenReturn(1);
230
+
231
+        boolean result = woSvc.assignWorkOrder(1L, 200L, "李四");
232
+        assertTrue(result);
233
+        verify(woProcessMapper).insert(any());
234
+    }
235
+
236
+    @Test
237
+    void testStartWorkOrder() {
238
+        WorkOrder wo = new WorkOrder();
239
+        wo.setId(1L);
240
+        wo.setAssigneeId(200L);
241
+        wo.setAssigneeName("李四");
242
+
243
+        when(woMapper.updateStatus(1L, "processing", "in_progress")).thenReturn(1);
244
+        when(woMapper.selectById(1L)).thenReturn(wo);
245
+        when(woProcessMapper.insert(any())).thenReturn(1);
246
+
247
+        boolean result = woSvc.startWorkOrder(1L);
248
+        assertTrue(result);
249
+    }
250
+
251
+    @Test
252
+    void testCompleteWorkOrder() {
253
+        WorkOrder wo = new WorkOrder();
254
+        wo.setId(1L);
255
+        wo.setAssigneeId(200L);
256
+        wo.setAssigneeName("李四");
257
+
258
+        when(woMapper.updateCompletion(eq(1L), any(), any(), eq("已修复"))).thenReturn(1);
259
+        when(woMapper.selectById(1L)).thenReturn(wo);
260
+        when(woProcessMapper.insert(any())).thenReturn(1);
261
+        when(woMapper.updateStatus(1L, "completed", "completed")).thenReturn(1);
262
+
263
+        boolean result = woSvc.completeWorkOrder(1L, "已修复");
264
+        assertTrue(result);
265
+    }
266
+
267
+    @Test
268
+    void testDeleteWorkOrder() {
269
+        when(woMapper.deleteById(1L)).thenReturn(1);
270
+        boolean result = woSvc.deleteWorkOrder(1L);
271
+        assertTrue(result);
272
+    }
273
+
274
+    @Test
275
+    void testGetWorkOrderStatistics() {
276
+        when(woMapper.countAll()).thenReturn(50);
277
+        when(woMapper.countByStatus("pending")).thenReturn(10);
278
+        when(woMapper.countByStatus("assigned")).thenReturn(5);
279
+        when(woMapper.countByStatus("processing")).thenReturn(15);
280
+        when(woMapper.countByStatus("completed")).thenReturn(18);
281
+        when(woMapper.countByStatus("cancelled")).thenReturn(2);
282
+
283
+        WorkOrderService.WorkOrderStatistics stats = woSvc.getWorkOrderStatistics();
284
+        assertEquals(50, stats.getTotalOrders());
285
+        assertEquals(10, stats.getPendingCount());
286
+        assertEquals(5, stats.getAssignedCount());
287
+        assertEquals(15, stats.getProcessingCount());
288
+        assertEquals(18, stats.getCompletedCount());
289
+        assertEquals(2, stats.getCancelledCount());
290
+    }
291
+
292
+    @Test
293
+    void testDetermineOrderType() {
294
+        // Test via reflection or through createFromProblem
295
+        PatrolProblem p = new PatrolProblem();
296
+        p.setId(1L);
297
+        p.setProblemType("水质异常");
298
+        p.setProblemLevel("high");
299
+        p.setProblemTitle("水质问题");
300
+        p.setProblemDescription("描述");
301
+        p.setLocation("A区");
302
+        p.setReporterId(100L);
303
+        p.setReporterName("张三");
304
+
305
+        when(woMapper.generateOrderNo()).thenReturn("WO2026-001");
306
+        when(woMapper.insert(any())).thenAnswer(invocation -> {
307
+            WorkOrder wo = invocation.getArgument(0);
308
+            assertEquals("水质处理", wo.getOrderType());
309
+            return 1;
310
+        });
311
+        when(woProcessMapper.insert(any())).thenReturn(1);
312
+        when(problemMapper.updateStatus(anyLong(), anyString(), anyLong())).thenReturn(1);
313
+
314
+        woSvc.createFromProblem(p);
315
+    }
316
+}