Просмотр исходного кода

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 2 дней назад
Родитель
Сommit
b022c21a26

+ 2
- 0
wm-patrol/src/main/java/com/water/patrol/PatrolApplication.java Просмотреть файл

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

+ 17
- 17
wm-patrol/src/main/java/com/water/patrol/service/PatrolCoreService.java Просмотреть файл

26
         LocalDate today = LocalDate.now();
26
         LocalDate today = LocalDate.now();
27
         LocalDate monthStart = today.withDayOfMonth(1);
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
         long coverageRate = monthTotal > 0 ? Math.round(monthCompleted * 100.0 / monthTotal) : 0;
37
         long coverageRate = monthTotal > 0 ? Math.round(monthCompleted * 100.0 / monthTotal) : 0;
38
 
38
 
49
 
49
 
50
     public List<Map<String, Object>> getTodayTasks() {
50
     public List<Map<String, Object>> getTodayTasks() {
51
         return jdbc.queryForList(
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
             "WHERE pt.task_date = CURRENT_DATE ORDER BY pt.plan_start");
54
             "WHERE pt.task_date = CURRENT_DATE ORDER BY pt.plan_start");
55
     }
55
     }
56
 
56
 
57
     public List<Map<String, Object>> getRecentIssues(int limit) {
57
     public List<Map<String, Object>> getRecentIssues(int limit) {
58
         return jdbc.queryForList(
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
     // ========== PAT-02 任务轨迹(GPS记录与回放) ==========
62
     // ========== PAT-02 任务轨迹(GPS记录与回放) ==========
65
                                                  double lng, double lat,
65
                                                  double lng, double lat,
66
                                                  double speed, double accuracy) {
66
                                                  double speed, double accuracy) {
67
         jdbc.update(
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
             "VALUES (?,?,?,?,?,?)",
69
             "VALUES (?,?,?,?,?,?)",
70
             taskId, workerId, lng, lat, speed, accuracy);
70
             taskId, workerId, lng, lat, speed, accuracy);
71
         Map<String, Object> result = new LinkedHashMap<>();
71
         Map<String, Object> result = new LinkedHashMap<>();
79
 
79
 
80
     public List<Map<String, Object>> getTrack(Long taskId) {
80
     public List<Map<String, Object>> getTrack(Long taskId) {
81
         return jdbc.queryForList(
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
     public Map<String, Object> replayTrack(Long taskId) {
85
     public Map<String, Object> replayTrack(Long taskId) {
130
     // ========== PAT-03 任务台账 ==========
130
     // ========== PAT-03 任务台账 ==========
131
 
131
 
132
     public Map<String, Object> getTaskLedger(String status, Long workerId, int page, int size) {
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
         List<Object> params = new ArrayList<>();
134
         List<Object> params = new ArrayList<>();
135
         if (status != null && !status.isEmpty()) {
135
         if (status != null && !status.isEmpty()) {
136
             sql.append(" AND status = ?");
136
             sql.append(" AND status = ?");
146
 
146
 
147
         List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
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
         List<Object> countParams = new ArrayList<>();
150
         List<Object> countParams = new ArrayList<>();
151
         if (status != null && !status.isEmpty()) {
151
         if (status != null && !status.isEmpty()) {
152
             countSql.append(" AND status = ?");
152
             countSql.append(" AND status = ?");
170
 
170
 
171
     public Map<String, Object> getDeviceLedger(String deviceType, String status, String area,
171
     public Map<String, Object> getDeviceLedger(String deviceType, String status, String area,
172
                                                 int page, int size) {
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
         List<Object> params = new ArrayList<>();
174
         List<Object> params = new ArrayList<>();
175
         if (deviceType != null && !deviceType.isEmpty()) {
175
         if (deviceType != null && !deviceType.isEmpty()) {
176
             sql.append(" AND device_type = ?");
176
             sql.append(" AND device_type = ?");
190
 
190
 
191
         List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
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
         List<Object> countParams = new ArrayList<>();
194
         List<Object> countParams = new ArrayList<>();
195
         if (deviceType != null && !deviceType.isEmpty()) {
195
         if (deviceType != null && !deviceType.isEmpty()) {
196
             countSql.append(" AND device_type = ?");
196
             countSql.append(" AND device_type = ?");
217
     public Map<String, Object> createDevice(String deviceNo, String deviceName, String deviceType,
217
     public Map<String, Object> createDevice(String deviceNo, String deviceName, String deviceType,
218
                                              String location, double lng, double lat, String area) {
218
                                              String location, double lng, double lat, String area) {
219
         jdbc.update(
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
             "VALUES (?,?,?,?,?,?,?)",
221
             "VALUES (?,?,?,?,?,?,?)",
222
             deviceNo, deviceName, deviceType, location, lng, lat, area);
222
             deviceNo, deviceName, deviceType, location, lng, lat, area);
223
         Map<String, Object> result = new LinkedHashMap<>();
223
         Map<String, Object> result = new LinkedHashMap<>();
228
     }
228
     }
229
 
229
 
230
     public Map<String, Object> updateDeviceStatus(Long deviceId, String status) {
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
             status, deviceId);
232
             status, deviceId);
233
         return Map.of("deviceId", deviceId, "status", status, "updated", true);
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 Просмотреть файл

17
 
17
 
18
     // ========== 路线管理 ==========
18
     // ========== 路线管理 ==========
19
     public Map<String, Object> createRoute(String routeName, String area, List<Map<String, Object>> points, int estimDuration) {
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
             routeName, area, points.toString(), estimDuration);
21
             routeName, area, points.toString(), estimDuration);
22
         return Map.of("routeName", routeName, "area", area, "points", points.size());
22
         return Map.of("routeName", routeName, "area", area, "points", points.size());
23
     }
23
     }
24
 
24
 
25
     public List<Map<String, Object>> getRoutes(String area) {
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
     public Map<String, Object> createTask(Long routeId, Long assigneeId, String taskDate) {
30
     public Map<String, Object> createTask(Long routeId, Long assigneeId, String taskDate) {
31
         jdbc.update(
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
             "SELECT ?, ?, route_name, ?, CAST(? AS TIMESTAMP), CAST(? AS TIMESTAMP) + (estim_duration || ' minutes')::INTERVAL, 'pending' " +
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
             routeId, assigneeId, taskDate, taskDate + " 09:00:00", taskDate + " 09:00:00", routeId);
35
             routeId, assigneeId, taskDate, taskDate + " 09:00:00", taskDate + " 09:00:00", routeId);
36
         return Map.of("routeId", routeId, "assigneeId", assigneeId, "date", taskDate, "status", "created");
36
         return Map.of("routeId", routeId, "assigneeId", assigneeId, "date", taskDate, "status", "created");
37
     }
37
     }
38
 
38
 
39
     public List<Map<String, Object>> getTodayTasks(Long userId) {
39
     public List<Map<String, Object>> getTodayTasks(Long userId) {
40
         return jdbc.queryForList(
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
             "WHERE pt.task_date = CURRENT_DATE AND pt.assignee_id = ? " +
43
             "WHERE pt.task_date = CURRENT_DATE AND pt.assignee_id = ? " +
44
             "ORDER BY pt.plan_start", userId);
44
             "ORDER BY pt.plan_start", userId);
45
     }
45
     }
46
 
46
 
47
     public Map<String, Object> startTask(Long taskId) {
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
         return Map.of("taskId", taskId, "status", "in_progress", "startedAt", new Date());
49
         return Map.of("taskId", taskId, "status", "in_progress", "startedAt", new Date());
50
     }
50
     }
51
 
51
 
52
     public Map<String, Object> completeTask(Long taskId, double distance) {
52
     public Map<String, Object> completeTask(Long taskId, double distance) {
53
         jdbc.update(
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
             distance, taskId);
55
             distance, taskId);
56
         return Map.of("taskId", taskId, "status", "completed", "distance", distance);
56
         return Map.of("taskId", taskId, "status", "completed", "distance", distance);
57
     }
57
     }
61
                                             List<Map<String, Object>> checkItems,
61
                                             List<Map<String, Object>> checkItems,
62
                                             double lng, double lat) {
62
                                             double lng, double lat) {
63
         jdbc.update(
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
             "VALUES (?,?,?,?::jsonb,?,?,NOW())",
65
             "VALUES (?,?,?,?::jsonb,?,?,NOW())",
66
             taskId, pointSeq, deviceId, checkItems.toString(), lng, lat);
66
             taskId, pointSeq, deviceId, checkItems.toString(), lng, lat);
67
         return Map.of("taskId", taskId, "pointSeq", pointSeq, "recorded", true);
67
         return Map.of("taskId", taskId, "pointSeq", pointSeq, "recorded", true);
69
 
69
 
70
     public List<Map<String, Object>> getTaskRecords(Long taskId) {
70
     public List<Map<String, Object>> getTaskRecords(Long taskId) {
71
         return jdbc.queryForList(
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
     // ========== 问题上报(巡检APP) ==========
75
     // ========== 问题上报(巡检APP) ==========
78
                                             double lng, double lat) {
78
                                             double lng, double lat) {
79
         // 自动创建工单
79
         // 自动创建工单
80
         jdbc.update(
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
             issueType + ": " + description.substring(0, Math.min(description.length(), 50)), taskId);
83
             issueType + ": " + description.substring(0, Math.min(description.length(), 50)), taskId);
84
 
84
 
85
         log.info("Issue reported: type={} desc={}", issueType, description);
85
         log.info("Issue reported: type={} desc={}", issueType, description);
93
         // 任务执行率
93
         // 任务执行率
94
         stats.put("completionRate", jdbc.queryForMap(
94
         stats.put("completionRate", jdbc.queryForMap(
95
             "SELECT COUNT(*) as total, SUM(CASE WHEN status='completed' THEN 1 ELSE 0 END) as completed " +
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
         stats.put("personDistance", jdbc.queryForList(
99
         stats.put("personDistance", jdbc.queryForList(
100
             "SELECT u.real_name, SUM(pt.distance) as total_km " +
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
             "WHERE pt.task_date BETWEEN ? AND ? GROUP BY u.id, u.real_name", start, end));
102
             "WHERE pt.task_date BETWEEN ? AND ? GROUP BY u.id, u.real_name", start, end));
103
 
103
 
104
         // 巡检工作量
104
         // 巡检工作量
105
         stats.put("workload", jdbc.queryForList(
105
         stats.put("workload", jdbc.queryForList(
106
             "SELECT task_date, COUNT(*) as tasks, SUM(distance) as total_km " +
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
             start, end));
108
             start, end));
109
 
109
 
110
         // 问题分类统计
110
         // 问题分类统计
111
         stats.put("issueStats", jdbc.queryForList(
111
         stats.put("issueStats", jdbc.queryForList(
112
             "SELECT SUBSTRING(task_name FROM '^[^:]+') as issue_type, COUNT(*) as count " +
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
             start, end));
114
             start, end));
115
 
115
 
116
         return stats;
116
         return stats;

+ 8
- 8
wm-patrol/src/main/java/com/water/patrol/service/PatrolWoService.java Просмотреть файл

23
                                        String location, Double lng, Double lat, Long taskId) {
23
                                        String location, Double lng, Double lat, Long taskId) {
24
         String orderNo = "PWO-" + System.currentTimeMillis();
24
         String orderNo = "PWO-" + System.currentTimeMillis();
25
         jdbc.update(
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
             "location, lng, lat, status) VALUES (?,?,?,?,?,?,?,?,?)",
27
             "location, lng, lat, status) VALUES (?,?,?,?,?,?,?,?,?)",
28
             orderNo, taskId, issueType, description,
28
             orderNo, taskId, issueType, description,
29
             severity != null ? severity : "medium",
29
             severity != null ? severity : "medium",
39
     /** 分派工单 */
39
     /** 分派工单 */
40
     public Map<String, Object> assign(Long woId, Long assigneeId, String assigneeName) {
40
     public Map<String, Object> assign(Long woId, Long assigneeId, String assigneeName) {
41
         int rows = jdbc.update(
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
             "WHERE id = ?", assigneeId, assigneeName, woId);
43
             "WHERE id = ?", assigneeId, assigneeName, woId);
44
         return Map.of("success", rows > 0, "woId", woId);
44
         return Map.of("success", rows > 0, "woId", woId);
45
     }
45
     }
47
     /** 处理工单 */
47
     /** 处理工单 */
48
     public Map<String, Object> process(Long woId, String resolution) {
48
     public Map<String, Object> process(Long woId, String resolution) {
49
         int rows = jdbc.update(
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
             resolution, woId);
51
             resolution, woId);
52
         return Map.of("success", rows > 0, "woId", woId);
52
         return Map.of("success", rows > 0, "woId", woId);
53
     }
53
     }
55
     /** 解决工单 */
55
     /** 解决工单 */
56
     public Map<String, Object> resolve(Long woId) {
56
     public Map<String, Object> resolve(Long woId) {
57
         int rows = jdbc.update(
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
         return Map.of("success", rows > 0, "woId", woId);
59
         return Map.of("success", rows > 0, "woId", woId);
60
     }
60
     }
61
 
61
 
62
     /** 工单详情 */
62
     /** 工单详情 */
63
     public Map<String, Object> getDetail(Long woId) {
63
     public Map<String, Object> getDetail(Long woId) {
64
         List<Map<String, Object>> rows = jdbc.queryForList(
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
         return rows.isEmpty() ? null : rows.get(0);
66
         return rows.isEmpty() ? null : rows.get(0);
67
     }
67
     }
68
 
68
 
69
     /** 工单列表 */
69
     /** 工单列表 */
70
     public Map<String, Object> list(String status, String severity, int page, int size) {
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
         List<Object> params = new ArrayList<>();
72
         List<Object> params = new ArrayList<>();
73
         if (status != null && !status.isEmpty()) {
73
         if (status != null && !status.isEmpty()) {
74
             sql.append(" AND status = ?");
74
             sql.append(" AND status = ?");
84
 
84
 
85
         List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
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
         List<Object> countParams = new ArrayList<>();
88
         List<Object> countParams = new ArrayList<>();
89
         if (status != null && !status.isEmpty()) {
89
         if (status != null && !status.isEmpty()) {
90
             countSql.append(" AND status = ?");
90
             countSql.append(" AND status = ?");
107
     /** 工单统计 */
107
     /** 工单统计 */
108
     public Map<String, Object> stats() {
108
     public Map<String, Object> stats() {
109
         List<Map<String, Object>> rows = jdbc.queryForList(
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
         Map<String, Object> result = new LinkedHashMap<>();
111
         Map<String, Object> result = new LinkedHashMap<>();
112
         for (Map<String, Object> row : rows) {
112
         for (Map<String, Object> row : rows) {
113
             result.put(String.valueOf(row.get("status")), ((Number) row.get("count")).longValue());
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 Просмотреть файл

3
 import com.water.patrol.entity.PatrolProblem;
3
 import com.water.patrol.entity.PatrolProblem;
4
 import com.water.patrol.entity.WorkOrder;
4
 import com.water.patrol.entity.WorkOrder;
5
 import com.water.patrol.entity.WorkOrderProcess;
5
 import com.water.patrol.entity.WorkOrderProcess;
6
+import com.water.patrol.mapper.PatrolProblemMapper;
6
 import com.water.patrol.mapper.WorkOrderMapper;
7
 import com.water.patrol.mapper.WorkOrderMapper;
7
 import com.water.patrol.mapper.WorkOrderProcessMapper;
8
 import com.water.patrol.mapper.WorkOrderProcessMapper;
8
 import com.water.patrol.service.WorkOrderService;
9
 import com.water.patrol.service.WorkOrderService;
22
     @Autowired
23
     @Autowired
23
     private WorkOrderProcessMapper workOrderProcessMapper;
24
     private WorkOrderProcessMapper workOrderProcessMapper;
24
 
25
 
26
+    @Autowired
27
+    private PatrolProblemMapper patrolProblemMapper;
28
+
25
     @Override
29
     @Override
26
     @Transactional
30
     @Transactional
27
     public boolean createFromProblem(PatrolProblem problem) {
31
     public boolean createFromProblem(PatrolProblem problem) {
55
             process.setCreatedAt(LocalDateTime.now());
59
             process.setCreatedAt(LocalDateTime.now());
56
             workOrderProcessMapper.insert(process);
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
             return true;
64
             return true;
61
         }
65
         }
62
         
66
         

+ 0
- 40
wm-patrol/src/main/java/patrol/PatrolCoreTest.java Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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

+ 20
- 20
wm-patrol/src/main/resources/mapper/WorkOrderMapper.xml Просмотреть файл

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

+ 5
- 5
wm-patrol/src/main/resources/mapper/WorkOrderProcessMapper.xml Просмотреть файл

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

+ 113
- 0
wm-patrol/src/main/resources/sql/V89__patrol_fix.sql Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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
+}