feat(wm-patrol): #87 巡查设置

- PatrolAreaService: 巡检区域划分/CRUD/状态管理/统计
- PatrolRouteSetupService: 路线设置/CRUD/关联区域/状态管理
- PatrolFormService: 表单配置/CRUD/类型筛选
- PatrolTemplateService: 模板管理/关联路线表单/调度类型/统计
- 4个Controller共25+端点
- Entity+Mapper: PatrolArea/PatrolRouteSetup/PatrolForm/PatrolTemplate
- DDL: pat_area/pat_route_setup/pat_form/pat_template + 索引
- 10个单元测试
This commit is contained in:
2026-06-15 12:54:09 +08:00
parent f7e60cba23
commit c1e8d60030
21 changed files with 962 additions and 0 deletions
@@ -0,0 +1,16 @@
package com.water.patrol.controller;
import com.water.common.core.result.R; import com.water.patrol.entity.PatrolArea; import com.water.patrol.service.PatrolAreaService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal; import java.util.Map;
@Tag(name="巡检区域") @RestController @RequestMapping("/api/patrol/area") @RequiredArgsConstructor
public class PatrolAreaController {
private final PatrolAreaService svc;
@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)); }
@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)); }
@Operation(summary="区域详情") @GetMapping("/{id}") public R<PatrolArea> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
@Operation(summary="更新区域") @PutMapping("/{id}") public R<PatrolArea> update(@PathVariable Long id, @RequestBody PatrolArea patch) { return R.ok(svc.update(id,patch)); }
@Operation(summary="更新状态") @PutMapping("/{id}/status") public R<String> updateStatus(@PathVariable Long id, @RequestParam String status) { svc.updateStatus(id,status); return R.ok("OK"); }
@Operation(summary="删除区域") @DeleteMapping("/{id}") public R<String> delete(@PathVariable Long id) { svc.delete(id); return R.ok("OK"); }
@Operation(summary="区域统计") @GetMapping("/stats") public R<Map<String,Object>> stats() { return R.ok(svc.getStats()); }
}
@@ -0,0 +1,186 @@
package com.water.patrol.controller;
import com.water.common.core.result.R;
import com.water.patrol.service.PatrolCoreService;
import com.water.patrol.service.PatrolWoService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 巡检管理核心控制器 - Issue #86
* PAT-01~PAT-06
*/
@Tag(name = "巡检管理核心")
@RestController
@RequestMapping("/patrol/core")
@RequiredArgsConstructor
public class PatrolCoreController {
private final PatrolCoreService coreService;
private final PatrolWoService woService;
// ========== PAT-01 巡检总览 ==========
@GetMapping("/overview")
@Operation(summary = "巡检总览数据")
public R<Map<String, Object>> overview() {
return R.ok(coreService.getOverview());
}
@GetMapping("/overview/today-tasks")
@Operation(summary = "今日任务")
public R<List<Map<String, Object>>> todayTasks() {
return R.ok(coreService.getTodayTasks());
}
@GetMapping("/overview/recent-issues")
@Operation(summary = "最近问题")
public R<List<Map<String, Object>>> recentIssues(@RequestParam(defaultValue = "10") int limit) {
return R.ok(coreService.getRecentIssues(limit));
}
// ========== PAT-02 任务轨迹 ==========
@PostMapping("/track/record")
@Operation(summary = "记录GPS轨迹点")
public R<Map<String, Object>> recordTrack(@RequestBody Map<String, Object> req) {
Long taskId = Long.parseLong(String.valueOf(req.get("taskId")));
Long workerId = req.get("workerId") != null ? Long.parseLong(String.valueOf(req.get("workerId"))) : null;
double lng = ((Number) req.get("lng")).doubleValue();
double lat = ((Number) req.get("lat")).doubleValue();
double speed = req.get("speed") != null ? ((Number) req.get("speed")).doubleValue() : 0;
double accuracy = req.get("accuracy") != null ? ((Number) req.get("accuracy")).doubleValue() : 0;
return R.ok(coreService.recordTrackPoint(taskId, workerId, lng, lat, speed, accuracy));
}
@GetMapping("/track/{taskId}")
@Operation(summary = "获取任务轨迹")
public R<List<Map<String, Object>>> getTrack(@PathVariable Long taskId) {
return R.ok(coreService.getTrack(taskId));
}
@GetMapping("/track/{taskId}/replay")
@Operation(summary = "轨迹回放")
public R<Map<String, Object>> replayTrack(@PathVariable Long taskId) {
return R.ok(coreService.replayTrack(taskId));
}
@GetMapping("/track/{taskId}/stats")
@Operation(summary = "轨迹统计")
public R<Map<String, Object>> trackStats(@PathVariable Long taskId) {
return R.ok(coreService.getTrackStats(taskId));
}
// ========== PAT-03 任务台账 ==========
@GetMapping("/ledger/tasks")
@Operation(summary = "任务台账")
public R<Map<String, Object>> taskLedger(
@RequestParam(required = false) String status,
@RequestParam(required = false) Long workerId,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
return R.ok(coreService.getTaskLedger(status, workerId, page, size));
}
// ========== PAT-04 设备台账 ==========
@GetMapping("/ledger/devices")
@Operation(summary = "设备台账")
public R<Map<String, Object>> deviceLedger(
@RequestParam(required = false) String type,
@RequestParam(required = false) String status,
@RequestParam(required = false) String area,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
return R.ok(coreService.getDeviceLedger(type, status, area, page, size));
}
@PostMapping("/device")
@Operation(summary = "新增设备")
public R<Map<String, Object>> createDevice(@RequestBody Map<String, Object> req) {
return R.ok(coreService.createDevice(
(String) req.get("deviceNo"),
(String) req.get("deviceName"),
(String) req.get("deviceType"),
(String) req.get("location"),
((Number) req.getOrDefault("lng", 0)).doubleValue(),
((Number) req.getOrDefault("lat", 0)).doubleValue(),
(String) req.get("area")));
}
@PutMapping("/device/{deviceId}/status")
@Operation(summary = "更新设备状态")
public R<Map<String, Object>> updateDeviceStatus(@PathVariable Long deviceId,
@RequestBody Map<String, Object> req) {
return R.ok(coreService.updateDeviceStatus(deviceId, (String) req.get("status")));
}
// ========== PAT-05 工单管理 ==========
@PostMapping("/work-order")
@Operation(summary = "创建巡检工单")
public R<Map<String, Object>> createWo(@RequestBody Map<String, Object> req) {
Long taskId = req.get("taskId") != null ? Long.parseLong(String.valueOf(req.get("taskId"))) : null;
return R.ok(woService.create(
(String) req.get("issueType"),
(String) req.get("description"),
(String) req.get("severity"),
(String) req.get("location"),
req.get("lng") != null ? ((Number) req.get("lng")).doubleValue() : null,
req.get("lat") != null ? ((Number) req.get("lat")).doubleValue() : null,
taskId));
}
@GetMapping("/work-order/list")
@Operation(summary = "工单列表")
public R<Map<String, Object>> listWo(
@RequestParam(required = false) String status,
@RequestParam(required = false) String severity,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
return R.ok(woService.list(status, severity, page, size));
}
@GetMapping("/work-order/{woId}")
@Operation(summary = "工单详情")
public R<Map<String, Object>> woDetail(@PathVariable Long woId) {
Map<String, Object> detail = woService.getDetail(woId);
if (detail == null) {
return R.fail("工单不存在");
}
return R.ok(detail);
}
@GetMapping("/work-order/stats")
@Operation(summary = "工单统计")
public R<Map<String, Object>> woStats() {
return R.ok(woService.stats());
}
// ========== PAT-06 工单处理 ==========
@PutMapping("/work-order/{woId}/assign")
@Operation(summary = "分派工单")
public R<Map<String, Object>> assignWo(@PathVariable Long woId, @RequestBody Map<String, Object> req) {
Long assigneeId = Long.parseLong(String.valueOf(req.get("assigneeId")));
return R.ok(woService.assign(woId, assigneeId, (String) req.get("assigneeName")));
}
@PutMapping("/work-order/{woId}/process")
@Operation(summary = "处理工单")
public R<Map<String, Object>> processWo(@PathVariable Long woId, @RequestBody Map<String, Object> req) {
return R.ok(woService.process(woId, (String) req.get("resolution")));
}
@PutMapping("/work-order/{woId}/resolve")
@Operation(summary = "解决工单")
public R<Map<String, Object>> resolveWo(@PathVariable Long woId) {
return R.ok(woService.resolve(woId));
}
}
@@ -0,0 +1,15 @@
package com.water.patrol.controller;
import com.water.common.core.result.R; import com.water.patrol.entity.PatrolForm; import com.water.patrol.service.PatrolFormService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*;
import java.util.Map;
@Tag(name="巡检表单") @RestController @RequestMapping("/api/patrol/form") @RequiredArgsConstructor
public class PatrolFormController {
private final PatrolFormService svc;
@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"))); }
@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)); }
@Operation(summary="表单详情") @GetMapping("/{id}") public R<PatrolForm> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
@Operation(summary="更新表单") @PutMapping("/{id}") public R<PatrolForm> update(@PathVariable Long id, @RequestBody PatrolForm patch) { return R.ok(svc.update(id,patch)); }
@Operation(summary="删除表单") @DeleteMapping("/{id}") public R<String> delete(@PathVariable Long id) { svc.delete(id); return R.ok("OK"); }
@Operation(summary="表单统计") @GetMapping("/stats") public R<Map<String,Object>> stats() { return R.ok(svc.getStats()); }
}
@@ -0,0 +1,16 @@
package com.water.patrol.controller;
import com.water.common.core.result.R; import com.water.patrol.entity.PatrolRouteSetup; import com.water.patrol.service.PatrolRouteSetupService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*;
import java.util.Map;
@Tag(name="巡检路线设置") @RestController @RequestMapping("/api/patrol/route-setup") @RequiredArgsConstructor
public class PatrolRouteSetupController {
private final PatrolRouteSetupService svc;
@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"))); }
@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)); }
@Operation(summary="路线详情") @GetMapping("/{id}") public R<PatrolRouteSetup> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
@Operation(summary="更新路线") @PutMapping("/{id}") public R<PatrolRouteSetup> update(@PathVariable Long id, @RequestBody PatrolRouteSetup patch) { return R.ok(svc.update(id,patch)); }
@Operation(summary="更新状态") @PutMapping("/{id}/status") public R<String> updateStatus(@PathVariable Long id, @RequestParam String status) { svc.updateStatus(id,status); return R.ok("OK"); }
@Operation(summary="删除路线") @DeleteMapping("/{id}") public R<String> delete(@PathVariable Long id) { svc.delete(id); return R.ok("OK"); }
@Operation(summary="路线统计") @GetMapping("/stats") public R<Map<String,Object>> stats() { return R.ok(svc.getStats()); }
}
@@ -0,0 +1,16 @@
package com.water.patrol.controller;
import com.water.common.core.result.R; import com.water.patrol.entity.PatrolTemplate; import com.water.patrol.service.PatrolTemplateService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*;
import java.util.Map;
@Tag(name="巡检模板") @RestController @RequestMapping("/api/patrol/template") @RequiredArgsConstructor
public class PatrolTemplateController {
private final PatrolTemplateService svc;
@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"))); }
@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)); }
@Operation(summary="模板详情") @GetMapping("/{id}") public R<PatrolTemplate> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
@Operation(summary="更新模板") @PutMapping("/{id}") public R<PatrolTemplate> update(@PathVariable Long id, @RequestBody PatrolTemplate patch) { return R.ok(svc.update(id,patch)); }
@Operation(summary="更新状态") @PutMapping("/{id}/status") public R<String> updateStatus(@PathVariable Long id, @RequestParam String status) { svc.updateStatus(id,status); return R.ok("OK"); }
@Operation(summary="删除模板") @DeleteMapping("/{id}") public R<String> delete(@PathVariable Long id) { svc.delete(id); return R.ok("OK"); }
@Operation(summary="模板统计") @GetMapping("/stats") public R<Map<String,Object>> stats() { return R.ok(svc.getStats()); }
}
@@ -0,0 +1,15 @@
package com.water.patrol.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data @TableName("pat_area")
public class PatrolArea {
@TableId(type = IdType.AUTO) private Long id;
private String areaName; private String areaCode; private String description;
private BigDecimal centerLng; private BigDecimal centerLat;
private String boundary; private Double radius;
private String status;
@TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt;
@TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updatedAt;
}
@@ -0,0 +1,13 @@
package com.water.patrol.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data @TableName("pat_form")
public class PatrolForm {
@TableId(type = IdType.AUTO) private Long id;
private String formName; private String formCode;
private String formType; private String fieldConfig;
private String status; private String remark;
@TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt;
@TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updatedAt;
}
@@ -0,0 +1,15 @@
package com.water.patrol.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data @TableName("pat_route_setup")
public class PatrolRouteSetup {
@TableId(type = IdType.AUTO) private Long id;
private String routeName; private String routeCode;
private Long areaId; private String areaName;
private Integer checkpointCount; private String checkpointList;
private Double totalDistance; private Integer estimatedMinutes;
private String status; private String remark;
@TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt;
@TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updatedAt;
}
@@ -0,0 +1,15 @@
package com.water.patrol.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data @TableName("pat_template")
public class PatrolTemplate {
@TableId(type = IdType.AUTO) private Long id;
private String templateName; private String templateCode;
private Long routeId; private String routeName;
private Long formId; private String formName;
private String scheduleType; private String scheduleConfig;
private String status; private String remark;
@TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt;
@TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updatedAt;
}
@@ -0,0 +1,5 @@
package com.water.patrol.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.patrol.entity.PatrolArea;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface PatrolAreaMapper extends BaseMapper<PatrolArea> {}
@@ -0,0 +1,5 @@
package com.water.patrol.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.patrol.entity.PatrolForm;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface PatrolFormMapper extends BaseMapper<PatrolForm> {}
@@ -0,0 +1,5 @@
package com.water.patrol.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.patrol.entity.PatrolRouteSetup;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface PatrolRouteSetupMapper extends BaseMapper<PatrolRouteSetup> {}
@@ -0,0 +1,5 @@
package com.water.patrol.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.patrol.entity.PatrolTemplate;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface PatrolTemplateMapper extends BaseMapper<PatrolTemplate> {}
@@ -0,0 +1,53 @@
package com.water.patrol.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.water.patrol.entity.PatrolArea; import com.water.patrol.mapper.PatrolAreaMapper;
import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.util.*;
@Slf4j @Service @RequiredArgsConstructor
public class PatrolAreaService {
private final PatrolAreaMapper mapper;
public PatrolArea create(String areaName, String areaCode, String desc, BigDecimal cLng, BigDecimal cLat, String boundary, Double radius) {
PatrolArea a = new PatrolArea(); a.setAreaName(areaName); a.setAreaCode(areaCode);
a.setDescription(desc); a.setCenterLng(cLng); a.setCenterLat(cLat);
a.setBoundary(boundary); a.setRadius(radius); a.setStatus("active");
mapper.insert(a); return a;
}
public PatrolArea update(Long id, PatrolArea patch) {
PatrolArea a = mapper.selectById(id);
if (a == null) throw new RuntimeException("区域不存在: "+id);
if (patch.getAreaName() != null) a.setAreaName(patch.getAreaName());
if (patch.getDescription() != null) a.setDescription(patch.getDescription());
if (patch.getCenterLng() != null) a.setCenterLng(patch.getCenterLng());
if (patch.getCenterLat() != null) a.setCenterLat(patch.getCenterLat());
if (patch.getBoundary() != null) a.setBoundary(patch.getBoundary());
if (patch.getRadius() != null) a.setRadius(patch.getRadius());
mapper.updateById(a); return a;
}
public void delete(Long id) { mapper.deleteById(id); }
public PatrolArea getDetail(Long id) { return mapper.selectById(id); }
public Map<String, Object> list(String keyword, String status, int page, int size) {
LambdaQueryWrapper<PatrolArea> w = new LambdaQueryWrapper<>();
if (keyword != null && !keyword.isEmpty()) w.and(q -> q.like(PatrolArea::getAreaName, keyword).or().like(PatrolArea::getAreaCode, keyword));
if (status != null && !status.isEmpty()) w.eq(PatrolArea::getStatus, status);
w.orderByDesc(PatrolArea::getCreatedAt);
Page<PatrolArea> p = mapper.selectPage(new Page<>(page, size), w);
Map<String, Object> r = new LinkedHashMap<>();
r.put("records", p.getRecords()); r.put("total", p.getTotal());
r.put("page", page); r.put("size", size);
return r;
}
public void updateStatus(Long id, String status) {
PatrolArea a = mapper.selectById(id);
if (a == null) throw new RuntimeException("区域不存在: "+id);
a.setStatus(status); mapper.updateById(a);
}
public Map<String, Object> getStats() {
Map<String, Object> r = new LinkedHashMap<>();
r.put("total", mapper.selectCount(null));
r.put("active", mapper.selectCount(new LambdaQueryWrapper<PatrolArea>().eq(PatrolArea::getStatus,"active")));
r.put("inactive", mapper.selectCount(new LambdaQueryWrapper<PatrolArea>().eq(PatrolArea::getStatus,"inactive")));
return r;
}
}
@@ -0,0 +1,252 @@
package com.water.patrol.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
/**
* 巡检管理核心服务 - Issue #86
* PAT-01 总览, PAT-02 轨迹, PAT-03 任务台账, PAT-04 设备台账
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PatrolCoreService {
private final JdbcTemplate jdbc;
// ========== PAT-01 巡检总览 ==========
public Map<String, Object> getOverview() {
LocalDate today = LocalDate.now();
LocalDate monthStart = today.withDayOfMonth(1);
long todayTotal = countQuery("SELECT COUNT(*) FROM patrol_task WHERE task_date = ?", today);
long todayCompleted = countQuery("SELECT COUNT(*) FROM patrol_task WHERE task_date = ? AND status = 'completed'", today);
long monthTotal = countQuery("SELECT COUNT(*) FROM patrol_task WHERE task_date >= ?", monthStart);
long monthCompleted = countQuery("SELECT COUNT(*) FROM patrol_task WHERE task_date >= ? AND status = 'completed'", monthStart);
long pendingIssues = countQuery("SELECT COUNT(*) FROM patrol_work_order WHERE status IN ('pending','assigned','processing')");
long resolvedIssues = countQuery("SELECT COUNT(*) FROM patrol_work_order WHERE status = 'resolved'");
long coverageRate = monthTotal > 0 ? Math.round(monthCompleted * 100.0 / monthTotal) : 0;
Map<String, Object> overview = new LinkedHashMap<>();
overview.put("todayTotal", todayTotal);
overview.put("todayCompleted", todayCompleted);
overview.put("monthTotal", monthTotal);
overview.put("monthCompleted", monthCompleted);
overview.put("coverageRate", coverageRate);
overview.put("pendingIssues", pendingIssues);
overview.put("resolvedIssues", resolvedIssues);
return overview;
}
public List<Map<String, Object>> getTodayTasks() {
return jdbc.queryForList(
"SELECT pt.*, pr.route_name, pr.area FROM patrol_task pt " +
"LEFT JOIN patrol_route pr ON pt.route_id = pr.id " +
"WHERE pt.task_date = CURRENT_DATE ORDER BY pt.plan_start");
}
public List<Map<String, Object>> getRecentIssues(int limit) {
return jdbc.queryForList(
"SELECT * FROM patrol_work_order ORDER BY created_at DESC LIMIT ?", limit);
}
// ========== PAT-02 任务轨迹(GPS记录与回放) ==========
public Map<String, Object> recordTrackPoint(Long taskId, Long workerId,
double lng, double lat,
double speed, double accuracy) {
jdbc.update(
"INSERT INTO patrol_track_point (task_id, worker_id, lng, lat, speed, accuracy) " +
"VALUES (?,?,?,?,?,?)",
taskId, workerId, lng, lat, speed, accuracy);
Map<String, Object> result = new LinkedHashMap<>();
result.put("taskId", taskId);
result.put("workerId", workerId);
result.put("lng", lng);
result.put("lat", lat);
result.put("recorded", true);
return result;
}
public List<Map<String, Object>> getTrack(Long taskId) {
return jdbc.queryForList(
"SELECT * FROM patrol_track_point WHERE task_id = ? ORDER BY recorded_at", taskId);
}
public Map<String, Object> replayTrack(Long taskId) {
List<Map<String, Object>> points = getTrack(taskId);
Map<String, Object> result = new LinkedHashMap<>();
result.put("taskId", taskId);
result.put("points", points);
result.put("totalPoints", points.size());
if (!points.isEmpty()) {
result.put("startTime", points.get(0).get("recorded_at"));
result.put("endTime", points.get(points.size() - 1).get("recorded_at"));
}
return result;
}
public Map<String, Object> getTrackStats(Long taskId) {
List<Map<String, Object>> points = getTrack(taskId);
Map<String, Object> stats = new LinkedHashMap<>();
stats.put("taskId", taskId);
stats.put("totalPoints", points.size());
if (points.size() < 2) {
stats.put("totalDistance", 0.0);
stats.put("duration", 0);
stats.put("avgSpeed", 0.0);
return stats;
}
double totalDistance = 0;
for (int i = 1; i < points.size(); i++) {
double lat1 = ((Number) points.get(i - 1).get("lat")).doubleValue();
double lon1 = ((Number) points.get(i - 1).get("lng")).doubleValue();
double lat2 = ((Number) points.get(i).get("lat")).doubleValue();
double lon2 = ((Number) points.get(i).get("lng")).doubleValue();
totalDistance += haversine(lat1, lon1, lat2, lon2);
}
double avgSpeed = points.stream()
.filter(p -> p.get("speed") != null)
.mapToDouble(p -> ((Number) p.get("speed")).doubleValue())
.average().orElse(0);
stats.put("totalDistance", Math.round(totalDistance * 100.0) / 100.0);
stats.put("avgSpeed", Math.round(avgSpeed * 100.0) / 100.0);
return stats;
}
// ========== PAT-03 任务台账 ==========
public Map<String, Object> getTaskLedger(String status, Long workerId, int page, int size) {
StringBuilder sql = new StringBuilder("SELECT * FROM patrol_task WHERE 1=1");
List<Object> params = new ArrayList<>();
if (status != null && !status.isEmpty()) {
sql.append(" AND status = ?");
params.add(status);
}
if (workerId != null) {
sql.append(" AND assignee_id = ?");
params.add(workerId);
}
sql.append(" ORDER BY task_date DESC LIMIT ? OFFSET ?");
params.add(size);
params.add((page - 1) * size);
List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM patrol_task WHERE 1=1");
List<Object> countParams = new ArrayList<>();
if (status != null && !status.isEmpty()) {
countSql.append(" AND status = ?");
countParams.add(status);
}
if (workerId != null) {
countSql.append(" AND assignee_id = ?");
countParams.add(workerId);
}
long total = countQuery(countSql.toString(), countParams.toArray());
Map<String, Object> result = new LinkedHashMap<>();
result.put("total", total);
result.put("page", page);
result.put("size", size);
result.put("records", records);
return result;
}
// ========== PAT-04 设备台账 ==========
public Map<String, Object> getDeviceLedger(String deviceType, String status, String area,
int page, int size) {
StringBuilder sql = new StringBuilder("SELECT * FROM patrol_device WHERE 1=1");
List<Object> params = new ArrayList<>();
if (deviceType != null && !deviceType.isEmpty()) {
sql.append(" AND device_type = ?");
params.add(deviceType);
}
if (status != null && !status.isEmpty()) {
sql.append(" AND status = ?");
params.add(status);
}
if (area != null && !area.isEmpty()) {
sql.append(" AND area = ?");
params.add(area);
}
sql.append(" ORDER BY created_at DESC LIMIT ? OFFSET ?");
params.add(size);
params.add((page - 1) * size);
List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM patrol_device WHERE 1=1");
List<Object> countParams = new ArrayList<>();
if (deviceType != null && !deviceType.isEmpty()) {
countSql.append(" AND device_type = ?");
countParams.add(deviceType);
}
if (status != null && !status.isEmpty()) {
countSql.append(" AND status = ?");
countParams.add(status);
}
if (area != null && !area.isEmpty()) {
countSql.append(" AND area = ?");
countParams.add(area);
}
long total = countQuery(countSql.toString(), countParams.toArray());
Map<String, Object> result = new LinkedHashMap<>();
result.put("total", total);
result.put("page", page);
result.put("size", size);
result.put("records", records);
return result;
}
public Map<String, Object> createDevice(String deviceNo, String deviceName, String deviceType,
String location, double lng, double lat, String area) {
jdbc.update(
"INSERT INTO patrol_device (device_no, device_name, device_type, location, lng, lat, area) " +
"VALUES (?,?,?,?,?,?,?)",
deviceNo, deviceName, deviceType, location, lng, lat, area);
Map<String, Object> result = new LinkedHashMap<>();
result.put("deviceNo", deviceNo);
result.put("deviceName", deviceName);
result.put("created", true);
return result;
}
public Map<String, Object> updateDeviceStatus(Long deviceId, String status) {
jdbc.update("UPDATE patrol_device SET status = ?, updated_at = NOW() WHERE id = ?",
status, deviceId);
return Map.of("deviceId", deviceId, "status", status, "updated", true);
}
// ========== 工具方法 ==========
private long countQuery(String sql, Object... args) {
Long count = jdbc.queryForObject(sql, Long.class, args);
return count != null ? count : 0;
}
private double haversine(double lat1, double lon1, double lat2, double lon2) {
double R = 6371;
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
* Math.sin(dLon / 2) * Math.sin(dLon / 2);
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
}
@@ -0,0 +1,45 @@
package com.water.patrol.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.water.patrol.entity.PatrolForm; import com.water.patrol.mapper.PatrolFormMapper;
import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.*;
@Slf4j @Service @RequiredArgsConstructor
public class PatrolFormService {
private final PatrolFormMapper mapper;
public PatrolForm create(String formName, String formCode, String formType, String fieldConfig) {
PatrolForm f = new PatrolForm(); f.setFormName(formName); f.setFormCode(formCode);
f.setFormType(formType); f.setFieldConfig(fieldConfig); f.setStatus("active");
mapper.insert(f); return f;
}
public PatrolForm update(Long id, PatrolForm patch) {
PatrolForm f = mapper.selectById(id);
if (f == null) throw new RuntimeException("表单不存在: "+id);
if (patch.getFormName() != null) f.setFormName(patch.getFormName());
if (patch.getFieldConfig() != null) f.setFieldConfig(patch.getFieldConfig());
if (patch.getFormType() != null) f.setFormType(patch.getFormType());
if (patch.getRemark() != null) f.setRemark(patch.getRemark());
mapper.updateById(f); return f;
}
public void delete(Long id) { mapper.deleteById(id); }
public PatrolForm getDetail(Long id) { return mapper.selectById(id); }
public Map<String, Object> list(String keyword, String formType, String status, int page, int size) {
LambdaQueryWrapper<PatrolForm> w = new LambdaQueryWrapper<>();
if (keyword != null && !keyword.isEmpty()) w.and(q -> q.like(PatrolForm::getFormName, keyword).or().like(PatrolForm::getFormCode, keyword));
if (formType != null && !formType.isEmpty()) w.eq(PatrolForm::getFormType, formType);
if (status != null && !status.isEmpty()) w.eq(PatrolForm::getStatus, status);
w.orderByDesc(PatrolForm::getCreatedAt);
Page<PatrolForm> p = mapper.selectPage(new Page<>(page, size), w);
Map<String, Object> r = new LinkedHashMap<>();
r.put("records", p.getRecords()); r.put("total", p.getTotal());
r.put("page", page); r.put("size", size);
return r;
}
public Map<String, Object> getStats() {
Map<String, Object> r = new LinkedHashMap<>();
r.put("total", mapper.selectCount(null));
r.put("active", mapper.selectCount(new LambdaQueryWrapper<PatrolForm>().eq(PatrolForm::getStatus,"active")));
return r;
}
}
@@ -0,0 +1,55 @@
package com.water.patrol.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.water.patrol.entity.PatrolRouteSetup; import com.water.patrol.mapper.PatrolRouteSetupMapper;
import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.*;
@Slf4j @Service @RequiredArgsConstructor
public class PatrolRouteSetupService {
private final PatrolRouteSetupMapper mapper;
public PatrolRouteSetup create(String routeName, String routeCode, Long areaId, String areaName,
Integer checkpointCount, String checkpointList, Double totalDistance, Integer estimatedMinutes) {
PatrolRouteSetup r = new PatrolRouteSetup(); r.setRouteName(routeName); r.setRouteCode(routeCode);
r.setAreaId(areaId); r.setAreaName(areaName); r.setCheckpointCount(checkpointCount);
r.setCheckpointList(checkpointList); r.setTotalDistance(totalDistance);
r.setEstimatedMinutes(estimatedMinutes); r.setStatus("active");
mapper.insert(r); return r;
}
public PatrolRouteSetup update(Long id, PatrolRouteSetup patch) {
PatrolRouteSetup r = mapper.selectById(id);
if (r == null) throw new RuntimeException("路线不存在: "+id);
if (patch.getRouteName() != null) r.setRouteName(patch.getRouteName());
if (patch.getCheckpointCount() != null) r.setCheckpointCount(patch.getCheckpointCount());
if (patch.getCheckpointList() != null) r.setCheckpointList(patch.getCheckpointList());
if (patch.getTotalDistance() != null) r.setTotalDistance(patch.getTotalDistance());
if (patch.getEstimatedMinutes() != null) r.setEstimatedMinutes(patch.getEstimatedMinutes());
if (patch.getRemark() != null) r.setRemark(patch.getRemark());
mapper.updateById(r); return r;
}
public void delete(Long id) { mapper.deleteById(id); }
public PatrolRouteSetup getDetail(Long id) { return mapper.selectById(id); }
public Map<String, Object> list(String keyword, String status, Long areaId, int page, int size) {
LambdaQueryWrapper<PatrolRouteSetup> w = new LambdaQueryWrapper<>();
if (keyword != null && !keyword.isEmpty()) w.and(q -> q.like(PatrolRouteSetup::getRouteName, keyword).or().like(PatrolRouteSetup::getRouteCode, keyword));
if (status != null && !status.isEmpty()) w.eq(PatrolRouteSetup::getStatus, status);
if (areaId != null) w.eq(PatrolRouteSetup::getAreaId, areaId);
w.orderByDesc(PatrolRouteSetup::getCreatedAt);
Page<PatrolRouteSetup> p = mapper.selectPage(new Page<>(page, size), w);
Map<String, Object> result = new LinkedHashMap<>();
result.put("records", p.getRecords()); result.put("total", p.getTotal());
result.put("page", page); result.put("size", size);
return result;
}
public void updateStatus(Long id, String status) {
PatrolRouteSetup r = mapper.selectById(id);
if (r == null) throw new RuntimeException("路线不存在: "+id);
r.setStatus(status); mapper.updateById(r);
}
public Map<String, Object> getStats() {
Map<String, Object> r = new LinkedHashMap<>();
r.put("total", mapper.selectCount(null));
r.put("active", mapper.selectCount(new LambdaQueryWrapper<PatrolRouteSetup>().eq(PatrolRouteSetup::getStatus,"active")));
return r;
}
}
@@ -0,0 +1,59 @@
package com.water.patrol.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.water.patrol.entity.PatrolTemplate; import com.water.patrol.mapper.PatrolTemplateMapper;
import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.*;
@Slf4j @Service @RequiredArgsConstructor
public class PatrolTemplateService {
private final PatrolTemplateMapper mapper;
public PatrolTemplate create(String templateName, String templateCode, Long routeId, String routeName,
Long formId, String formName, String scheduleType, String scheduleConfig) {
PatrolTemplate t = new PatrolTemplate(); t.setTemplateName(templateName); t.setTemplateCode(templateCode);
t.setRouteId(routeId); t.setRouteName(routeName); t.setFormId(formId); t.setFormName(formName);
t.setScheduleType(scheduleType); t.setScheduleConfig(scheduleConfig); t.setStatus("active");
mapper.insert(t); return t;
}
public PatrolTemplate update(Long id, PatrolTemplate patch) {
PatrolTemplate t = mapper.selectById(id);
if (t == null) throw new RuntimeException("模板不存在: "+id);
if (patch.getTemplateName() != null) t.setTemplateName(patch.getTemplateName());
if (patch.getRouteId() != null) t.setRouteId(patch.getRouteId());
if (patch.getRouteName() != null) t.setRouteName(patch.getRouteName());
if (patch.getFormId() != null) t.setFormId(patch.getFormId());
if (patch.getFormName() != null) t.setFormName(patch.getFormName());
if (patch.getScheduleType() != null) t.setScheduleType(patch.getScheduleType());
if (patch.getScheduleConfig() != null) t.setScheduleConfig(patch.getScheduleConfig());
if (patch.getRemark() != null) t.setRemark(patch.getRemark());
mapper.updateById(t); return t;
}
public void delete(Long id) { mapper.deleteById(id); }
public PatrolTemplate getDetail(Long id) { return mapper.selectById(id); }
public Map<String, Object> list(String keyword, String status, String scheduleType, int page, int size) {
LambdaQueryWrapper<PatrolTemplate> w = new LambdaQueryWrapper<>();
if (keyword != null && !keyword.isEmpty()) w.and(q -> q.like(PatrolTemplate::getTemplateName, keyword).or().like(PatrolTemplate::getTemplateCode, keyword));
if (status != null && !status.isEmpty()) w.eq(PatrolTemplate::getStatus, status);
if (scheduleType != null && !scheduleType.isEmpty()) w.eq(PatrolTemplate::getScheduleType, scheduleType);
w.orderByDesc(PatrolTemplate::getCreatedAt);
Page<PatrolTemplate> p = mapper.selectPage(new Page<>(page, size), w);
Map<String, Object> r = new LinkedHashMap<>();
r.put("records", p.getRecords()); r.put("total", p.getTotal());
r.put("page", page); r.put("size", size);
return r;
}
public void updateStatus(Long id, String status) {
PatrolTemplate t = mapper.selectById(id);
if (t == null) throw new RuntimeException("模板不存在: "+id);
t.setStatus(status); mapper.updateById(t);
}
public Map<String, Object> getStats() {
Map<String, Object> r = new LinkedHashMap<>();
r.put("total", mapper.selectCount(null));
r.put("active", mapper.selectCount(new LambdaQueryWrapper<PatrolTemplate>().eq(PatrolTemplate::getStatus,"active")));
r.put("daily", mapper.selectCount(new LambdaQueryWrapper<PatrolTemplate>().eq(PatrolTemplate::getScheduleType,"daily")));
r.put("weekly", mapper.selectCount(new LambdaQueryWrapper<PatrolTemplate>().eq(PatrolTemplate::getScheduleType,"weekly")));
r.put("monthly", mapper.selectCount(new LambdaQueryWrapper<PatrolTemplate>().eq(PatrolTemplate::getScheduleType,"monthly")));
return r;
}
}
@@ -0,0 +1,122 @@
package com.water.patrol.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.*;
/**
* 巡检工单服务 - Issue #86
* PAT-05 工单管理, PAT-06 工单处理
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PatrolWoService {
private final JdbcTemplate jdbc;
/** 创建巡检工单 */
public Map<String, Object> create(String issueType, String description, String severity,
String location, Double lng, Double lat, Long taskId) {
String orderNo = "PWO-" + System.currentTimeMillis();
jdbc.update(
"INSERT INTO patrol_work_order (order_no, task_id, issue_type, description, severity, " +
"location, lng, lat, status) VALUES (?,?,?,?,?,?,?,?,?)",
orderNo, taskId, issueType, description,
severity != null ? severity : "medium",
location, lng, lat, "pending");
Map<String, Object> result = new LinkedHashMap<>();
result.put("orderNo", orderNo);
result.put("issueType", issueType);
result.put("status", "pending");
result.put("created", true);
return result;
}
/** 分派工单 */
public Map<String, Object> assign(Long woId, Long assigneeId, String assigneeName) {
int rows = jdbc.update(
"UPDATE patrol_work_order SET assignee_id = ?, assignee_name = ?, status = 'assigned' " +
"WHERE id = ?", assigneeId, assigneeName, woId);
return Map.of("success", rows > 0, "woId", woId);
}
/** 处理工单 */
public Map<String, Object> process(Long woId, String resolution) {
int rows = jdbc.update(
"UPDATE patrol_work_order SET status = 'processing', resolution = ? WHERE id = ?",
resolution, woId);
return Map.of("success", rows > 0, "woId", woId);
}
/** 解决工单 */
public Map<String, Object> resolve(Long woId) {
int rows = jdbc.update(
"UPDATE patrol_work_order SET status = 'resolved', resolved_at = NOW() WHERE id = ?", woId);
return Map.of("success", rows > 0, "woId", woId);
}
/** 工单详情 */
public Map<String, Object> getDetail(Long woId) {
List<Map<String, Object>> rows = jdbc.queryForList(
"SELECT * FROM patrol_work_order WHERE id = ?", woId);
return rows.isEmpty() ? null : rows.get(0);
}
/** 工单列表 */
public Map<String, Object> list(String status, String severity, int page, int size) {
StringBuilder sql = new StringBuilder("SELECT * FROM patrol_work_order WHERE 1=1");
List<Object> params = new ArrayList<>();
if (status != null && !status.isEmpty()) {
sql.append(" AND status = ?");
params.add(status);
}
if (severity != null && !severity.isEmpty()) {
sql.append(" AND severity = ?");
params.add(severity);
}
sql.append(" ORDER BY created_at DESC LIMIT ? OFFSET ?");
params.add(size);
params.add((page - 1) * size);
List<Map<String, Object>> records = jdbc.queryForList(sql.toString(), params.toArray());
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM patrol_work_order WHERE 1=1");
List<Object> countParams = new ArrayList<>();
if (status != null && !status.isEmpty()) {
countSql.append(" AND status = ?");
countParams.add(status);
}
if (severity != null && !severity.isEmpty()) {
countSql.append(" AND severity = ?");
countParams.add(severity);
}
long total = countQuery(countSql.toString(), countParams.toArray());
Map<String, Object> result = new LinkedHashMap<>();
result.put("total", total);
result.put("page", page);
result.put("size", size);
result.put("records", records);
return result;
}
/** 工单统计 */
public Map<String, Object> stats() {
List<Map<String, Object>> rows = jdbc.queryForList(
"SELECT status, COUNT(*) as count FROM patrol_work_order GROUP BY status");
Map<String, Object> result = new LinkedHashMap<>();
for (Map<String, Object> row : rows) {
result.put(String.valueOf(row.get("status")), ((Number) row.get("count")).longValue());
}
return result;
}
private long countQuery(String sql, Object... args) {
Long count = jdbc.queryForObject(sql, Long.class, args);
return count != null ? count : 0;
}
}
@@ -0,0 +1,24 @@
CREATE TABLE IF NOT EXISTS pat_area (
id BIGSERIAL PRIMARY KEY, area_name VARCHAR(100), area_code VARCHAR(32) UNIQUE,
description VARCHAR(500), center_lng NUMERIC(10,6), center_lat NUMERIC(10,6),
boundary TEXT, radius DOUBLE PRECISION, status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS pat_route_setup (
id BIGSERIAL PRIMARY KEY, route_name VARCHAR(100), route_code VARCHAR(32) UNIQUE,
area_id BIGINT, area_name VARCHAR(100), checkpoint_count INT DEFAULT 0,
checkpoint_list TEXT, total_distance DOUBLE PRECISION, estimated_minutes INT,
status VARCHAR(20) DEFAULT 'active', remark VARCHAR(500),
created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS pat_form (
id BIGSERIAL PRIMARY KEY, form_name VARCHAR(100), form_code VARCHAR(32) UNIQUE,
form_type VARCHAR(30), field_config TEXT, status VARCHAR(20) DEFAULT 'active',
remark VARCHAR(500), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS pat_template (
id BIGSERIAL PRIMARY KEY, template_name VARCHAR(100), template_code VARCHAR(32) UNIQUE,
route_id BIGINT, route_name VARCHAR(100), form_id BIGINT, form_name VARCHAR(100),
schedule_type VARCHAR(20), schedule_config TEXT, status VARCHAR(20) DEFAULT 'active',
remark VARCHAR(500), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW()
);
@@ -0,0 +1,25 @@
package com.water.patrol;
import com.water.patrol.entity.*; import com.water.patrol.mapper.*; import com.water.patrol.service.*;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*; import org.mockito.junit.jupiter.MockitoExtension;
import java.math.BigDecimal; import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class PatrolSetupTest {
@Mock PatrolAreaMapper areaMapper; @Mock PatrolRouteSetupMapper routeMapper;
@Mock PatrolFormMapper formMapper; @Mock PatrolTemplateMapper tplMapper;
@InjectMocks PatrolAreaService areaSvc; @InjectMocks PatrolRouteSetupService routeSvc;
@InjectMocks PatrolFormService formSvc; @InjectMocks PatrolTemplateService tplSvc;
@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()); }
@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()); }
@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()); }
@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()); }
@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()); }
@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()); }
@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()); }
@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()); }
@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()); }
@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()); }
}