2 Incheckningar

Upphovsman SHA1 Meddelande Datum
  bot_dev2 54b5ee6a28 feat(wm-patrol): #87 巡查设置(区域/路线/表单/模版) 2 veckor sedan
  bot_dev2 9fc8140931 feat(wm-patrol): #87 巡查设置 2 veckor sedan
24 ändrade filer med 872 tillägg och 922 borttagningar
  1. 68
    0
      sql/V86__patrol_core.sql
  2. 70
    0
      sql/V87__patrol_setup.sql
  3. 0
    16
      wm-patrol/src/main/java/com/water/patrol/controller/PatrolAreaController.java
  4. 0
    186
      wm-patrol/src/main/java/com/water/patrol/controller/PatrolCoreController.java
  5. 0
    15
      wm-patrol/src/main/java/com/water/patrol/controller/PatrolFormController.java
  6. 0
    16
      wm-patrol/src/main/java/com/water/patrol/controller/PatrolRouteSetupController.java
  7. 241
    0
      wm-patrol/src/main/java/com/water/patrol/controller/PatrolSetupController.java
  8. 0
    16
      wm-patrol/src/main/java/com/water/patrol/controller/PatrolTemplateController.java
  9. 0
    15
      wm-patrol/src/main/java/com/water/patrol/entity/PatrolArea.java
  10. 0
    13
      wm-patrol/src/main/java/com/water/patrol/entity/PatrolForm.java
  11. 0
    15
      wm-patrol/src/main/java/com/water/patrol/entity/PatrolRouteSetup.java
  12. 0
    15
      wm-patrol/src/main/java/com/water/patrol/entity/PatrolTemplate.java
  13. 0
    5
      wm-patrol/src/main/java/com/water/patrol/mapper/PatrolAreaMapper.java
  14. 0
    5
      wm-patrol/src/main/java/com/water/patrol/mapper/PatrolFormMapper.java
  15. 0
    5
      wm-patrol/src/main/java/com/water/patrol/mapper/PatrolRouteSetupMapper.java
  16. 0
    5
      wm-patrol/src/main/java/com/water/patrol/mapper/PatrolTemplateMapper.java
  17. 107
    44
      wm-patrol/src/main/java/com/water/patrol/service/PatrolAreaService.java
  18. 0
    252
      wm-patrol/src/main/java/com/water/patrol/service/PatrolCoreService.java
  19. 94
    36
      wm-patrol/src/main/java/com/water/patrol/service/PatrolFormService.java
  20. 154
    43
      wm-patrol/src/main/java/com/water/patrol/service/PatrolRouteSetupService.java
  21. 138
    49
      wm-patrol/src/main/java/com/water/patrol/service/PatrolTemplateService.java
  22. 0
    122
      wm-patrol/src/main/java/com/water/patrol/service/PatrolWoService.java
  23. 0
    24
      wm-patrol/src/main/resources/sql/V87__patrol_setup.sql
  24. 0
    25
      wm-patrol/src/test/java/com/water/patrol/PatrolSetupTest.java

+ 68
- 0
sql/V86__patrol_core.sql Visa fil

@@ -0,0 +1,68 @@
1
+-- Issue #86: 巡检管理核心(总览/轨迹/台账/设备/工单)
2
+
3
+-- 1. 巡检设备台账
4
+CREATE TABLE IF NOT EXISTS patrol_device (
5
+    id              BIGSERIAL PRIMARY KEY,
6
+    device_no       VARCHAR(50) UNIQUE,
7
+    device_name     VARCHAR(100) NOT NULL,
8
+    device_type     VARCHAR(30) NOT NULL,        -- valve/meter/hydrant/pump/sensor
9
+    location        VARCHAR(300),
10
+    lng             DOUBLE PRECISION,
11
+    lat             DOUBLE PRECISION,
12
+    status          VARCHAR(20) DEFAULT 'normal', -- normal/abnormal/offline/maintenance
13
+    area            VARCHAR(50),
14
+    point_seq       INT DEFAULT 0,
15
+    remark          TEXT,
16
+    last_maintenance_date TIMESTAMPTZ,
17
+    created_at      TIMESTAMPTZ DEFAULT NOW(),
18
+    updated_at      TIMESTAMPTZ DEFAULT NOW()
19
+);
20
+CREATE INDEX IF NOT EXISTS idx_patrol_device_area ON patrol_device(area);
21
+CREATE INDEX IF NOT EXISTS idx_patrol_device_type ON patrol_device(device_type);
22
+CREATE INDEX IF NOT EXISTS idx_patrol_device_status ON patrol_device(status);
23
+
24
+-- 2. GPS轨迹点
25
+CREATE TABLE IF NOT EXISTS patrol_track_point (
26
+    id              BIGSERIAL PRIMARY KEY,
27
+    task_id         BIGINT NOT NULL REFERENCES patrol_task(id),
28
+    worker_id       BIGINT,
29
+    lng             DOUBLE PRECISION NOT NULL,
30
+    lat             DOUBLE PRECISION NOT NULL,
31
+    speed           DOUBLE PRECISION DEFAULT 0,
32
+    accuracy        DOUBLE PRECISION DEFAULT 0,
33
+    recorded_at     TIMESTAMPTZ DEFAULT NOW()
34
+);
35
+CREATE INDEX IF NOT EXISTS idx_track_task ON patrol_track_point(task_id, recorded_at);
36
+
37
+-- 3. 巡检工单增强(如果 patrol_work_order 已存在则 ALTER,否则 CREATE)
38
+-- 先检查 patrol_work_order 是否存在
39
+DO $$
40
+BEGIN
41
+    IF NOT EXISTS (SELECT FROM pg_tables WHERE tablename = 'patrol_work_order') THEN
42
+        CREATE TABLE patrol_work_order (
43
+            id              BIGSERIAL PRIMARY KEY,
44
+            order_no        VARCHAR(50) UNIQUE NOT NULL,
45
+            task_id         BIGINT REFERENCES patrol_task(id),
46
+            issue_type      VARCHAR(30) NOT NULL,     -- leak/damage/abnormal/pollution/other
47
+            description     TEXT,
48
+            photos          TEXT,                      -- JSON array
49
+            severity        VARCHAR(10) DEFAULT 'medium', -- low/medium/high/critical
50
+            assignee_id     BIGINT,
51
+            assignee_name   VARCHAR(50),
52
+            status          VARCHAR(20) DEFAULT 'pending', -- pending/assigned/processing/resolved/closed
53
+            location        VARCHAR(300),
54
+            lng             DOUBLE PRECISION,
55
+            lat             DOUBLE PRECISION,
56
+            resolution      TEXT,
57
+            created_at      TIMESTAMPTZ DEFAULT NOW(),
58
+            resolved_at     TIMESTAMPTZ,
59
+            closed_at       TIMESTAMPTZ
60
+        );
61
+        CREATE INDEX IF NOT EXISTS idx_pwo_status ON patrol_work_order(status);
62
+        CREATE INDEX IF NOT EXISTS idx_pwo_assignee ON patrol_work_order(assignee_id);
63
+    END IF;
64
+END $$;
65
+
66
+-- 4. 设备巡检点检记录扩展(给 patrol_record 加 device_status 字段)
67
+ALTER TABLE patrol_record ADD COLUMN IF NOT EXISTS device_status VARCHAR(20) DEFAULT 'normal';
68
+ALTER TABLE patrol_record ADD COLUMN IF NOT EXISTS remark TEXT;

+ 70
- 0
sql/V87__patrol_setup.sql Visa fil

@@ -0,0 +1,70 @@
1
+-- Issue #87: 巡查设置(区域/路线/表单/模版)
2
+
3
+-- PAT-16: 巡检区域
4
+CREATE TABLE IF NOT EXISTS patrol_area (
5
+    id              BIGSERIAL PRIMARY KEY,
6
+    name            VARCHAR(100) NOT NULL,
7
+    code            VARCHAR(50) UNIQUE,
8
+    parent_id       BIGINT DEFAULT 0,
9
+    description     TEXT,
10
+    lng             DOUBLE PRECISION,
11
+    lat             DOUBLE PRECISION,
12
+    radius          DOUBLE PRECISION DEFAULT 0,
13
+    status          VARCHAR(20) DEFAULT 'active',
14
+    created_at      TIMESTAMPTZ DEFAULT NOW(),
15
+    updated_at      TIMESTAMPTZ DEFAULT NOW()
16
+);
17
+CREATE INDEX IF NOT EXISTS idx_patrol_area_parent ON patrol_area(parent_id);
18
+
19
+-- PAT-17: 巡检路线扩展
20
+-- patrol_route 已在 V1__production.sql 创建,此处补充缺失字段
21
+ALTER TABLE patrol_route ADD COLUMN IF NOT EXISTS area_id BIGINT;
22
+ALTER TABLE patrol_route ADD COLUMN IF NOT EXISTS description TEXT;
23
+ALTER TABLE patrol_route ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT NOW();
24
+
25
+-- 巡检路线巡检点
26
+CREATE TABLE IF NOT EXISTS patrol_route_checkpoint (
27
+    id              BIGSERIAL PRIMARY KEY,
28
+    route_id        BIGINT NOT NULL,
29
+    checkpoint_seq  INT NOT NULL,
30
+    device_id       BIGINT,
31
+    device_name     VARCHAR(100),
32
+    lng             DOUBLE PRECISION,
33
+    lat             DOUBLE PRECISION,
34
+    check_items     TEXT,  -- JSON array
35
+    created_at      TIMESTAMPTZ DEFAULT NOW()
36
+);
37
+CREATE INDEX IF NOT EXISTS idx_route_checkpoint ON patrol_route_checkpoint(route_id);
38
+
39
+-- PAT-18: 自定义巡检表单
40
+CREATE TABLE IF NOT EXISTS patrol_form (
41
+    id              BIGSERIAL PRIMARY KEY,
42
+    name            VARCHAR(100) NOT NULL,
43
+    type            VARCHAR(30) NOT NULL,  -- daily/special/safety/quality
44
+    fields          TEXT NOT NULL,          -- JSON array of field definitions
45
+    status          VARCHAR(20) DEFAULT 'active',
46
+    created_at      TIMESTAMPTZ DEFAULT NOW(),
47
+    updated_at      TIMESTAMPTZ DEFAULT NOW()
48
+);
49
+
50
+-- PAT-19: 表单绑定关系
51
+CREATE TABLE IF NOT EXISTS patrol_form_binding (
52
+    id              BIGSERIAL PRIMARY KEY,
53
+    form_id         BIGINT NOT NULL,
54
+    target_type     VARCHAR(20) NOT NULL,  -- route/device/area
55
+    target_id       BIGINT NOT NULL,
56
+    created_at      TIMESTAMPTZ DEFAULT NOW(),
57
+    UNIQUE(form_id, target_type, target_id)
58
+);
59
+CREATE INDEX IF NOT EXISTS idx_form_binding_target ON patrol_form_binding(target_type, target_id);
60
+
61
+-- PAT-20: 巡检模板
62
+CREATE TABLE IF NOT EXISTS patrol_template (
63
+    id              BIGSERIAL PRIMARY KEY,
64
+    name            VARCHAR(100) NOT NULL,
65
+    type            VARCHAR(20) NOT NULL,  -- daily/weekly/monthly/special
66
+    config          TEXT NOT NULL,          -- JSON: frequency, routes, workers, schedule
67
+    status          VARCHAR(20) DEFAULT 'active',
68
+    created_at      TIMESTAMPTZ DEFAULT NOW(),
69
+    updated_at      TIMESTAMPTZ DEFAULT NOW()
70
+);

+ 0
- 16
wm-patrol/src/main/java/com/water/patrol/controller/PatrolAreaController.java Visa fil

@@ -1,16 +0,0 @@
1
-package com.water.patrol.controller;
2
-import com.water.common.core.result.R; import com.water.patrol.entity.PatrolArea; import com.water.patrol.service.PatrolAreaService;
3
-import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag;
4
-import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*;
5
-import java.math.BigDecimal; import java.util.Map;
6
-@Tag(name="巡检区域") @RestController @RequestMapping("/api/patrol/area") @RequiredArgsConstructor
7
-public class PatrolAreaController {
8
-    private final PatrolAreaService svc;
9
-    @Operation(summary="创建区域") @PostMapping public R<PatrolArea> create(@RequestParam String areaName, @RequestParam String areaCode, @RequestParam(required=false) String description, @RequestParam BigDecimal centerLng, @RequestParam BigDecimal centerLat, @RequestParam(required=false) String boundary, @RequestParam(required=false) Double radius) { return R.ok(svc.create(areaName,areaCode,description,centerLng,centerLat,boundary,radius)); }
10
-    @Operation(summary="区域列表") @GetMapping public R<Map<String,Object>> list(@RequestParam(required=false) String keyword, @RequestParam(required=false) String status, @RequestParam(defaultValue="1") int page, @RequestParam(defaultValue="20") int size) { return R.ok(svc.list(keyword,status,page,size)); }
11
-    @Operation(summary="区域详情") @GetMapping("/{id}") public R<PatrolArea> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
12
-    @Operation(summary="更新区域") @PutMapping("/{id}") public R<PatrolArea> update(@PathVariable Long id, @RequestBody PatrolArea patch) { return R.ok(svc.update(id,patch)); }
13
-    @Operation(summary="更新状态") @PutMapping("/{id}/status") public R<String> updateStatus(@PathVariable Long id, @RequestParam String status) { svc.updateStatus(id,status); return R.ok("OK"); }
14
-    @Operation(summary="删除区域") @DeleteMapping("/{id}") public R<String> delete(@PathVariable Long id) { svc.delete(id); return R.ok("OK"); }
15
-    @Operation(summary="区域统计") @GetMapping("/stats") public R<Map<String,Object>> stats() { return R.ok(svc.getStats()); }
16
-}

+ 0
- 186
wm-patrol/src/main/java/com/water/patrol/controller/PatrolCoreController.java Visa fil

@@ -1,186 +0,0 @@
1
-package com.water.patrol.controller;
2
-
3
-import com.water.common.core.result.R;
4
-import com.water.patrol.service.PatrolCoreService;
5
-import com.water.patrol.service.PatrolWoService;
6
-import io.swagger.v3.oas.annotations.Operation;
7
-import io.swagger.v3.oas.annotations.tags.Tag;
8
-import lombok.RequiredArgsConstructor;
9
-import org.springframework.web.bind.annotation.*;
10
-
11
-import java.util.List;
12
-import java.util.Map;
13
-
14
-/**
15
- * 巡检管理核心控制器 - Issue #86
16
- * PAT-01~PAT-06
17
- */
18
-@Tag(name = "巡检管理核心")
19
-@RestController
20
-@RequestMapping("/patrol/core")
21
-@RequiredArgsConstructor
22
-public class PatrolCoreController {
23
-
24
-    private final PatrolCoreService coreService;
25
-    private final PatrolWoService woService;
26
-
27
-    // ========== PAT-01 巡检总览 ==========
28
-
29
-    @GetMapping("/overview")
30
-    @Operation(summary = "巡检总览数据")
31
-    public R<Map<String, Object>> overview() {
32
-        return R.ok(coreService.getOverview());
33
-    }
34
-
35
-    @GetMapping("/overview/today-tasks")
36
-    @Operation(summary = "今日任务")
37
-    public R<List<Map<String, Object>>> todayTasks() {
38
-        return R.ok(coreService.getTodayTasks());
39
-    }
40
-
41
-    @GetMapping("/overview/recent-issues")
42
-    @Operation(summary = "最近问题")
43
-    public R<List<Map<String, Object>>> recentIssues(@RequestParam(defaultValue = "10") int limit) {
44
-        return R.ok(coreService.getRecentIssues(limit));
45
-    }
46
-
47
-    // ========== PAT-02 任务轨迹 ==========
48
-
49
-    @PostMapping("/track/record")
50
-    @Operation(summary = "记录GPS轨迹点")
51
-    public R<Map<String, Object>> recordTrack(@RequestBody Map<String, Object> req) {
52
-        Long taskId = Long.parseLong(String.valueOf(req.get("taskId")));
53
-        Long workerId = req.get("workerId") != null ? Long.parseLong(String.valueOf(req.get("workerId"))) : null;
54
-        double lng = ((Number) req.get("lng")).doubleValue();
55
-        double lat = ((Number) req.get("lat")).doubleValue();
56
-        double speed = req.get("speed") != null ? ((Number) req.get("speed")).doubleValue() : 0;
57
-        double accuracy = req.get("accuracy") != null ? ((Number) req.get("accuracy")).doubleValue() : 0;
58
-        return R.ok(coreService.recordTrackPoint(taskId, workerId, lng, lat, speed, accuracy));
59
-    }
60
-
61
-    @GetMapping("/track/{taskId}")
62
-    @Operation(summary = "获取任务轨迹")
63
-    public R<List<Map<String, Object>>> getTrack(@PathVariable Long taskId) {
64
-        return R.ok(coreService.getTrack(taskId));
65
-    }
66
-
67
-    @GetMapping("/track/{taskId}/replay")
68
-    @Operation(summary = "轨迹回放")
69
-    public R<Map<String, Object>> replayTrack(@PathVariable Long taskId) {
70
-        return R.ok(coreService.replayTrack(taskId));
71
-    }
72
-
73
-    @GetMapping("/track/{taskId}/stats")
74
-    @Operation(summary = "轨迹统计")
75
-    public R<Map<String, Object>> trackStats(@PathVariable Long taskId) {
76
-        return R.ok(coreService.getTrackStats(taskId));
77
-    }
78
-
79
-    // ========== PAT-03 任务台账 ==========
80
-
81
-    @GetMapping("/ledger/tasks")
82
-    @Operation(summary = "任务台账")
83
-    public R<Map<String, Object>> taskLedger(
84
-            @RequestParam(required = false) String status,
85
-            @RequestParam(required = false) Long workerId,
86
-            @RequestParam(defaultValue = "1") int page,
87
-            @RequestParam(defaultValue = "20") int size) {
88
-        return R.ok(coreService.getTaskLedger(status, workerId, page, size));
89
-    }
90
-
91
-    // ========== PAT-04 设备台账 ==========
92
-
93
-    @GetMapping("/ledger/devices")
94
-    @Operation(summary = "设备台账")
95
-    public R<Map<String, Object>> deviceLedger(
96
-            @RequestParam(required = false) String type,
97
-            @RequestParam(required = false) String status,
98
-            @RequestParam(required = false) String area,
99
-            @RequestParam(defaultValue = "1") int page,
100
-            @RequestParam(defaultValue = "20") int size) {
101
-        return R.ok(coreService.getDeviceLedger(type, status, area, page, size));
102
-    }
103
-
104
-    @PostMapping("/device")
105
-    @Operation(summary = "新增设备")
106
-    public R<Map<String, Object>> createDevice(@RequestBody Map<String, Object> req) {
107
-        return R.ok(coreService.createDevice(
108
-            (String) req.get("deviceNo"),
109
-            (String) req.get("deviceName"),
110
-            (String) req.get("deviceType"),
111
-            (String) req.get("location"),
112
-            ((Number) req.getOrDefault("lng", 0)).doubleValue(),
113
-            ((Number) req.getOrDefault("lat", 0)).doubleValue(),
114
-            (String) req.get("area")));
115
-    }
116
-
117
-    @PutMapping("/device/{deviceId}/status")
118
-    @Operation(summary = "更新设备状态")
119
-    public R<Map<String, Object>> updateDeviceStatus(@PathVariable Long deviceId,
120
-                                                      @RequestBody Map<String, Object> req) {
121
-        return R.ok(coreService.updateDeviceStatus(deviceId, (String) req.get("status")));
122
-    }
123
-
124
-    // ========== PAT-05 工单管理 ==========
125
-
126
-    @PostMapping("/work-order")
127
-    @Operation(summary = "创建巡检工单")
128
-    public R<Map<String, Object>> createWo(@RequestBody Map<String, Object> req) {
129
-        Long taskId = req.get("taskId") != null ? Long.parseLong(String.valueOf(req.get("taskId"))) : null;
130
-        return R.ok(woService.create(
131
-            (String) req.get("issueType"),
132
-            (String) req.get("description"),
133
-            (String) req.get("severity"),
134
-            (String) req.get("location"),
135
-            req.get("lng") != null ? ((Number) req.get("lng")).doubleValue() : null,
136
-            req.get("lat") != null ? ((Number) req.get("lat")).doubleValue() : null,
137
-            taskId));
138
-    }
139
-
140
-    @GetMapping("/work-order/list")
141
-    @Operation(summary = "工单列表")
142
-    public R<Map<String, Object>> listWo(
143
-            @RequestParam(required = false) String status,
144
-            @RequestParam(required = false) String severity,
145
-            @RequestParam(defaultValue = "1") int page,
146
-            @RequestParam(defaultValue = "20") int size) {
147
-        return R.ok(woService.list(status, severity, page, size));
148
-    }
149
-
150
-    @GetMapping("/work-order/{woId}")
151
-    @Operation(summary = "工单详情")
152
-    public R<Map<String, Object>> woDetail(@PathVariable Long woId) {
153
-        Map<String, Object> detail = woService.getDetail(woId);
154
-        if (detail == null) {
155
-            return R.fail("工单不存在");
156
-        }
157
-        return R.ok(detail);
158
-    }
159
-
160
-    @GetMapping("/work-order/stats")
161
-    @Operation(summary = "工单统计")
162
-    public R<Map<String, Object>> woStats() {
163
-        return R.ok(woService.stats());
164
-    }
165
-
166
-    // ========== PAT-06 工单处理 ==========
167
-
168
-    @PutMapping("/work-order/{woId}/assign")
169
-    @Operation(summary = "分派工单")
170
-    public R<Map<String, Object>> assignWo(@PathVariable Long woId, @RequestBody Map<String, Object> req) {
171
-        Long assigneeId = Long.parseLong(String.valueOf(req.get("assigneeId")));
172
-        return R.ok(woService.assign(woId, assigneeId, (String) req.get("assigneeName")));
173
-    }
174
-
175
-    @PutMapping("/work-order/{woId}/process")
176
-    @Operation(summary = "处理工单")
177
-    public R<Map<String, Object>> processWo(@PathVariable Long woId, @RequestBody Map<String, Object> req) {
178
-        return R.ok(woService.process(woId, (String) req.get("resolution")));
179
-    }
180
-
181
-    @PutMapping("/work-order/{woId}/resolve")
182
-    @Operation(summary = "解决工单")
183
-    public R<Map<String, Object>> resolveWo(@PathVariable Long woId) {
184
-        return R.ok(woService.resolve(woId));
185
-    }
186
-}

+ 0
- 15
wm-patrol/src/main/java/com/water/patrol/controller/PatrolFormController.java Visa fil

@@ -1,15 +0,0 @@
1
-package com.water.patrol.controller;
2
-import com.water.common.core.result.R; import com.water.patrol.entity.PatrolForm; import com.water.patrol.service.PatrolFormService;
3
-import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag;
4
-import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*;
5
-import java.util.Map;
6
-@Tag(name="巡检表单") @RestController @RequestMapping("/api/patrol/form") @RequiredArgsConstructor
7
-public class PatrolFormController {
8
-    private final PatrolFormService svc;
9
-    @Operation(summary="创建表单") @PostMapping public R<PatrolForm> create(@RequestBody Map<String,Object> req) { return R.ok(svc.create((String)req.get("formName"),(String)req.get("formCode"),(String)req.get("formType"),(String)req.get("fieldConfig"))); }
10
-    @Operation(summary="表单列表") @GetMapping public R<Map<String,Object>> list(@RequestParam(required=false) String keyword, @RequestParam(required=false) String formType, @RequestParam(required=false) String status, @RequestParam(defaultValue="1") int page, @RequestParam(defaultValue="20") int size) { return R.ok(svc.list(keyword,formType,status,page,size)); }
11
-    @Operation(summary="表单详情") @GetMapping("/{id}") public R<PatrolForm> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
12
-    @Operation(summary="更新表单") @PutMapping("/{id}") public R<PatrolForm> update(@PathVariable Long id, @RequestBody PatrolForm patch) { return R.ok(svc.update(id,patch)); }
13
-    @Operation(summary="删除表单") @DeleteMapping("/{id}") public R<String> delete(@PathVariable Long id) { svc.delete(id); return R.ok("OK"); }
14
-    @Operation(summary="表单统计") @GetMapping("/stats") public R<Map<String,Object>> stats() { return R.ok(svc.getStats()); }
15
-}

+ 0
- 16
wm-patrol/src/main/java/com/water/patrol/controller/PatrolRouteSetupController.java Visa fil

@@ -1,16 +0,0 @@
1
-package com.water.patrol.controller;
2
-import com.water.common.core.result.R; import com.water.patrol.entity.PatrolRouteSetup; import com.water.patrol.service.PatrolRouteSetupService;
3
-import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag;
4
-import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*;
5
-import java.util.Map;
6
-@Tag(name="巡检路线设置") @RestController @RequestMapping("/api/patrol/route-setup") @RequiredArgsConstructor
7
-public class PatrolRouteSetupController {
8
-    private final PatrolRouteSetupService svc;
9
-    @Operation(summary="创建路线") @PostMapping public R<PatrolRouteSetup> create(@RequestBody Map<String,Object> req) { return R.ok(svc.create((String)req.get("routeName"),(String)req.get("routeCode"),((Number)req.get("areaId")).longValue(),(String)req.get("areaName"),(Integer)req.get("checkpointCount"),(String)req.get("checkpointList"),(Double)req.get("totalDistance"),(Integer)req.get("estimatedMinutes"))); }
10
-    @Operation(summary="路线列表") @GetMapping public R<Map<String,Object>> list(@RequestParam(required=false) String keyword, @RequestParam(required=false) String status, @RequestParam(required=false) Long areaId, @RequestParam(defaultValue="1") int page, @RequestParam(defaultValue="20") int size) { return R.ok(svc.list(keyword,status,areaId,page,size)); }
11
-    @Operation(summary="路线详情") @GetMapping("/{id}") public R<PatrolRouteSetup> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
12
-    @Operation(summary="更新路线") @PutMapping("/{id}") public R<PatrolRouteSetup> update(@PathVariable Long id, @RequestBody PatrolRouteSetup patch) { return R.ok(svc.update(id,patch)); }
13
-    @Operation(summary="更新状态") @PutMapping("/{id}/status") public R<String> updateStatus(@PathVariable Long id, @RequestParam String status) { svc.updateStatus(id,status); return R.ok("OK"); }
14
-    @Operation(summary="删除路线") @DeleteMapping("/{id}") public R<String> delete(@PathVariable Long id) { svc.delete(id); return R.ok("OK"); }
15
-    @Operation(summary="路线统计") @GetMapping("/stats") public R<Map<String,Object>> stats() { return R.ok(svc.getStats()); }
16
-}

+ 241
- 0
wm-patrol/src/main/java/com/water/patrol/controller/PatrolSetupController.java Visa fil

@@ -0,0 +1,241 @@
1
+package com.water.patrol.controller;
2
+
3
+import com.water.common.core.result.R;
4
+import com.water.patrol.service.PatrolAreaService;
5
+import com.water.patrol.service.PatrolFormService;
6
+import com.water.patrol.service.PatrolRouteSetupService;
7
+import com.water.patrol.service.PatrolTemplateService;
8
+import io.swagger.v3.oas.annotations.Operation;
9
+import io.swagger.v3.oas.annotations.tags.Tag;
10
+import lombok.RequiredArgsConstructor;
11
+import org.springframework.web.bind.annotation.*;
12
+
13
+import java.util.List;
14
+import java.util.Map;
15
+
16
+/**
17
+ * 巡查设置控制器 - Issue #87
18
+ * PAT-16 区域设置 / PAT-17 路线设置 / PAT-18 自定义表单 / PAT-19 表单挂接 / PAT-20 模版设置
19
+ */
20
+@Tag(name = "巡查设置")
21
+@RestController
22
+@RequestMapping("/patrol/setup")
23
+@RequiredArgsConstructor
24
+public class PatrolSetupController {
25
+
26
+    private final PatrolAreaService areaService;
27
+    private final PatrolRouteSetupService routeService;
28
+    private final PatrolFormService formService;
29
+    private final PatrolTemplateService templateService;
30
+
31
+    // ========== PAT-16 区域设置 ==========
32
+
33
+    @PostMapping("/area")
34
+    @Operation(summary = "创建巡检区域")
35
+    public R<Map<String, Object>> createArea(@RequestBody Map<String, Object> req) {
36
+        return R.ok(areaService.create(
37
+            (String) req.get("name"),
38
+            (String) req.get("code"),
39
+            req.get("parentId") != null ? Long.parseLong(String.valueOf(req.get("parentId"))) : null,
40
+            (String) req.get("description"),
41
+            req.get("lng") != null ? ((Number) req.get("lng")).doubleValue() : null,
42
+            req.get("lat") != null ? ((Number) req.get("lat")).doubleValue() : null,
43
+            req.get("radius") != null ? ((Number) req.get("radius")).doubleValue() : null));
44
+    }
45
+
46
+    @PutMapping("/area/{id}")
47
+    @Operation(summary = "更新巡检区域")
48
+    public R<Map<String, Object>> updateArea(@PathVariable Long id, @RequestBody Map<String, Object> req) {
49
+        return R.ok(areaService.update(id,
50
+            (String) req.get("name"),
51
+            (String) req.get("code"),
52
+            req.get("parentId") != null ? Long.parseLong(String.valueOf(req.get("parentId"))) : null,
53
+            (String) req.get("description"),
54
+            req.get("lng") != null ? ((Number) req.get("lng")).doubleValue() : null,
55
+            req.get("lat") != null ? ((Number) req.get("lat")).doubleValue() : null,
56
+            req.get("radius") != null ? ((Number) req.get("radius")).doubleValue() : null));
57
+    }
58
+
59
+    @DeleteMapping("/area/{id}")
60
+    @Operation(summary = "删除巡检区域")
61
+    public R<Void> deleteArea(@PathVariable Long id) {
62
+        areaService.delete(id);
63
+        return R.ok(null);
64
+    }
65
+
66
+    @GetMapping("/area/list")
67
+    @Operation(summary = "区域列表(支持树形)")
68
+    public R<Map<String, Object>> listAreas(@RequestParam(required = false) Long parentId) {
69
+        return R.ok(areaService.list(parentId));
70
+    }
71
+
72
+    @GetMapping("/area/{id}")
73
+    @Operation(summary = "区域详情(含统计)")
74
+    public R<Map<String, Object>> getAreaDetail(@PathVariable Long id) {
75
+        Map<String, Object> detail = areaService.getDetail(id);
76
+        if (detail == null) return R.fail("区域不存在");
77
+        return R.ok(detail);
78
+    }
79
+
80
+    // ========== PAT-17 路线设置 ==========
81
+
82
+    @PostMapping("/route")
83
+    @Operation(summary = "创建巡检路线")
84
+    @SuppressWarnings("unchecked")
85
+    public R<Map<String, Object>> createRoute(@RequestBody Map<String, Object> req) {
86
+        List<Map<String, Object>> checkpoints = (List<Map<String, Object>>) req.get("checkpoints");
87
+        return R.ok(routeService.create(
88
+            (String) req.get("name"),
89
+            req.get("areaId") != null ? Long.parseLong(String.valueOf(req.get("areaId"))) : null,
90
+            checkpoints,
91
+            req.get("estimDuration") != null ? ((Number) req.get("estimDuration")).intValue() : null,
92
+            (String) req.get("description")));
93
+    }
94
+
95
+    @PutMapping("/route/{id}")
96
+    @Operation(summary = "更新巡检路线")
97
+    @SuppressWarnings("unchecked")
98
+    public R<Map<String, Object>> updateRoute(@PathVariable Long id, @RequestBody Map<String, Object> req) {
99
+        List<Map<String, Object>> checkpoints = (List<Map<String, Object>>) req.get("checkpoints");
100
+        return R.ok(routeService.update(id,
101
+            (String) req.get("name"),
102
+            req.get("areaId") != null ? Long.parseLong(String.valueOf(req.get("areaId"))) : null,
103
+            checkpoints,
104
+            req.get("estimDuration") != null ? ((Number) req.get("estimDuration")).intValue() : null,
105
+            (String) req.get("description")));
106
+    }
107
+
108
+    @DeleteMapping("/route/{id}")
109
+    @Operation(summary = "删除巡检路线")
110
+    public R<Void> deleteRoute(@PathVariable Long id) {
111
+        routeService.delete(id);
112
+        return R.ok(null);
113
+    }
114
+
115
+    @GetMapping("/route/list")
116
+    @Operation(summary = "路线列表")
117
+    public R<Map<String, Object>> listRoutes(
118
+            @RequestParam(required = false) Long areaId,
119
+            @RequestParam(defaultValue = "1") int page,
120
+            @RequestParam(defaultValue = "20") int size) {
121
+        return R.ok(routeService.list(areaId, page, size));
122
+    }
123
+
124
+    @GetMapping("/route/{id}")
125
+    @Operation(summary = "路线详情(含巡检点列表)")
126
+    public R<Map<String, Object>> getRouteDetail(@PathVariable Long id) {
127
+        Map<String, Object> detail = routeService.getDetail(id);
128
+        if (detail == null) return R.fail("路线不存在");
129
+        return R.ok(detail);
130
+    }
131
+
132
+    @PostMapping("/route/{id}/copy")
133
+    @Operation(summary = "复制路线")
134
+    public R<Map<String, Object>> copyRoute(@PathVariable Long id) {
135
+        return R.ok(routeService.copyRoute(id));
136
+    }
137
+
138
+    // ========== PAT-18 自定义表单 + PAT-19 表单挂接 ==========
139
+
140
+    @PostMapping("/form")
141
+    @Operation(summary = "创建巡检表单")
142
+    @SuppressWarnings("unchecked")
143
+    public R<Map<String, Object>> createForm(@RequestBody Map<String, Object> req) {
144
+        List<Map<String, Object>> fields = (List<Map<String, Object>>) req.get("fields");
145
+        return R.ok(formService.createForm(
146
+            (String) req.get("name"),
147
+            (String) req.get("type"),
148
+            fields));
149
+    }
150
+
151
+    @GetMapping("/form/list")
152
+    @Operation(summary = "表单列表")
153
+    public R<Map<String, Object>> listForms(@RequestParam(required = false) String type) {
154
+        return R.ok(formService.listForms(type));
155
+    }
156
+
157
+    @GetMapping("/form/{id}")
158
+    @Operation(summary = "表单详情(含字段定义)")
159
+    public R<Map<String, Object>> getFormDetail(@PathVariable Long id) {
160
+        Map<String, Object> form = formService.getForm(id);
161
+        if (form == null) return R.fail("表单不存在");
162
+        return R.ok(form);
163
+    }
164
+
165
+    @PostMapping("/form/{id}/bind")
166
+    @Operation(summary = "挂接表单")
167
+    public R<Map<String, Object>> bindForm(@PathVariable Long id, @RequestBody Map<String, Object> req) {
168
+        String targetType = (String) req.get("targetType");
169
+        Long targetId = Long.parseLong(String.valueOf(req.get("targetId")));
170
+        return R.ok(formService.bindForm(id, targetType, targetId));
171
+    }
172
+
173
+    @PostMapping("/form/{id}/unbind")
174
+    @Operation(summary = "解绑表单")
175
+    public R<Map<String, Object>> unbindForm(@PathVariable Long id, @RequestBody Map<String, Object> req) {
176
+        String targetType = (String) req.get("targetType");
177
+        Long targetId = Long.parseLong(String.valueOf(req.get("targetId")));
178
+        return R.ok(formService.unbindForm(id, targetType, targetId));
179
+    }
180
+
181
+    @GetMapping("/form/bound")
182
+    @Operation(summary = "获取已绑定的表单")
183
+    public R<Map<String, Object>> getBoundForms(
184
+            @RequestParam String targetType,
185
+            @RequestParam Long targetId) {
186
+        return R.ok(formService.getBoundForms(targetType, targetId));
187
+    }
188
+
189
+    // ========== PAT-20 模版设置 ==========
190
+
191
+    @PostMapping("/template")
192
+    @Operation(summary = "创建巡检模板")
193
+    @SuppressWarnings("unchecked")
194
+    public R<Map<String, Object>> createTemplate(@RequestBody Map<String, Object> req) {
195
+        Map<String, Object> config = (Map<String, Object>) req.get("config");
196
+        return R.ok(templateService.create(
197
+            (String) req.get("name"),
198
+            (String) req.get("type"),
199
+            config));
200
+    }
201
+
202
+    @PutMapping("/template/{id}")
203
+    @Operation(summary = "更新巡检模板")
204
+    @SuppressWarnings("unchecked")
205
+    public R<Map<String, Object>> updateTemplate(@PathVariable Long id, @RequestBody Map<String, Object> req) {
206
+        Map<String, Object> config = (Map<String, Object>) req.get("config");
207
+        return R.ok(templateService.update(id,
208
+            (String) req.get("name"),
209
+            (String) req.get("type"),
210
+            config));
211
+    }
212
+
213
+    @DeleteMapping("/template/{id}")
214
+    @Operation(summary = "删除巡检模板")
215
+    public R<Void> deleteTemplate(@PathVariable Long id) {
216
+        templateService.delete(id);
217
+        return R.ok(null);
218
+    }
219
+
220
+    @GetMapping("/template/list")
221
+    @Operation(summary = "模板列表")
222
+    public R<Map<String, Object>> listTemplates(@RequestParam(required = false) String type) {
223
+        return R.ok(templateService.list(type));
224
+    }
225
+
226
+    @GetMapping("/template/{id}")
227
+    @Operation(summary = "模板详情(含关联路线/表单)")
228
+    public R<Map<String, Object>> getTemplateDetail(@PathVariable Long id) {
229
+        Map<String, Object> detail = templateService.getDetail(id);
230
+        if (detail == null) return R.fail("模板不存在");
231
+        return R.ok(detail);
232
+    }
233
+
234
+    @PostMapping("/template/{id}/apply")
235
+    @Operation(summary = "应用模板生成巡检计划")
236
+    @SuppressWarnings("unchecked")
237
+    public R<Map<String, Object>> applyTemplate(@PathVariable Long id, @RequestBody Map<String, Object> req) {
238
+        Map<String, String> dateRange = (Map<String, String>) req.get("dateRange");
239
+        return R.ok(templateService.applyTemplate(id, dateRange));
240
+    }
241
+}

+ 0
- 16
wm-patrol/src/main/java/com/water/patrol/controller/PatrolTemplateController.java Visa fil

@@ -1,16 +0,0 @@
1
-package com.water.patrol.controller;
2
-import com.water.common.core.result.R; import com.water.patrol.entity.PatrolTemplate; import com.water.patrol.service.PatrolTemplateService;
3
-import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag;
4
-import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*;
5
-import java.util.Map;
6
-@Tag(name="巡检模板") @RestController @RequestMapping("/api/patrol/template") @RequiredArgsConstructor
7
-public class PatrolTemplateController {
8
-    private final PatrolTemplateService svc;
9
-    @Operation(summary="创建模板") @PostMapping public R<PatrolTemplate> create(@RequestBody Map<String,Object> req) { return R.ok(svc.create((String)req.get("templateName"),(String)req.get("templateCode"),((Number)req.get("routeId")).longValue(),(String)req.get("routeName"),((Number)req.get("formId")).longValue(),(String)req.get("formName"),(String)req.get("scheduleType"),(String)req.get("scheduleConfig"))); }
10
-    @Operation(summary="模板列表") @GetMapping public R<Map<String,Object>> list(@RequestParam(required=false) String keyword, @RequestParam(required=false) String status, @RequestParam(required=false) String scheduleType, @RequestParam(defaultValue="1") int page, @RequestParam(defaultValue="20") int size) { return R.ok(svc.list(keyword,status,scheduleType,page,size)); }
11
-    @Operation(summary="模板详情") @GetMapping("/{id}") public R<PatrolTemplate> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
12
-    @Operation(summary="更新模板") @PutMapping("/{id}") public R<PatrolTemplate> update(@PathVariable Long id, @RequestBody PatrolTemplate patch) { return R.ok(svc.update(id,patch)); }
13
-    @Operation(summary="更新状态") @PutMapping("/{id}/status") public R<String> updateStatus(@PathVariable Long id, @RequestParam String status) { svc.updateStatus(id,status); return R.ok("OK"); }
14
-    @Operation(summary="删除模板") @DeleteMapping("/{id}") public R<String> delete(@PathVariable Long id) { svc.delete(id); return R.ok("OK"); }
15
-    @Operation(summary="模板统计") @GetMapping("/stats") public R<Map<String,Object>> stats() { return R.ok(svc.getStats()); }
16
-}

+ 0
- 15
wm-patrol/src/main/java/com/water/patrol/entity/PatrolArea.java Visa fil

@@ -1,15 +0,0 @@
1
-package com.water.patrol.entity;
2
-import com.baomidou.mybatisplus.annotation.*;
3
-import lombok.Data;
4
-import java.math.BigDecimal;
5
-import java.time.LocalDateTime;
6
-@Data @TableName("pat_area")
7
-public class PatrolArea {
8
-    @TableId(type = IdType.AUTO) private Long id;
9
-    private String areaName; private String areaCode; private String description;
10
-    private BigDecimal centerLng; private BigDecimal centerLat;
11
-    private String boundary; private Double radius;
12
-    private String status;
13
-    @TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt;
14
-    @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updatedAt;
15
-}

+ 0
- 13
wm-patrol/src/main/java/com/water/patrol/entity/PatrolForm.java Visa fil

@@ -1,13 +0,0 @@
1
-package com.water.patrol.entity;
2
-import com.baomidou.mybatisplus.annotation.*;
3
-import lombok.Data;
4
-import java.time.LocalDateTime;
5
-@Data @TableName("pat_form")
6
-public class PatrolForm {
7
-    @TableId(type = IdType.AUTO) private Long id;
8
-    private String formName; private String formCode;
9
-    private String formType; private String fieldConfig;
10
-    private String status; private String remark;
11
-    @TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt;
12
-    @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updatedAt;
13
-}

+ 0
- 15
wm-patrol/src/main/java/com/water/patrol/entity/PatrolRouteSetup.java Visa fil

@@ -1,15 +0,0 @@
1
-package com.water.patrol.entity;
2
-import com.baomidou.mybatisplus.annotation.*;
3
-import lombok.Data;
4
-import java.time.LocalDateTime;
5
-@Data @TableName("pat_route_setup")
6
-public class PatrolRouteSetup {
7
-    @TableId(type = IdType.AUTO) private Long id;
8
-    private String routeName; private String routeCode;
9
-    private Long areaId; private String areaName;
10
-    private Integer checkpointCount; private String checkpointList;
11
-    private Double totalDistance; private Integer estimatedMinutes;
12
-    private String status; private String remark;
13
-    @TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt;
14
-    @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updatedAt;
15
-}

+ 0
- 15
wm-patrol/src/main/java/com/water/patrol/entity/PatrolTemplate.java Visa fil

@@ -1,15 +0,0 @@
1
-package com.water.patrol.entity;
2
-import com.baomidou.mybatisplus.annotation.*;
3
-import lombok.Data;
4
-import java.time.LocalDateTime;
5
-@Data @TableName("pat_template")
6
-public class PatrolTemplate {
7
-    @TableId(type = IdType.AUTO) private Long id;
8
-    private String templateName; private String templateCode;
9
-    private Long routeId; private String routeName;
10
-    private Long formId; private String formName;
11
-    private String scheduleType; private String scheduleConfig;
12
-    private String status; private String remark;
13
-    @TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt;
14
-    @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updatedAt;
15
-}

+ 0
- 5
wm-patrol/src/main/java/com/water/patrol/mapper/PatrolAreaMapper.java Visa fil

@@ -1,5 +0,0 @@
1
-package com.water.patrol.mapper;
2
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
3
-import com.water.patrol.entity.PatrolArea;
4
-import org.apache.ibatis.annotations.Mapper;
5
-@Mapper public interface PatrolAreaMapper extends BaseMapper<PatrolArea> {}

+ 0
- 5
wm-patrol/src/main/java/com/water/patrol/mapper/PatrolFormMapper.java Visa fil

@@ -1,5 +0,0 @@
1
-package com.water.patrol.mapper;
2
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
3
-import com.water.patrol.entity.PatrolForm;
4
-import org.apache.ibatis.annotations.Mapper;
5
-@Mapper public interface PatrolFormMapper extends BaseMapper<PatrolForm> {}

+ 0
- 5
wm-patrol/src/main/java/com/water/patrol/mapper/PatrolRouteSetupMapper.java Visa fil

@@ -1,5 +0,0 @@
1
-package com.water.patrol.mapper;
2
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
3
-import com.water.patrol.entity.PatrolRouteSetup;
4
-import org.apache.ibatis.annotations.Mapper;
5
-@Mapper public interface PatrolRouteSetupMapper extends BaseMapper<PatrolRouteSetup> {}

+ 0
- 5
wm-patrol/src/main/java/com/water/patrol/mapper/PatrolTemplateMapper.java Visa fil

@@ -1,5 +0,0 @@
1
-package com.water.patrol.mapper;
2
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
3
-import com.water.patrol.entity.PatrolTemplate;
4
-import org.apache.ibatis.annotations.Mapper;
5
-@Mapper public interface PatrolTemplateMapper extends BaseMapper<PatrolTemplate> {}

+ 107
- 44
wm-patrol/src/main/java/com/water/patrol/service/PatrolAreaService.java Visa fil

@@ -1,53 +1,116 @@
1 1
 package com.water.patrol.service;
2
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
3
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
4
-import com.water.patrol.entity.PatrolArea; import com.water.patrol.mapper.PatrolAreaMapper;
5
-import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
2
+
3
+import lombok.RequiredArgsConstructor;
4
+import lombok.extern.slf4j.Slf4j;
5
+import org.springframework.jdbc.core.JdbcTemplate;
6 6
 import org.springframework.stereotype.Service;
7
-import java.math.BigDecimal; import java.util.*;
8
-@Slf4j @Service @RequiredArgsConstructor
7
+
8
+import java.util.*;
9
+
10
+/**
11
+ * 巡检区域设置服务 - PAT-16
12
+ */
13
+@Slf4j
14
+@Service
15
+@RequiredArgsConstructor
9 16
 public class PatrolAreaService {
10
-    private final PatrolAreaMapper mapper;
11
-    public PatrolArea create(String areaName, String areaCode, String desc, BigDecimal cLng, BigDecimal cLat, String boundary, Double radius) {
12
-        PatrolArea a = new PatrolArea(); a.setAreaName(areaName); a.setAreaCode(areaCode);
13
-        a.setDescription(desc); a.setCenterLng(cLng); a.setCenterLat(cLat);
14
-        a.setBoundary(boundary); a.setRadius(radius); a.setStatus("active");
15
-        mapper.insert(a); return a;
17
+
18
+    private final JdbcTemplate jdbc;
19
+
20
+    public Map<String, Object> create(String name, String code, Long parentId,
21
+                                       String description, Double lng, Double lat, Double radius) {
22
+        jdbc.update(
23
+            "INSERT INTO patrol_area (name, code, parent_id, description, lng, lat, radius, status) " +
24
+            "VALUES (?,?,?,?,?,?,?,'active')",
25
+            name, code, parentId != null ? parentId : 0, description, lng, lat, radius != null ? radius : 0);
26
+        Map<String, Object> result = new LinkedHashMap<>();
27
+        result.put("name", name);
28
+        result.put("code", code);
29
+        result.put("created", true);
30
+        return result;
16 31
     }
17
-    public PatrolArea update(Long id, PatrolArea patch) {
18
-        PatrolArea a = mapper.selectById(id);
19
-        if (a == null) throw new RuntimeException("区域不存在: "+id);
20
-        if (patch.getAreaName() != null) a.setAreaName(patch.getAreaName());
21
-        if (patch.getDescription() != null) a.setDescription(patch.getDescription());
22
-        if (patch.getCenterLng() != null) a.setCenterLng(patch.getCenterLng());
23
-        if (patch.getCenterLat() != null) a.setCenterLat(patch.getCenterLat());
24
-        if (patch.getBoundary() != null) a.setBoundary(patch.getBoundary());
25
-        if (patch.getRadius() != null) a.setRadius(patch.getRadius());
26
-        mapper.updateById(a); return a;
32
+
33
+    public Map<String, Object> update(Long id, String name, String code, Long parentId,
34
+                                       String description, Double lng, Double lat, Double radius) {
35
+        Map<String, Object> existing = getDetail(id);
36
+        if (existing == null) throw new RuntimeException("区域不存在: " + id);
37
+
38
+        StringBuilder sql = new StringBuilder("UPDATE patrol_area SET updated_at = NOW()");
39
+        List<Object> params = new ArrayList<>();
40
+
41
+        if (name != null) { sql.append(", name = ?"); params.add(name); }
42
+        if (code != null) { sql.append(", code = ?"); params.add(code); }
43
+        if (parentId != null) { sql.append(", parent_id = ?"); params.add(parentId); }
44
+        if (description != null) { sql.append(", description = ?"); params.add(description); }
45
+        if (lng != null) { sql.append(", lng = ?"); params.add(lng); }
46
+        if (lat != null) { sql.append(", lat = ?"); params.add(lat); }
47
+        if (radius != null) { sql.append(", radius = ?"); params.add(radius); }
48
+
49
+        sql.append(" WHERE id = ?");
50
+        params.add(id);
51
+        jdbc.update(sql.toString(), params.toArray());
52
+
53
+        return getDetail(id);
27 54
     }
28
-    public void delete(Long id) { mapper.deleteById(id); }
29
-    public PatrolArea getDetail(Long id) { return mapper.selectById(id); }
30
-    public Map<String, Object> list(String keyword, String status, int page, int size) {
31
-        LambdaQueryWrapper<PatrolArea> w = new LambdaQueryWrapper<>();
32
-        if (keyword != null && !keyword.isEmpty()) w.and(q -> q.like(PatrolArea::getAreaName, keyword).or().like(PatrolArea::getAreaCode, keyword));
33
-        if (status != null && !status.isEmpty()) w.eq(PatrolArea::getStatus, status);
34
-        w.orderByDesc(PatrolArea::getCreatedAt);
35
-        Page<PatrolArea> p = mapper.selectPage(new Page<>(page, size), w);
36
-        Map<String, Object> r = new LinkedHashMap<>();
37
-        r.put("records", p.getRecords()); r.put("total", p.getTotal());
38
-        r.put("page", page); r.put("size", size);
39
-        return r;
55
+
56
+    public void delete(Long id) {
57
+        long childCount = countQuery("SELECT COUNT(*) FROM patrol_area WHERE parent_id = ?", id);
58
+        if (childCount > 0) throw new RuntimeException("存在子区域,无法删除");
59
+        jdbc.update("DELETE FROM patrol_area WHERE id = ?", id);
40 60
     }
41
-    public void updateStatus(Long id, String status) {
42
-        PatrolArea a = mapper.selectById(id);
43
-        if (a == null) throw new RuntimeException("区域不存在: "+id);
44
-        a.setStatus(status); mapper.updateById(a);
61
+
62
+    public Map<String, Object> getDetail(Long id) {
63
+        List<Map<String, Object>> rows = jdbc.queryForList("SELECT * FROM patrol_area WHERE id = ?", id);
64
+        if (rows.isEmpty()) return null;
65
+
66
+        Map<String, Object> area = new LinkedHashMap<>(rows.get(0));
67
+
68
+        long deviceCount = countQuery(
69
+            "SELECT COUNT(*) FROM patrol_device WHERE area = (SELECT code FROM patrol_area WHERE id = ?)", id);
70
+        area.put("deviceCount", deviceCount);
71
+
72
+        long routeCount = countQuery("SELECT COUNT(*) FROM patrol_route WHERE area_id = ?", id);
73
+        area.put("routeCount", routeCount);
74
+
75
+        return area;
45 76
     }
46
-    public Map<String, Object> getStats() {
47
-        Map<String, Object> r = new LinkedHashMap<>();
48
-        r.put("total", mapper.selectCount(null));
49
-        r.put("active", mapper.selectCount(new LambdaQueryWrapper<PatrolArea>().eq(PatrolArea::getStatus,"active")));
50
-        r.put("inactive", mapper.selectCount(new LambdaQueryWrapper<PatrolArea>().eq(PatrolArea::getStatus,"inactive")));
51
-        return r;
77
+
78
+    public Map<String, Object> list(Long parentId) {
79
+        List<Map<String, Object>> areas;
80
+        if (parentId != null) {
81
+            areas = jdbc.queryForList("SELECT * FROM patrol_area WHERE parent_id = ? ORDER BY name", parentId);
82
+        } else {
83
+            areas = jdbc.queryForList("SELECT * FROM patrol_area ORDER BY parent_id, name");
84
+        }
85
+
86
+        List<Map<String, Object>> tree = buildTree(areas, 0L);
87
+
88
+        Map<String, Object> result = new LinkedHashMap<>();
89
+        result.put("total", areas.size());
90
+        result.put("records", tree);
91
+        return result;
92
+    }
93
+
94
+    private List<Map<String, Object>> buildTree(List<Map<String, Object>> all, Long parentId) {
95
+        List<Map<String, Object>> tree = new ArrayList<>();
96
+        for (Map<String, Object> area : all) {
97
+            Object pid = area.get("parent_id");
98
+            long areaParentId = pid instanceof Number ? ((Number) pid).longValue() : 0L;
99
+            if (areaParentId == parentId) {
100
+                Map<String, Object> node = new LinkedHashMap<>(area);
101
+                Long areaId = ((Number) area.get("id")).longValue();
102
+                List<Map<String, Object>> children = buildTree(all, areaId);
103
+                if (!children.isEmpty()) {
104
+                    node.put("children", children);
105
+                }
106
+                tree.add(node);
107
+            }
108
+        }
109
+        return tree;
110
+    }
111
+
112
+    private long countQuery(String sql, Object... args) {
113
+        Long count = jdbc.queryForObject(sql, Long.class, args);
114
+        return count != null ? count : 0;
52 115
     }
53 116
 }

+ 0
- 252
wm-patrol/src/main/java/com/water/patrol/service/PatrolCoreService.java Visa fil

@@ -1,252 +0,0 @@
1
-package com.water.patrol.service;
2
-
3
-import lombok.RequiredArgsConstructor;
4
-import lombok.extern.slf4j.Slf4j;
5
-import org.springframework.jdbc.core.JdbcTemplate;
6
-import org.springframework.stereotype.Service;
7
-
8
-import java.time.LocalDate;
9
-import java.time.LocalDateTime;
10
-import java.util.*;
11
-
12
-/**
13
- * 巡检管理核心服务 - Issue #86
14
- * PAT-01 总览, PAT-02 轨迹, PAT-03 任务台账, PAT-04 设备台账
15
- */
16
-@Slf4j
17
-@Service
18
-@RequiredArgsConstructor
19
-public class PatrolCoreService {
20
-
21
-    private final JdbcTemplate jdbc;
22
-
23
-    // ========== PAT-01 巡检总览 ==========
24
-
25
-    public Map<String, Object> getOverview() {
26
-        LocalDate today = LocalDate.now();
27
-        LocalDate monthStart = today.withDayOfMonth(1);
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);
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'");
36
-
37
-        long coverageRate = monthTotal > 0 ? Math.round(monthCompleted * 100.0 / monthTotal) : 0;
38
-
39
-        Map<String, Object> overview = new LinkedHashMap<>();
40
-        overview.put("todayTotal", todayTotal);
41
-        overview.put("todayCompleted", todayCompleted);
42
-        overview.put("monthTotal", monthTotal);
43
-        overview.put("monthCompleted", monthCompleted);
44
-        overview.put("coverageRate", coverageRate);
45
-        overview.put("pendingIssues", pendingIssues);
46
-        overview.put("resolvedIssues", resolvedIssues);
47
-        return overview;
48
-    }
49
-
50
-    public List<Map<String, Object>> getTodayTasks() {
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 " +
54
-            "WHERE pt.task_date = CURRENT_DATE ORDER BY pt.plan_start");
55
-    }
56
-
57
-    public List<Map<String, Object>> getRecentIssues(int limit) {
58
-        return jdbc.queryForList(
59
-            "SELECT * FROM patrol_work_order ORDER BY created_at DESC LIMIT ?", limit);
60
-    }
61
-
62
-    // ========== PAT-02 任务轨迹(GPS记录与回放) ==========
63
-
64
-    public Map<String, Object> recordTrackPoint(Long taskId, Long workerId,
65
-                                                 double lng, double lat,
66
-                                                 double speed, double accuracy) {
67
-        jdbc.update(
68
-            "INSERT INTO patrol_track_point (task_id, worker_id, lng, lat, speed, accuracy) " +
69
-            "VALUES (?,?,?,?,?,?)",
70
-            taskId, workerId, lng, lat, speed, accuracy);
71
-        Map<String, Object> result = new LinkedHashMap<>();
72
-        result.put("taskId", taskId);
73
-        result.put("workerId", workerId);
74
-        result.put("lng", lng);
75
-        result.put("lat", lat);
76
-        result.put("recorded", true);
77
-        return result;
78
-    }
79
-
80
-    public List<Map<String, Object>> getTrack(Long taskId) {
81
-        return jdbc.queryForList(
82
-            "SELECT * FROM patrol_track_point WHERE task_id = ? ORDER BY recorded_at", taskId);
83
-    }
84
-
85
-    public Map<String, Object> replayTrack(Long taskId) {
86
-        List<Map<String, Object>> points = getTrack(taskId);
87
-        Map<String, Object> result = new LinkedHashMap<>();
88
-        result.put("taskId", taskId);
89
-        result.put("points", points);
90
-        result.put("totalPoints", points.size());
91
-        if (!points.isEmpty()) {
92
-            result.put("startTime", points.get(0).get("recorded_at"));
93
-            result.put("endTime", points.get(points.size() - 1).get("recorded_at"));
94
-        }
95
-        return result;
96
-    }
97
-
98
-    public Map<String, Object> getTrackStats(Long taskId) {
99
-        List<Map<String, Object>> points = getTrack(taskId);
100
-        Map<String, Object> stats = new LinkedHashMap<>();
101
-        stats.put("taskId", taskId);
102
-        stats.put("totalPoints", points.size());
103
-
104
-        if (points.size() < 2) {
105
-            stats.put("totalDistance", 0.0);
106
-            stats.put("duration", 0);
107
-            stats.put("avgSpeed", 0.0);
108
-            return stats;
109
-        }
110
-
111
-        double totalDistance = 0;
112
-        for (int i = 1; i < points.size(); i++) {
113
-            double lat1 = ((Number) points.get(i - 1).get("lat")).doubleValue();
114
-            double lon1 = ((Number) points.get(i - 1).get("lng")).doubleValue();
115
-            double lat2 = ((Number) points.get(i).get("lat")).doubleValue();
116
-            double lon2 = ((Number) points.get(i).get("lng")).doubleValue();
117
-            totalDistance += haversine(lat1, lon1, lat2, lon2);
118
-        }
119
-
120
-        double avgSpeed = points.stream()
121
-            .filter(p -> p.get("speed") != null)
122
-            .mapToDouble(p -> ((Number) p.get("speed")).doubleValue())
123
-            .average().orElse(0);
124
-
125
-        stats.put("totalDistance", Math.round(totalDistance * 100.0) / 100.0);
126
-        stats.put("avgSpeed", Math.round(avgSpeed * 100.0) / 100.0);
127
-        return stats;
128
-    }
129
-
130
-    // ========== PAT-03 任务台账 ==========
131
-
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");
134
-        List<Object> params = new ArrayList<>();
135
-        if (status != null && !status.isEmpty()) {
136
-            sql.append(" AND status = ?");
137
-            params.add(status);
138
-        }
139
-        if (workerId != null) {
140
-            sql.append(" AND assignee_id = ?");
141
-            params.add(workerId);
142
-        }
143
-        sql.append(" ORDER BY task_date DESC LIMIT ? OFFSET ?");
144
-        params.add(size);
145
-        params.add((page - 1) * size);
146
-
147
-        List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
148
-
149
-        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM patrol_task WHERE 1=1");
150
-        List<Object> countParams = new ArrayList<>();
151
-        if (status != null && !status.isEmpty()) {
152
-            countSql.append(" AND status = ?");
153
-            countParams.add(status);
154
-        }
155
-        if (workerId != null) {
156
-            countSql.append(" AND assignee_id = ?");
157
-            countParams.add(workerId);
158
-        }
159
-        long total = countQuery(countSql.toString(), countParams.toArray());
160
-
161
-        Map<String, Object> result = new LinkedHashMap<>();
162
-        result.put("total", total);
163
-        result.put("page", page);
164
-        result.put("size", size);
165
-        result.put("records", records);
166
-        return result;
167
-    }
168
-
169
-    // ========== PAT-04 设备台账 ==========
170
-
171
-    public Map<String, Object> getDeviceLedger(String deviceType, String status, String area,
172
-                                                int page, int size) {
173
-        StringBuilder sql = new StringBuilder("SELECT * FROM patrol_device WHERE 1=1");
174
-        List<Object> params = new ArrayList<>();
175
-        if (deviceType != null && !deviceType.isEmpty()) {
176
-            sql.append(" AND device_type = ?");
177
-            params.add(deviceType);
178
-        }
179
-        if (status != null && !status.isEmpty()) {
180
-            sql.append(" AND status = ?");
181
-            params.add(status);
182
-        }
183
-        if (area != null && !area.isEmpty()) {
184
-            sql.append(" AND area = ?");
185
-            params.add(area);
186
-        }
187
-        sql.append(" ORDER BY created_at DESC LIMIT ? OFFSET ?");
188
-        params.add(size);
189
-        params.add((page - 1) * size);
190
-
191
-        List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
192
-
193
-        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM patrol_device WHERE 1=1");
194
-        List<Object> countParams = new ArrayList<>();
195
-        if (deviceType != null && !deviceType.isEmpty()) {
196
-            countSql.append(" AND device_type = ?");
197
-            countParams.add(deviceType);
198
-        }
199
-        if (status != null && !status.isEmpty()) {
200
-            countSql.append(" AND status = ?");
201
-            countParams.add(status);
202
-        }
203
-        if (area != null && !area.isEmpty()) {
204
-            countSql.append(" AND area = ?");
205
-            countParams.add(area);
206
-        }
207
-        long total = countQuery(countSql.toString(), countParams.toArray());
208
-
209
-        Map<String, Object> result = new LinkedHashMap<>();
210
-        result.put("total", total);
211
-        result.put("page", page);
212
-        result.put("size", size);
213
-        result.put("records", records);
214
-        return result;
215
-    }
216
-
217
-    public Map<String, Object> createDevice(String deviceNo, String deviceName, String deviceType,
218
-                                             String location, double lng, double lat, String area) {
219
-        jdbc.update(
220
-            "INSERT INTO patrol_device (device_no, device_name, device_type, location, lng, lat, area) " +
221
-            "VALUES (?,?,?,?,?,?,?)",
222
-            deviceNo, deviceName, deviceType, location, lng, lat, area);
223
-        Map<String, Object> result = new LinkedHashMap<>();
224
-        result.put("deviceNo", deviceNo);
225
-        result.put("deviceName", deviceName);
226
-        result.put("created", true);
227
-        return result;
228
-    }
229
-
230
-    public Map<String, Object> updateDeviceStatus(Long deviceId, String status) {
231
-        jdbc.update("UPDATE patrol_device SET status = ?, updated_at = NOW() WHERE id = ?",
232
-            status, deviceId);
233
-        return Map.of("deviceId", deviceId, "status", status, "updated", true);
234
-    }
235
-
236
-    // ========== 工具方法 ==========
237
-
238
-    private long countQuery(String sql, Object... args) {
239
-        Long count = jdbc.queryForObject(sql, Long.class, args);
240
-        return count != null ? count : 0;
241
-    }
242
-
243
-    private double haversine(double lat1, double lon1, double lat2, double lon2) {
244
-        double R = 6371;
245
-        double dLat = Math.toRadians(lat2 - lat1);
246
-        double dLon = Math.toRadians(lon2 - lon1);
247
-        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
248
-            + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
249
-            * Math.sin(dLon / 2) * Math.sin(dLon / 2);
250
-        return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
251
-    }
252
-}

+ 94
- 36
wm-patrol/src/main/java/com/water/patrol/service/PatrolFormService.java Visa fil

@@ -1,45 +1,103 @@
1 1
 package com.water.patrol.service;
2
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
3
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
4
-import com.water.patrol.entity.PatrolForm; import com.water.patrol.mapper.PatrolFormMapper;
5
-import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
2
+
3
+import lombok.RequiredArgsConstructor;
4
+import lombok.extern.slf4j.Slf4j;
5
+import org.springframework.jdbc.core.JdbcTemplate;
6 6
 import org.springframework.stereotype.Service;
7
+
7 8
 import java.util.*;
8
-@Slf4j @Service @RequiredArgsConstructor
9
+
10
+/**
11
+ * 巡检自定义表单服务 - PAT-18 + PAT-19
12
+ */
13
+@Slf4j
14
+@Service
15
+@RequiredArgsConstructor
9 16
 public class PatrolFormService {
10
-    private final PatrolFormMapper mapper;
11
-    public PatrolForm create(String formName, String formCode, String formType, String fieldConfig) {
12
-        PatrolForm f = new PatrolForm(); f.setFormName(formName); f.setFormCode(formCode);
13
-        f.setFormType(formType); f.setFieldConfig(fieldConfig); f.setStatus("active");
14
-        mapper.insert(f); return f;
17
+
18
+    private final JdbcTemplate jdbc;
19
+
20
+    // ========== PAT-18: 自定义表单 ==========
21
+
22
+    public Map<String, Object> createForm(String name, String type, List<Map<String, Object>> fields) {
23
+        String fieldsJson = fields != null ? fields.toString() : "[]";
24
+        jdbc.update(
25
+            "INSERT INTO patrol_form (name, type, fields, status) VALUES (?,?,?::jsonb,'active')",
26
+            name, type, fieldsJson);
27
+
28
+        Map<String, Object> result = new LinkedHashMap<>();
29
+        result.put("name", name);
30
+        result.put("type", type);
31
+        result.put("fieldCount", fields != null ? fields.size() : 0);
32
+        result.put("created", true);
33
+        return result;
15 34
     }
16
-    public PatrolForm update(Long id, PatrolForm patch) {
17
-        PatrolForm f = mapper.selectById(id);
18
-        if (f == null) throw new RuntimeException("表单不存在: "+id);
19
-        if (patch.getFormName() != null) f.setFormName(patch.getFormName());
20
-        if (patch.getFieldConfig() != null) f.setFieldConfig(patch.getFieldConfig());
21
-        if (patch.getFormType() != null) f.setFormType(patch.getFormType());
22
-        if (patch.getRemark() != null) f.setRemark(patch.getRemark());
23
-        mapper.updateById(f); return f;
35
+
36
+    public Map<String, Object> listForms(String type) {
37
+        List<Map<String, Object>> forms;
38
+        if (type != null && !type.isEmpty()) {
39
+            forms = jdbc.queryForList("SELECT * FROM patrol_form WHERE type = ? ORDER BY created_at DESC", type);
40
+        } else {
41
+            forms = jdbc.queryForList("SELECT * FROM patrol_form ORDER BY created_at DESC");
42
+        }
43
+
44
+        Map<String, Object> result = new LinkedHashMap<>();
45
+        result.put("total", forms.size());
46
+        result.put("records", forms);
47
+        return result;
24 48
     }
25
-    public void delete(Long id) { mapper.deleteById(id); }
26
-    public PatrolForm getDetail(Long id) { return mapper.selectById(id); }
27
-    public Map<String, Object> list(String keyword, String formType, String status, int page, int size) {
28
-        LambdaQueryWrapper<PatrolForm> w = new LambdaQueryWrapper<>();
29
-        if (keyword != null && !keyword.isEmpty()) w.and(q -> q.like(PatrolForm::getFormName, keyword).or().like(PatrolForm::getFormCode, keyword));
30
-        if (formType != null && !formType.isEmpty()) w.eq(PatrolForm::getFormType, formType);
31
-        if (status != null && !status.isEmpty()) w.eq(PatrolForm::getStatus, status);
32
-        w.orderByDesc(PatrolForm::getCreatedAt);
33
-        Page<PatrolForm> p = mapper.selectPage(new Page<>(page, size), w);
34
-        Map<String, Object> r = new LinkedHashMap<>();
35
-        r.put("records", p.getRecords()); r.put("total", p.getTotal());
36
-        r.put("page", page); r.put("size", size);
37
-        return r;
49
+
50
+    public Map<String, Object> getForm(Long id) {
51
+        List<Map<String, Object>> rows = jdbc.queryForList("SELECT * FROM patrol_form WHERE id = ?", id);
52
+        if (rows.isEmpty()) return null;
53
+        return new LinkedHashMap<>(rows.get(0));
38 54
     }
39
-    public Map<String, Object> getStats() {
40
-        Map<String, Object> r = new LinkedHashMap<>();
41
-        r.put("total", mapper.selectCount(null));
42
-        r.put("active", mapper.selectCount(new LambdaQueryWrapper<PatrolForm>().eq(PatrolForm::getStatus,"active")));
43
-        return r;
55
+
56
+    // ========== PAT-19: 表单挂接 ==========
57
+
58
+    public Map<String, Object> bindForm(Long formId, String targetType, Long targetId) {
59
+        Map<String, Object> form = getForm(formId);
60
+        if (form == null) throw new RuntimeException("表单不存在: " + formId);
61
+
62
+        jdbc.update(
63
+            "INSERT INTO patrol_form_binding (form_id, target_type, target_id) VALUES (?,?,?) " +
64
+            "ON CONFLICT (form_id, target_type, target_id) DO NOTHING",
65
+            formId, targetType, targetId);
66
+
67
+        Map<String, Object> result = new LinkedHashMap<>();
68
+        result.put("formId", formId);
69
+        result.put("targetType", targetType);
70
+        result.put("targetId", targetId);
71
+        result.put("bound", true);
72
+        return result;
73
+    }
74
+
75
+    public Map<String, Object> unbindForm(Long formId, String targetType, Long targetId) {
76
+        int rows = jdbc.update(
77
+            "DELETE FROM patrol_form_binding WHERE form_id = ? AND target_type = ? AND target_id = ?",
78
+            formId, targetType, targetId);
79
+
80
+        Map<String, Object> result = new LinkedHashMap<>();
81
+        result.put("formId", formId);
82
+        result.put("targetType", targetType);
83
+        result.put("targetId", targetId);
84
+        result.put("unbound", rows > 0);
85
+        return result;
86
+    }
87
+
88
+    public Map<String, Object> getBoundForms(String targetType, Long targetId) {
89
+        List<Map<String, Object>> forms = jdbc.queryForList(
90
+            "SELECT pf.*, pfb.created_at as bound_at FROM patrol_form pf " +
91
+            "INNER JOIN patrol_form_binding pfb ON pf.id = pfb.form_id " +
92
+            "WHERE pfb.target_type = ? AND pfb.target_id = ? " +
93
+            "ORDER BY pfb.created_at DESC",
94
+            targetType, targetId);
95
+
96
+        Map<String, Object> result = new LinkedHashMap<>();
97
+        result.put("targetType", targetType);
98
+        result.put("targetId", targetId);
99
+        result.put("total", forms.size());
100
+        result.put("forms", forms);
101
+        return result;
44 102
     }
45 103
 }

+ 154
- 43
wm-patrol/src/main/java/com/water/patrol/service/PatrolRouteSetupService.java Visa fil

@@ -1,55 +1,166 @@
1 1
 package com.water.patrol.service;
2
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
3
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
4
-import com.water.patrol.entity.PatrolRouteSetup; import com.water.patrol.mapper.PatrolRouteSetupMapper;
5
-import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
2
+
3
+import lombok.RequiredArgsConstructor;
4
+import lombok.extern.slf4j.Slf4j;
5
+import org.springframework.jdbc.core.JdbcTemplate;
6 6
 import org.springframework.stereotype.Service;
7
+
7 8
 import java.util.*;
8
-@Slf4j @Service @RequiredArgsConstructor
9
+
10
+/**
11
+ * 巡检路线设置服务 - PAT-17
12
+ */
13
+@Slf4j
14
+@Service
15
+@RequiredArgsConstructor
9 16
 public class PatrolRouteSetupService {
10
-    private final PatrolRouteSetupMapper mapper;
11
-    public PatrolRouteSetup create(String routeName, String routeCode, Long areaId, String areaName,
12
-            Integer checkpointCount, String checkpointList, Double totalDistance, Integer estimatedMinutes) {
13
-        PatrolRouteSetup r = new PatrolRouteSetup(); r.setRouteName(routeName); r.setRouteCode(routeCode);
14
-        r.setAreaId(areaId); r.setAreaName(areaName); r.setCheckpointCount(checkpointCount);
15
-        r.setCheckpointList(checkpointList); r.setTotalDistance(totalDistance);
16
-        r.setEstimatedMinutes(estimatedMinutes); r.setStatus("active");
17
-        mapper.insert(r); return r;
17
+
18
+    private final JdbcTemplate jdbc;
19
+
20
+    public Map<String, Object> create(String name, Long areaId, List<Map<String, Object>> checkpoints,
21
+                                       Integer estimDuration, String description) {
22
+        jdbc.update(
23
+            "INSERT INTO patrol_route (route_name, area_id, estim_duration, description, status) " +
24
+            "VALUES (?,?,?,?,'active')",
25
+            name, areaId, estimDuration, description);
26
+
27
+        Long routeId = jdbc.queryForObject("SELECT currval('patrol_route_id_seq')", Long.class);
28
+
29
+        if (checkpoints != null && !checkpoints.isEmpty()) {
30
+            for (int i = 0; i < checkpoints.size(); i++) {
31
+                Map<String, Object> cp = checkpoints.get(i);
32
+                jdbc.update(
33
+                    "INSERT INTO patrol_route_checkpoint (route_id, checkpoint_seq, device_id, device_name, lng, lat, check_items) " +
34
+                    "VALUES (?,?,?,?,?,?,?::jsonb)",
35
+                    routeId, i + 1,
36
+                    cp.get("deviceId"),
37
+                    (String) cp.get("deviceName"),
38
+                    cp.get("lng") != null ? ((Number) cp.get("lng")).doubleValue() : null,
39
+                    cp.get("lat") != null ? ((Number) cp.get("lat")).doubleValue() : null,
40
+                    cp.get("checkItems") != null ? cp.get("checkItems").toString() : "[]");
41
+            }
42
+        }
43
+
44
+        Map<String, Object> result = new LinkedHashMap<>();
45
+        result.put("id", routeId);
46
+        result.put("name", name);
47
+        result.put("areaId", areaId);
48
+        result.put("checkpointCount", checkpoints != null ? checkpoints.size() : 0);
49
+        result.put("created", true);
50
+        return result;
51
+    }
52
+
53
+    public Map<String, Object> update(Long id, String name, Long areaId,
54
+                                       List<Map<String, Object>> checkpoints,
55
+                                       Integer estimDuration, String description) {
56
+        Map<String, Object> existing = getDetail(id);
57
+        if (existing == null) throw new RuntimeException("路线不存在: " + id);
58
+
59
+        StringBuilder sql = new StringBuilder("UPDATE patrol_route SET updated_at = NOW()");
60
+        List<Object> params = new ArrayList<>();
61
+
62
+        if (name != null) { sql.append(", route_name = ?"); params.add(name); }
63
+        if (areaId != null) { sql.append(", area_id = ?"); params.add(areaId); }
64
+        if (estimDuration != null) { sql.append(", estim_duration = ?"); params.add(estimDuration); }
65
+        if (description != null) { sql.append(", description = ?"); params.add(description); }
66
+
67
+        sql.append(" WHERE id = ?");
68
+        params.add(id);
69
+        jdbc.update(sql.toString(), params.toArray());
70
+
71
+        if (checkpoints != null) {
72
+            jdbc.update("DELETE FROM patrol_route_checkpoint WHERE route_id = ?", id);
73
+            for (int i = 0; i < checkpoints.size(); i++) {
74
+                Map<String, Object> cp = checkpoints.get(i);
75
+                jdbc.update(
76
+                    "INSERT INTO patrol_route_checkpoint (route_id, checkpoint_seq, device_id, device_name, lng, lat, check_items) " +
77
+                    "VALUES (?,?,?,?,?,?,?::jsonb)",
78
+                    id, i + 1,
79
+                    cp.get("deviceId"),
80
+                    (String) cp.get("deviceName"),
81
+                    cp.get("lng") != null ? ((Number) cp.get("lng")).doubleValue() : null,
82
+                    cp.get("lat") != null ? ((Number) cp.get("lat")).doubleValue() : null,
83
+                    cp.get("checkItems") != null ? cp.get("checkItems").toString() : "[]");
84
+            }
85
+        }
86
+
87
+        return getDetail(id);
88
+    }
89
+
90
+    public void delete(Long id) {
91
+        jdbc.update("DELETE FROM patrol_route_checkpoint WHERE route_id = ?", id);
92
+        jdbc.update("DELETE FROM patrol_route WHERE id = ?", id);
18 93
     }
19
-    public PatrolRouteSetup update(Long id, PatrolRouteSetup patch) {
20
-        PatrolRouteSetup r = mapper.selectById(id);
21
-        if (r == null) throw new RuntimeException("路线不存在: "+id);
22
-        if (patch.getRouteName() != null) r.setRouteName(patch.getRouteName());
23
-        if (patch.getCheckpointCount() != null) r.setCheckpointCount(patch.getCheckpointCount());
24
-        if (patch.getCheckpointList() != null) r.setCheckpointList(patch.getCheckpointList());
25
-        if (patch.getTotalDistance() != null) r.setTotalDistance(patch.getTotalDistance());
26
-        if (patch.getEstimatedMinutes() != null) r.setEstimatedMinutes(patch.getEstimatedMinutes());
27
-        if (patch.getRemark() != null) r.setRemark(patch.getRemark());
28
-        mapper.updateById(r); return r;
94
+
95
+    public Map<String, Object> getDetail(Long id) {
96
+        List<Map<String, Object>> rows = jdbc.queryForList("SELECT * FROM patrol_route WHERE id = ?", id);
97
+        if (rows.isEmpty()) return null;
98
+
99
+        Map<String, Object> route = new LinkedHashMap<>(rows.get(0));
100
+
101
+        List<Map<String, Object>> checkpoints = jdbc.queryForList(
102
+            "SELECT * FROM patrol_route_checkpoint WHERE route_id = ? ORDER BY checkpoint_seq", id);
103
+        route.put("checkpoints", checkpoints);
104
+
105
+        Object areaId = route.get("area_id");
106
+        if (areaId != null && areaId instanceof Number) {
107
+            List<Map<String, Object>> areaRows = jdbc.queryForList(
108
+                "SELECT name FROM patrol_area WHERE id = ?", ((Number) areaId).longValue());
109
+            if (!areaRows.isEmpty()) {
110
+                route.put("areaName", areaRows.get(0).get("name"));
111
+            }
112
+        }
113
+
114
+        return route;
29 115
     }
30
-    public void delete(Long id) { mapper.deleteById(id); }
31
-    public PatrolRouteSetup getDetail(Long id) { return mapper.selectById(id); }
32
-    public Map<String, Object> list(String keyword, String status, Long areaId, int page, int size) {
33
-        LambdaQueryWrapper<PatrolRouteSetup> w = new LambdaQueryWrapper<>();
34
-        if (keyword != null && !keyword.isEmpty()) w.and(q -> q.like(PatrolRouteSetup::getRouteName, keyword).or().like(PatrolRouteSetup::getRouteCode, keyword));
35
-        if (status != null && !status.isEmpty()) w.eq(PatrolRouteSetup::getStatus, status);
36
-        if (areaId != null) w.eq(PatrolRouteSetup::getAreaId, areaId);
37
-        w.orderByDesc(PatrolRouteSetup::getCreatedAt);
38
-        Page<PatrolRouteSetup> p = mapper.selectPage(new Page<>(page, size), w);
116
+
117
+    public Map<String, Object> list(Long areaId, int page, int size) {
118
+        StringBuilder sql = new StringBuilder("SELECT * FROM patrol_route WHERE 1=1");
119
+        List<Object> params = new ArrayList<>();
120
+
121
+        if (areaId != null) {
122
+            sql.append(" AND area_id = ?");
123
+            params.add(areaId);
124
+        }
125
+        sql.append(" ORDER BY created_at DESC LIMIT ? OFFSET ?");
126
+        params.add(size);
127
+        params.add((page - 1) * size);
128
+
129
+        List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
130
+
131
+        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM patrol_route WHERE 1=1");
132
+        List<Object> countParams = new ArrayList<>();
133
+        if (areaId != null) {
134
+            countSql.append(" AND area_id = ?");
135
+            countParams.add(areaId);
136
+        }
137
+        long total = countQuery(countSql.toString(), countParams.toArray());
138
+
39 139
         Map<String, Object> result = new LinkedHashMap<>();
40
-        result.put("records", p.getRecords()); result.put("total", p.getTotal());
41
-        result.put("page", page); result.put("size", size);
140
+        result.put("total", total);
141
+        result.put("page", page);
142
+        result.put("size", size);
143
+        result.put("records", records);
42 144
         return result;
43 145
     }
44
-    public void updateStatus(Long id, String status) {
45
-        PatrolRouteSetup r = mapper.selectById(id);
46
-        if (r == null) throw new RuntimeException("路线不存在: "+id);
47
-        r.setStatus(status); mapper.updateById(r);
146
+
147
+    public Map<String, Object> copyRoute(Long id) {
148
+        Map<String, Object> source = getDetail(id);
149
+        if (source == null) throw new RuntimeException("路线不存在: " + id);
150
+
151
+        String newName = source.get("route_name") + " (副本)";
152
+        Long areaId = source.get("area_id") instanceof Number ? ((Number) source.get("area_id")).longValue() : null;
153
+        Integer estimDuration = source.get("estim_duration") instanceof Number ? ((Number) source.get("estim_duration")).intValue() : null;
154
+        String description = (String) source.get("description");
155
+
156
+        @SuppressWarnings("unchecked")
157
+        List<Map<String, Object>> checkpoints = (List<Map<String, Object>>) source.get("checkpoints");
158
+
159
+        return create(newName, areaId, checkpoints, estimDuration, description);
48 160
     }
49
-    public Map<String, Object> getStats() {
50
-        Map<String, Object> r = new LinkedHashMap<>();
51
-        r.put("total", mapper.selectCount(null));
52
-        r.put("active", mapper.selectCount(new LambdaQueryWrapper<PatrolRouteSetup>().eq(PatrolRouteSetup::getStatus,"active")));
53
-        return r;
161
+
162
+    private long countQuery(String sql, Object... args) {
163
+        Long count = jdbc.queryForObject(sql, Long.class, args);
164
+        return count != null ? count : 0;
54 165
     }
55 166
 }

+ 138
- 49
wm-patrol/src/main/java/com/water/patrol/service/PatrolTemplateService.java Visa fil

@@ -1,59 +1,148 @@
1 1
 package com.water.patrol.service;
2
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
3
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
4
-import com.water.patrol.entity.PatrolTemplate; import com.water.patrol.mapper.PatrolTemplateMapper;
5
-import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
2
+
3
+import lombok.RequiredArgsConstructor;
4
+import lombok.extern.slf4j.Slf4j;
5
+import org.springframework.jdbc.core.JdbcTemplate;
6 6
 import org.springframework.stereotype.Service;
7
+
8
+import java.time.LocalDate;
9
+import java.time.LocalDateTime;
10
+import java.time.LocalTime;
7 11
 import java.util.*;
8
-@Slf4j @Service @RequiredArgsConstructor
12
+
13
+/**
14
+ * 巡检模板设置服务 - PAT-20
15
+ */
16
+@Slf4j
17
+@Service
18
+@RequiredArgsConstructor
9 19
 public class PatrolTemplateService {
10
-    private final PatrolTemplateMapper mapper;
11
-    public PatrolTemplate create(String templateName, String templateCode, Long routeId, String routeName,
12
-            Long formId, String formName, String scheduleType, String scheduleConfig) {
13
-        PatrolTemplate t = new PatrolTemplate(); t.setTemplateName(templateName); t.setTemplateCode(templateCode);
14
-        t.setRouteId(routeId); t.setRouteName(routeName); t.setFormId(formId); t.setFormName(formName);
15
-        t.setScheduleType(scheduleType); t.setScheduleConfig(scheduleConfig); t.setStatus("active");
16
-        mapper.insert(t); return t;
20
+
21
+    private final JdbcTemplate jdbc;
22
+
23
+    public Map<String, Object> create(String name, String type, Map<String, Object> config) {
24
+        String configJson = config != null ? config.toString() : "{}";
25
+        jdbc.update(
26
+            "INSERT INTO patrol_template (name, type, config, status) VALUES (?,?,?::jsonb,'active')",
27
+            name, type, configJson);
28
+
29
+        Map<String, Object> result = new LinkedHashMap<>();
30
+        result.put("name", name);
31
+        result.put("type", type);
32
+        result.put("created", true);
33
+        return result;
17 34
     }
18
-    public PatrolTemplate update(Long id, PatrolTemplate patch) {
19
-        PatrolTemplate t = mapper.selectById(id);
20
-        if (t == null) throw new RuntimeException("模板不存在: "+id);
21
-        if (patch.getTemplateName() != null) t.setTemplateName(patch.getTemplateName());
22
-        if (patch.getRouteId() != null) t.setRouteId(patch.getRouteId());
23
-        if (patch.getRouteName() != null) t.setRouteName(patch.getRouteName());
24
-        if (patch.getFormId() != null) t.setFormId(patch.getFormId());
25
-        if (patch.getFormName() != null) t.setFormName(patch.getFormName());
26
-        if (patch.getScheduleType() != null) t.setScheduleType(patch.getScheduleType());
27
-        if (patch.getScheduleConfig() != null) t.setScheduleConfig(patch.getScheduleConfig());
28
-        if (patch.getRemark() != null) t.setRemark(patch.getRemark());
29
-        mapper.updateById(t); return t;
35
+
36
+    public Map<String, Object> update(Long id, String name, String type, Map<String, Object> config) {
37
+        Map<String, Object> existing = getDetail(id);
38
+        if (existing == null) throw new RuntimeException("模板不存在: " + id);
39
+
40
+        StringBuilder sql = new StringBuilder("UPDATE patrol_template SET updated_at = NOW()");
41
+        List<Object> params = new ArrayList<>();
42
+
43
+        if (name != null) { sql.append(", name = ?"); params.add(name); }
44
+        if (type != null) { sql.append(", type = ?"); params.add(type); }
45
+        if (config != null) { sql.append(", config = ?::jsonb"); params.add(config.toString()); }
46
+
47
+        sql.append(" WHERE id = ?");
48
+        params.add(id);
49
+        jdbc.update(sql.toString(), params.toArray());
50
+
51
+        return getDetail(id);
30 52
     }
31
-    public void delete(Long id) { mapper.deleteById(id); }
32
-    public PatrolTemplate getDetail(Long id) { return mapper.selectById(id); }
33
-    public Map<String, Object> list(String keyword, String status, String scheduleType, int page, int size) {
34
-        LambdaQueryWrapper<PatrolTemplate> w = new LambdaQueryWrapper<>();
35
-        if (keyword != null && !keyword.isEmpty()) w.and(q -> q.like(PatrolTemplate::getTemplateName, keyword).or().like(PatrolTemplate::getTemplateCode, keyword));
36
-        if (status != null && !status.isEmpty()) w.eq(PatrolTemplate::getStatus, status);
37
-        if (scheduleType != null && !scheduleType.isEmpty()) w.eq(PatrolTemplate::getScheduleType, scheduleType);
38
-        w.orderByDesc(PatrolTemplate::getCreatedAt);
39
-        Page<PatrolTemplate> p = mapper.selectPage(new Page<>(page, size), w);
40
-        Map<String, Object> r = new LinkedHashMap<>();
41
-        r.put("records", p.getRecords()); r.put("total", p.getTotal());
42
-        r.put("page", page); r.put("size", size);
43
-        return r;
53
+
54
+    public void delete(Long id) {
55
+        jdbc.update("DELETE FROM patrol_template WHERE id = ?", id);
44 56
     }
45
-    public void updateStatus(Long id, String status) {
46
-        PatrolTemplate t = mapper.selectById(id);
47
-        if (t == null) throw new RuntimeException("模板不存在: "+id);
48
-        t.setStatus(status); mapper.updateById(t);
57
+
58
+    public Map<String, Object> getDetail(Long id) {
59
+        List<Map<String, Object>> rows = jdbc.queryForList("SELECT * FROM patrol_template WHERE id = ?", id);
60
+        if (rows.isEmpty()) return null;
61
+        return new LinkedHashMap<>(rows.get(0));
49 62
     }
50
-    public Map<String, Object> getStats() {
51
-        Map<String, Object> r = new LinkedHashMap<>();
52
-        r.put("total", mapper.selectCount(null));
53
-        r.put("active", mapper.selectCount(new LambdaQueryWrapper<PatrolTemplate>().eq(PatrolTemplate::getStatus,"active")));
54
-        r.put("daily", mapper.selectCount(new LambdaQueryWrapper<PatrolTemplate>().eq(PatrolTemplate::getScheduleType,"daily")));
55
-        r.put("weekly", mapper.selectCount(new LambdaQueryWrapper<PatrolTemplate>().eq(PatrolTemplate::getScheduleType,"weekly")));
56
-        r.put("monthly", mapper.selectCount(new LambdaQueryWrapper<PatrolTemplate>().eq(PatrolTemplate::getScheduleType,"monthly")));
57
-        return r;
63
+
64
+    public Map<String, Object> list(String type) {
65
+        List<Map<String, Object>> templates;
66
+        if (type != null && !type.isEmpty()) {
67
+            templates = jdbc.queryForList(
68
+                "SELECT * FROM patrol_template WHERE type = ? ORDER BY created_at DESC", type);
69
+        } else {
70
+            templates = jdbc.queryForList("SELECT * FROM patrol_template ORDER BY created_at DESC");
71
+        }
72
+
73
+        Map<String, Object> result = new LinkedHashMap<>();
74
+        result.put("total", templates.size());
75
+        result.put("records", templates);
76
+        return result;
77
+    }
78
+
79
+    /**
80
+     * 应用模板生成巡检计划
81
+     */
82
+    public Map<String, Object> applyTemplate(Long templateId, Map<String, String> dateRange) {
83
+        Map<String, Object> template = getDetail(templateId);
84
+        if (template == null) throw new RuntimeException("模板不存在: " + templateId);
85
+
86
+        String type = (String) template.get("type");
87
+        String startDateStr = dateRange.get("startDate");
88
+        String endDateStr = dateRange.get("endDate");
89
+        LocalDate startDate = LocalDate.parse(startDateStr);
90
+        LocalDate endDate = LocalDate.parse(endDateStr);
91
+
92
+        List<LocalDate> taskDates = new ArrayList<>();
93
+        LocalDate current = startDate;
94
+
95
+        switch (type) {
96
+            case "daily":
97
+                while (!current.isAfter(endDate)) {
98
+                    taskDates.add(current);
99
+                    current = current.plusDays(1);
100
+                }
101
+                break;
102
+            case "weekly":
103
+                while (!current.isAfter(endDate)) {
104
+                    taskDates.add(current);
105
+                    current = current.plusWeeks(1);
106
+                }
107
+                break;
108
+            case "monthly":
109
+                while (!current.isAfter(endDate)) {
110
+                    taskDates.add(current);
111
+                    current = current.plusMonths(1);
112
+                }
113
+                break;
114
+            case "special":
115
+                taskDates.add(startDate);
116
+                break;
117
+            default:
118
+                taskDates.add(startDate);
119
+        }
120
+
121
+        int taskCount = 0;
122
+        for (LocalDate taskDate : taskDates) {
123
+            try {
124
+                jdbc.update(
125
+                    "INSERT INTO patrol_task (task_name, task_date, plan_start, plan_end, status) " +
126
+                    "VALUES (?, ?, ?, ?, 'pending')",
127
+                    template.get("name") + " - " + taskDate,
128
+                    taskDate,
129
+                    LocalDateTime.of(taskDate, LocalTime.of(9, 0)),
130
+                    LocalDateTime.of(taskDate, LocalTime.of(17, 0)));
131
+                taskCount++;
132
+            } catch (Exception e) {
133
+                log.warn("Failed to create task for date {}: {}", taskDate, e.getMessage());
134
+            }
135
+        }
136
+
137
+        Map<String, Object> result = new LinkedHashMap<>();
138
+        result.put("templateId", templateId);
139
+        result.put("templateName", template.get("name"));
140
+        result.put("type", type);
141
+        result.put("startDate", startDateStr);
142
+        result.put("endDate", endDateStr);
143
+        result.put("taskDates", taskDates.size());
144
+        result.put("tasksCreated", taskCount);
145
+        result.put("applied", true);
146
+        return result;
58 147
     }
59 148
 }

+ 0
- 122
wm-patrol/src/main/java/com/water/patrol/service/PatrolWoService.java Visa fil

@@ -1,122 +0,0 @@
1
-package com.water.patrol.service;
2
-
3
-import lombok.RequiredArgsConstructor;
4
-import lombok.extern.slf4j.Slf4j;
5
-import org.springframework.jdbc.core.JdbcTemplate;
6
-import org.springframework.stereotype.Service;
7
-
8
-import java.util.*;
9
-
10
-/**
11
- * 巡检工单服务 - Issue #86
12
- * PAT-05 工单管理, PAT-06 工单处理
13
- */
14
-@Slf4j
15
-@Service
16
-@RequiredArgsConstructor
17
-public class PatrolWoService {
18
-
19
-    private final JdbcTemplate jdbc;
20
-
21
-    /** 创建巡检工单 */
22
-    public Map<String, Object> create(String issueType, String description, String severity,
23
-                                       String location, Double lng, Double lat, Long taskId) {
24
-        String orderNo = "PWO-" + System.currentTimeMillis();
25
-        jdbc.update(
26
-            "INSERT INTO patrol_work_order (order_no, task_id, issue_type, description, severity, " +
27
-            "location, lng, lat, status) VALUES (?,?,?,?,?,?,?,?,?)",
28
-            orderNo, taskId, issueType, description,
29
-            severity != null ? severity : "medium",
30
-            location, lng, lat, "pending");
31
-        Map<String, Object> result = new LinkedHashMap<>();
32
-        result.put("orderNo", orderNo);
33
-        result.put("issueType", issueType);
34
-        result.put("status", "pending");
35
-        result.put("created", true);
36
-        return result;
37
-    }
38
-
39
-    /** 分派工单 */
40
-    public Map<String, Object> assign(Long woId, Long assigneeId, String assigneeName) {
41
-        int rows = jdbc.update(
42
-            "UPDATE patrol_work_order SET assignee_id = ?, assignee_name = ?, status = 'assigned' " +
43
-            "WHERE id = ?", assigneeId, assigneeName, woId);
44
-        return Map.of("success", rows > 0, "woId", woId);
45
-    }
46
-
47
-    /** 处理工单 */
48
-    public Map<String, Object> process(Long woId, String resolution) {
49
-        int rows = jdbc.update(
50
-            "UPDATE patrol_work_order SET status = 'processing', resolution = ? WHERE id = ?",
51
-            resolution, woId);
52
-        return Map.of("success", rows > 0, "woId", woId);
53
-    }
54
-
55
-    /** 解决工单 */
56
-    public Map<String, Object> resolve(Long woId) {
57
-        int rows = jdbc.update(
58
-            "UPDATE patrol_work_order SET status = 'resolved', resolved_at = NOW() WHERE id = ?", woId);
59
-        return Map.of("success", rows > 0, "woId", woId);
60
-    }
61
-
62
-    /** 工单详情 */
63
-    public Map<String, Object> getDetail(Long woId) {
64
-        List<Map<String, Object>> rows = jdbc.queryForList(
65
-            "SELECT * FROM patrol_work_order WHERE id = ?", woId);
66
-        return rows.isEmpty() ? null : rows.get(0);
67
-    }
68
-
69
-    /** 工单列表 */
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");
72
-        List<Object> params = new ArrayList<>();
73
-        if (status != null && !status.isEmpty()) {
74
-            sql.append(" AND status = ?");
75
-            params.add(status);
76
-        }
77
-        if (severity != null && !severity.isEmpty()) {
78
-            sql.append(" AND severity = ?");
79
-            params.add(severity);
80
-        }
81
-        sql.append(" ORDER BY created_at DESC LIMIT ? OFFSET ?");
82
-        params.add(size);
83
-        params.add((page - 1) * size);
84
-
85
-        List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
86
-
87
-        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM patrol_work_order WHERE 1=1");
88
-        List<Object> countParams = new ArrayList<>();
89
-        if (status != null && !status.isEmpty()) {
90
-            countSql.append(" AND status = ?");
91
-            countParams.add(status);
92
-        }
93
-        if (severity != null && !severity.isEmpty()) {
94
-            countSql.append(" AND severity = ?");
95
-            countParams.add(severity);
96
-        }
97
-        long total = countQuery(countSql.toString(), countParams.toArray());
98
-
99
-        Map<String, Object> result = new LinkedHashMap<>();
100
-        result.put("total", total);
101
-        result.put("page", page);
102
-        result.put("size", size);
103
-        result.put("records", records);
104
-        return result;
105
-    }
106
-
107
-    /** 工单统计 */
108
-    public Map<String, Object> stats() {
109
-        List<Map<String, Object>> rows = jdbc.queryForList(
110
-            "SELECT status, COUNT(*) as count FROM patrol_work_order GROUP BY status");
111
-        Map<String, Object> result = new LinkedHashMap<>();
112
-        for (Map<String, Object> row : rows) {
113
-            result.put(String.valueOf(row.get("status")), ((Number) row.get("count")).longValue());
114
-        }
115
-        return result;
116
-    }
117
-
118
-    private long countQuery(String sql, Object... args) {
119
-        Long count = jdbc.queryForObject(sql, Long.class, args);
120
-        return count != null ? count : 0;
121
-    }
122
-}

+ 0
- 24
wm-patrol/src/main/resources/sql/V87__patrol_setup.sql Visa fil

@@ -1,24 +0,0 @@
1
-CREATE TABLE IF NOT EXISTS pat_area (
2
-    id BIGSERIAL PRIMARY KEY, area_name VARCHAR(100), area_code VARCHAR(32) UNIQUE,
3
-    description VARCHAR(500), center_lng NUMERIC(10,6), center_lat NUMERIC(10,6),
4
-    boundary TEXT, radius DOUBLE PRECISION, status VARCHAR(20) DEFAULT 'active',
5
-    created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW()
6
-);
7
-CREATE TABLE IF NOT EXISTS pat_route_setup (
8
-    id BIGSERIAL PRIMARY KEY, route_name VARCHAR(100), route_code VARCHAR(32) UNIQUE,
9
-    area_id BIGINT, area_name VARCHAR(100), checkpoint_count INT DEFAULT 0,
10
-    checkpoint_list TEXT, total_distance DOUBLE PRECISION, estimated_minutes INT,
11
-    status VARCHAR(20) DEFAULT 'active', remark VARCHAR(500),
12
-    created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW()
13
-);
14
-CREATE TABLE IF NOT EXISTS pat_form (
15
-    id BIGSERIAL PRIMARY KEY, form_name VARCHAR(100), form_code VARCHAR(32) UNIQUE,
16
-    form_type VARCHAR(30), field_config TEXT, status VARCHAR(20) DEFAULT 'active',
17
-    remark VARCHAR(500), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW()
18
-);
19
-CREATE TABLE IF NOT EXISTS pat_template (
20
-    id BIGSERIAL PRIMARY KEY, template_name VARCHAR(100), template_code VARCHAR(32) UNIQUE,
21
-    route_id BIGINT, route_name VARCHAR(100), form_id BIGINT, form_name VARCHAR(100),
22
-    schedule_type VARCHAR(20), schedule_config TEXT, status VARCHAR(20) DEFAULT 'active',
23
-    remark VARCHAR(500), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW()
24
-);

+ 0
- 25
wm-patrol/src/test/java/com/water/patrol/PatrolSetupTest.java Visa fil

@@ -1,25 +0,0 @@
1
-package com.water.patrol;
2
-import com.water.patrol.entity.*; import com.water.patrol.mapper.*; import com.water.patrol.service.*;
3
-import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith;
4
-import org.mockito.*; import org.mockito.junit.jupiter.MockitoExtension;
5
-import java.math.BigDecimal; import java.util.*;
6
-import static org.junit.jupiter.api.Assertions.*;
7
-import static org.mockito.Mockito.*;
8
-@ExtendWith(MockitoExtension.class)
9
-class PatrolSetupTest {
10
-    @Mock PatrolAreaMapper areaMapper; @Mock PatrolRouteSetupMapper routeMapper;
11
-    @Mock PatrolFormMapper formMapper; @Mock PatrolTemplateMapper tplMapper;
12
-    @InjectMocks PatrolAreaService areaSvc; @InjectMocks PatrolRouteSetupService routeSvc;
13
-    @InjectMocks PatrolFormService formSvc; @InjectMocks PatrolTemplateService tplSvc;
14
-
15
-    @Test void testCreateArea() { when(areaMapper.insert(any())).thenReturn(1); PatrolArea a=areaSvc.create("A区","AREA-001","供水主管",new BigDecimal("116"),new BigDecimal("39"),null,500.0); assertEquals("active",a.getStatus()); assertEquals("A区",a.getAreaName()); }
16
-    @Test void testUpdateArea() { PatrolArea a=new PatrolArea(); a.setId(1L); a.setAreaName("A区"); a.setStatus("active"); when(areaMapper.selectById(1L)).thenReturn(a); when(areaMapper.updateById(any())).thenReturn(1); PatrolArea p=new PatrolArea(); p.setAreaName("B区"); assertEquals("B区",areaSvc.update(1L,p).getAreaName()); }
17
-    @Test void testAreaStatus() { PatrolArea a=new PatrolArea(); a.setId(1L); a.setStatus("active"); when(areaMapper.selectById(1L)).thenReturn(a); when(areaMapper.updateById(any())).thenReturn(1); areaSvc.updateStatus(1L,"inactive"); assertEquals("inactive",a.getStatus()); }
18
-    @Test void testCreateRoute() { when(routeMapper.insert(any())).thenReturn(1); PatrolRouteSetup r=routeSvc.create("主管线","ROUTE-001",1L,"A区",5,null,2000.0,60); assertEquals("active",r.getStatus()); assertEquals(5,(int)r.getCheckpointCount()); }
19
-    @Test void testUpdateRoute() { PatrolRouteSetup r=new PatrolRouteSetup(); r.setId(1L); r.setRouteName("R1"); when(routeMapper.selectById(1L)).thenReturn(r); when(routeMapper.updateById(any())).thenReturn(1); PatrolRouteSetup p=new PatrolRouteSetup(); p.setRouteName("R2"); assertEquals("R2",routeSvc.update(1L,p).getRouteName()); }
20
-    @Test void testRouteStatus() { PatrolRouteSetup r=new PatrolRouteSetup(); r.setId(1L); r.setStatus("active"); when(routeMapper.selectById(1L)).thenReturn(r); when(routeMapper.updateById(any())).thenReturn(1); routeSvc.updateStatus(1L,"inactive"); assertEquals("inactive",r.getStatus()); }
21
-    @Test void testCreateForm() { when(formMapper.insert(any())).thenReturn(1); PatrolForm f=formSvc.create("日常巡检表","FORM-001","checklist","{\"fields\":[]}"); assertEquals("active",f.getStatus()); assertEquals("checklist",f.getFormType()); }
22
-    @Test void testUpdateForm() { PatrolForm f=new PatrolForm(); f.setId(1L); f.setFormName("F1"); when(formMapper.selectById(1L)).thenReturn(f); when(formMapper.updateById(any())).thenReturn(1); PatrolForm p=new PatrolForm(); p.setFormName("F2"); assertEquals("F2",formSvc.update(1L,p).getFormName()); }
23
-    @Test void testCreateTemplate() { when(tplMapper.insert(any())).thenReturn(1); PatrolTemplate t=tplSvc.create("日常模板","TPL-001",1L,"主管线",1L,"日常巡检表","daily","08:00"); assertEquals("active",t.getStatus()); assertEquals("daily",t.getScheduleType()); }
24
-    @Test void testUpdateTemplate() { PatrolTemplate t=new PatrolTemplate(); t.setId(1L); t.setTemplateName("T1"); when(tplMapper.selectById(1L)).thenReturn(t); when(tplMapper.updateById(any())).thenReturn(1); PatrolTemplate p=new PatrolTemplate(); p.setTemplateName("T2"); assertEquals("T2",tplSvc.update(1L,p).getTemplateName()); }
25
-}