merge: 合并 feature/issue-72 到 feature/dev (阈值管理+信息发布+设备管理)

- 合并冲突解决: 保留 issue-72 的完善版本(支持 MyBatis-Plus、AND/OR 组合条件引擎、逻辑删除)
- 覆盖 feature/dev 中的早期简化版 AlertRule 相关代码
- 新增: 阈值管理 CRUD + 信息发布 + 设备管理功能
This commit is contained in:
2026-06-14 16:02:50 +08:00
274 changed files with 18370 additions and 117 deletions
@@ -0,0 +1,100 @@
package com.water.patrol.controller;
import com.water.common.core.result.R;
import com.water.patrol.service.PatrolService;
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.time.LocalDate;
import java.util.*;
@Tag(name = "巡检管理")
@RestController
@RequestMapping("/patrol")
@RequiredArgsConstructor
public class PatrolController {
private final PatrolService patrolService;
// ---- 路线 ----
@PostMapping("/route")
public R<Map<String, Object>> createRoute(@RequestBody Map<String, Object> req) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> points = (List<Map<String, Object>>) req.getOrDefault("points", List.of());
return R.ok(patrolService.createRoute(
(String) req.get("routeName"), (String) req.get("area"),
points, (int) req.getOrDefault("estimDuration", 60)));
}
@GetMapping("/route/list")
public R<List<Map<String, Object>>> routes(@RequestParam String area) {
return R.ok(patrolService.getRoutes(area));
}
// ---- 任务 ----
@PostMapping("/task")
public R<Map<String, Object>> createTask(@RequestBody Map<String, Object> req) {
return R.ok(patrolService.createTask(
Long.parseLong(String.valueOf(req.get("routeId"))),
Long.parseLong(String.valueOf(req.get("assigneeId"))),
(String) req.get("taskDate")));
}
@GetMapping("/task/today")
public R<List<Map<String, Object>>> todayTasks(@RequestParam Long userId) {
return R.ok(patrolService.getTodayTasks(userId));
}
@PutMapping("/task/{id}/start")
public R<Map<String, Object>> startTask(@PathVariable Long id) {
return R.ok(patrolService.startTask(id));
}
@PutMapping("/task/{id}/complete")
public R<Map<String, Object>> completeTask(@PathVariable Long id, @RequestParam double distance) {
return R.ok(patrolService.completeTask(id, distance));
}
// ---- 巡检记录 ----
@PostMapping("/record")
public R<Map<String, Object>> record(@RequestBody Map<String, Object> req) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> items = (List<Map<String, Object>>) req.getOrDefault("checkItems", List.of());
return R.ok(patrolService.recordCheck(
Long.parseLong(String.valueOf(req.get("taskId"))),
(int) req.get("pointSeq"),
req.get("deviceId") != null ? Long.parseLong(String.valueOf(req.get("deviceId"))) : null,
items,
((Number) req.get("lng")).doubleValue(),
((Number) req.get("lat")).doubleValue()));
}
@GetMapping("/record/list/{taskId}")
public R<List<Map<String, Object>>> records(@PathVariable Long taskId) {
return R.ok(patrolService.getTaskRecords(taskId));
}
// ---- 问题上报 ----
@PostMapping("/issue/report")
public R<Map<String, Object>> reportIssue(@RequestBody Map<String, Object> req) {
@SuppressWarnings("unchecked")
List<String> photos = (List<String>) req.getOrDefault("photoUrls", List.of());
return R.ok(patrolService.reportIssue(
Long.parseLong(String.valueOf(req.get("taskId"))),
req.get("deviceId") != null ? Long.parseLong(String.valueOf(req.get("deviceId"))) : null,
(String) req.get("issueType"), (String) req.get("description"),
photos,
((Number) req.get("lng")).doubleValue(),
((Number) req.get("lat")).doubleValue()));
}
// ---- 统计 ----
@GetMapping("/stats")
public R<Map<String, Object>> stats(@RequestParam String area,
@RequestParam String start,
@RequestParam String end) {
return R.ok(patrolService.getStats(area, LocalDate.parse(start), LocalDate.parse(end)));
}
}
@@ -0,0 +1,126 @@
package com.water.patrol.controller;
import com.water.patrol.entity.PatrolProblem;
import com.water.patrol.service.PatrolProblemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/patrol/problems")
public class PatrolProblemController {
@Autowired
private PatrolProblemService patrolProblemService;
/**
* 创建巡检问题
*/
@PostMapping
public ResponseEntity<PatrolProblem> createProblem(@RequestBody PatrolProblem patrolProblem) {
PatrolProblem created = patrolProblemService.createProblem(patrolProblem);
return ResponseEntity.ok(created);
}
/**
* 根据ID查询问题
*/
@GetMapping("/{id}")
public ResponseEntity<PatrolProblem> getProblemById(@PathVariable Long id) {
PatrolProblem problem = patrolProblemService.getProblemById(id);
if (problem != null) {
return ResponseEntity.ok(problem);
}
return ResponseEntity.notFound().build();
}
/**
* 根据问题编号查询
*/
@GetMapping("/problem-no/{problemNo}")
public ResponseEntity<PatrolProblem> getProblemByProblemNo(@PathVariable String problemNo) {
PatrolProblem problem = patrolProblemService.getProblemByProblemNo(problemNo);
if (problem != null) {
return ResponseEntity.ok(problem);
}
return ResponseEntity.notFound().build();
}
/**
* 根据任务ID查询问题列表
*/
@GetMapping("/task/{taskId}")
public ResponseEntity<List<PatrolProblem>> getProblemsByTaskId(@PathVariable Long taskId) {
List<PatrolProblem> problems = patrolProblemService.getProblemsByTaskId(taskId);
return ResponseEntity.ok(problems);
}
/**
* 根据状态查询问题列表
*/
@GetMapping("/status/{status}")
public ResponseEntity<List<PatrolProblem>> getProblemsByStatus(@PathVariable String status) {
List<PatrolProblem> problems = patrolProblemService.getProblemsByStatus(status);
return ResponseEntity.ok(problems);
}
/**
* 根据设备ID查询问题列表
*/
@GetMapping("/device/{deviceId}")
public ResponseEntity<List<PatrolProblem>> getProblemsByDeviceId(@PathVariable Long deviceId) {
List<PatrolProblem> problems = patrolProblemService.getProblemsByDeviceId(deviceId);
return ResponseEntity.ok(problems);
}
/**
* 更新问题信息
*/
@PutMapping("/{id}")
public ResponseEntity<PatrolProblem> updateProblem(@PathVariable Long id, @RequestBody PatrolProblem patrolProblem) {
patrolProblem.setId(id);
PatrolProblem updated = patrolProblemService.updateProblem(patrolProblem);
if (updated != null) {
return ResponseEntity.ok(updated);
}
return ResponseEntity.notFound().build();
}
/**
* 更新问题状态
*/
@PutMapping("/{id}/status")
public ResponseEntity<Boolean> updateProblemStatus(@PathVariable Long id, @RequestParam String status, @RequestParam(required = false) Long workOrderId) {
boolean updated = patrolProblemService.updateProblemStatus(id, status, workOrderId);
return ResponseEntity.ok(updated);
}
/**
* 删除问题
*/
@DeleteMapping("/{id}")
public ResponseEntity<Boolean> deleteProblem(@PathVariable Long id) {
boolean deleted = patrolProblemService.deleteProblem(id);
return ResponseEntity.ok(deleted);
}
/**
* 自动创建工单
*/
@PostMapping("/{id}/auto-create-work-order")
public ResponseEntity<Boolean> autoCreateWorkOrder(@PathVariable Long id) {
boolean created = patrolProblemService.autoCreateWorkOrder(id);
return ResponseEntity.ok(created);
}
/**
* 获取问题统计信息
*/
@GetMapping("/statistics")
public ResponseEntity<PatrolProblemService.ProblemStatistics> getProblemStatistics() {
PatrolProblemService.ProblemStatistics statistics = patrolProblemService.getProblemStatistics();
return ResponseEntity.ok(statistics);
}
}
@@ -0,0 +1,151 @@
package com.water.patrol.controller;
import com.water.patrol.entity.WorkOrder;
import com.water.patrol.service.WorkOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/work-orders")
public class WorkOrderController {
@Autowired
private WorkOrderService workOrderService;
/**
* 根据ID查询工单
*/
@GetMapping("/{id}")
public ResponseEntity<WorkOrder> getWorkOrderById(@PathVariable Long id) {
WorkOrder workOrder = workOrderService.getWorkOrderById(id);
if (workOrder != null) {
return ResponseEntity.ok(workOrder);
}
return ResponseEntity.notFound().build();
}
/**
* 根据工单编号查询
*/
@GetMapping("/order-no/{orderNo}")
public ResponseEntity<WorkOrder> getWorkOrderByOrderNo(@PathVariable String orderNo) {
WorkOrder workOrder = workOrderService.getWorkOrderByOrderNo(orderNo);
if (workOrder != null) {
return ResponseEntity.ok(workOrder);
}
return ResponseEntity.notFound().build();
}
/**
* 根据问题ID查询工单
*/
@GetMapping("/problem/{problemId}")
public ResponseEntity<WorkOrder> getWorkOrderByProblemId(@PathVariable Long problemId) {
WorkOrder workOrder = workOrderService.getWorkOrderByProblemId(problemId);
if (workOrder != null) {
return ResponseEntity.ok(workOrder);
}
return ResponseEntity.notFound().build();
}
/**
* 根据状态查询工单列表
*/
@GetMapping("/status/{status}")
public ResponseEntity<List<WorkOrder>> getWorkOrdersByStatus(@PathVariable String status) {
List<WorkOrder> workOrders = workOrderService.getWorkOrdersByStatus(status);
return ResponseEntity.ok(workOrders);
}
/**
* 根据处理人查询工单列表
*/
@GetMapping("/assignee/{assigneeId}")
public ResponseEntity<List<WorkOrder>> getWorkOrdersByAssigneeId(@PathVariable Long assigneeId) {
List<WorkOrder> workOrders = workOrderService.getWorkOrdersByAssigneeId(assigneeId);
return ResponseEntity.ok(workOrders);
}
/**
* 根据处理状态查询工单列表
*/
@GetMapping("/process-status/{processStatus}")
public ResponseEntity<List<WorkOrder>> getWorkOrdersByProcessStatus(@PathVariable String processStatus) {
List<WorkOrder> workOrders = workOrderService.getWorkOrdersByProcessStatus(processStatus);
return ResponseEntity.ok(workOrders);
}
/**
* 更新工单信息
*/
@PutMapping("/{id}")
public ResponseEntity<WorkOrder> updateWorkOrder(@PathVariable Long id, @RequestBody WorkOrder workOrder) {
workOrder.setId(id);
WorkOrder updated = workOrderService.updateWorkOrder(workOrder);
if (updated != null) {
return ResponseEntity.ok(updated);
}
return ResponseEntity.notFound().build();
}
/**
* 更新工单状态
*/
@PutMapping("/{id}/status")
public ResponseEntity<Boolean> updateWorkOrderStatus(@PathVariable Long id,
@RequestParam String status,
@RequestParam(required = false) String processStatus) {
boolean updated = workOrderService.updateWorkOrderStatus(id, status, processStatus);
return ResponseEntity.ok(updated);
}
/**
* 分派工单
*/
@PutMapping("/{id}/assign")
public ResponseEntity<Boolean> assignWorkOrder(@PathVariable Long id,
@RequestParam Long assigneeId,
@RequestParam String assigneeName) {
boolean assigned = workOrderService.assignWorkOrder(id, assigneeId, assigneeName);
return ResponseEntity.ok(assigned);
}
/**
* 开始处理工单
*/
@PutMapping("/{id}/start")
public ResponseEntity<Boolean> startWorkOrder(@PathVariable Long id) {
boolean started = workOrderService.startWorkOrder(id);
return ResponseEntity.ok(started);
}
/**
* 完成工单
*/
@PutMapping("/{id}/complete")
public ResponseEntity<Boolean> completeWorkOrder(@PathVariable Long id, @RequestParam String solutionResult) {
boolean completed = workOrderService.completeWorkOrder(id, solutionResult);
return ResponseEntity.ok(completed);
}
/**
* 删除工单
*/
@DeleteMapping("/{id}")
public ResponseEntity<Boolean> deleteWorkOrder(@PathVariable Long id) {
boolean deleted = workOrderService.deleteWorkOrder(id);
return ResponseEntity.ok(deleted);
}
/**
* 获取工单统计信息
*/
@GetMapping("/statistics")
public ResponseEntity<WorkOrderService.WorkOrderStatistics> getWorkOrderStatistics() {
WorkOrderService.WorkOrderStatistics statistics = workOrderService.getWorkOrderStatistics();
return ResponseEntity.ok(statistics);
}
}
@@ -0,0 +1,31 @@
package com.water.patrol.entity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Data
public class PatrolProblem {
private Long id;
private String problemNo;
private Long taskId;
private Integer pointSeq;
private Long deviceId;
private String deviceName;
private String problemType; // 设备故障/水质异常/安全隐患/环境卫生/其他
private String problemLevel; // low/normal/high/critical
private String problemTitle;
private String problemDescription;
private String location;
private Double lng;
private Double lat;
private List<String> photoUrls;
private Long reporterId;
private String reporterName;
private LocalDateTime reportTime;
private String status; // reported/processing/completed/closed
private Long workOrderId;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@@ -0,0 +1,37 @@
package com.water.patrol.entity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Data
public class WorkOrder {
private Long id;
private String orderNo;
private Long problemId;
private String orderType; // 设备维修/水质处理/安全隐患处理/清洁/其他
private String priority; // low/normal/high/critical
private String title;
private String description;
private String location;
private String contactPerson;
private String contactPhone;
private Long reporterId;
private String reporterName;
private Long assigneeId;
private String assigneeName;
private String status; // pending/assigned/processing/completed/cancelled
private String processStatus; // created/accepted/in_progress/completed
private Integer estimatedDuration;
private LocalDateTime actualStartTime;
private LocalDateTime actualEndTime;
private LocalDateTime completionTime;
private List<String> photosBefore;
private List<String> photosAfter;
private String solutionDescription;
private String solutionResult;
private String customerFeedback;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@@ -0,0 +1,18 @@
package com.water.patrol.entity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class WorkOrderProcess {
private Long id;
private Long workOrderId;
private String processStep; // created/accepted/in_progress/completed
private Long processorId;
private String processorName;
private String action; // create/assign/start/complete/cancel
private String comment;
private List<String> photos;
private LocalDateTime createdAt;
}
@@ -0,0 +1,47 @@
package com.water.patrol.mapper;
import com.water.patrol.entity.PatrolProblem;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
public interface PatrolProblemMapper {
// 插入巡检问题
int insert(PatrolProblem patrolProblem);
// 根据ID查询
PatrolProblem selectById(Long id);
// 根据问题编号查询
PatrolProblem selectByProblemNo(String problemNo);
// 根据任务ID查询问题列表
List<PatrolProblem> selectByTaskId(Long taskId);
// 根据状态查询问题列表
List<PatrolProblem> selectByStatus(String status);
// 根据设备ID查询问题列表
List<PatrolProblem> selectByDeviceId(Long deviceId);
// 更新问题信息
int update(PatrolProblem patrolProblem);
// 更新问题状态
int updateStatus(@Param("id") Long id, @Param("status") String status, @Param("workOrderId") Long workOrderId);
// 删除问题
int deleteById(Long id);
// 生成问题编号
String generateProblemNo();
// 获取问题总数
int countAll();
// 获取指定状态的问题数
int countByStatus(String status);
}
@@ -0,0 +1,60 @@
package com.water.patrol.mapper;
import com.water.patrol.entity.WorkOrder;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
public interface WorkOrderMapper {
// 插入工单
int insert(WorkOrder workOrder);
// 根据ID查询
WorkOrder selectById(Long id);
// 根据工单编号查询
WorkOrder selectByOrderNo(String orderNo);
// 根据问题ID查询工单
WorkOrder selectByProblemId(Long problemId);
// 根据状态查询工单列表
List<WorkOrder> selectByStatus(String status);
// 根据处理人查询工单列表
List<WorkOrder> selectByAssigneeId(Long assigneeId);
// 根据处理状态查询工单列表
List<WorkOrder> selectByProcessStatus(String processStatus);
// 更新工单信息
int update(WorkOrder workOrder);
// 更新工单状态
int updateStatus(@Param("id") Long id, @Param("status") String status, @Param("processStatus") String processStatus);
// 更新处理人
int updateAssignee(@Param("id") Long id, @Param("assigneeId") Long assigneeId, @Param("assigneeName") String assigneeName);
// 更新完成信息
int updateCompletion(@Param("id") Long id, @Param("actualEndTime") LocalDateTime actualEndTime,
@Param("completionTime") LocalDateTime completionTime, @Param("solutionResult") String solutionResult);
// 删除工单
int deleteById(Long id);
// 生成工单编号
String generateOrderNo();
// 获取工单总数
int countAll();
// 获取指定状态的工单数
int countByStatus(String status);
// 获取指定处理状态的工单数
int countByProcessStatus(String processStatus);
}
@@ -0,0 +1,25 @@
package com.water.patrol.mapper;
import com.water.patrol.entity.WorkOrderProcess;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface WorkOrderProcessMapper {
// 插入处理记录
int insert(WorkOrderProcess workOrderProcess);
// 根据工单ID查询处理记录列表
List<WorkOrderProcess> selectByWorkOrderId(Long workOrderId);
// 根据ID查询
WorkOrderProcess selectById(Long id);
// 获取最新的处理记录
WorkOrderProcess selectLatestByWorkOrderId(Long workOrderId);
// 删除处理记录
int deleteById(Long id);
}
@@ -0,0 +1,82 @@
package com.water.patrol.service;
import com.water.patrol.entity.PatrolProblem;
import java.util.List;
public interface PatrolProblemService {
/**
* 创建巡检问题
*/
PatrolProblem createProblem(PatrolProblem patrolProblem);
/**
* 根据ID查询问题
*/
PatrolProblem getProblemById(Long id);
/**
* 根据问题编号查询
*/
PatrolProblem getProblemByProblemNo(String problemNo);
/**
* 根据任务ID查询问题列表
*/
List<PatrolProblem> getProblemsByTaskId(Long taskId);
/**
* 根据状态查询问题列表
*/
List<PatrolProblem> getProblemsByStatus(String status);
/**
* 根据设备ID查询问题列表
*/
List<PatrolProblem> getProblemsByDeviceId(Long deviceId);
/**
* 更新问题信息
*/
PatrolProblem updateProblem(PatrolProblem patrolProblem);
/**
* 更新问题状态
*/
boolean updateProblemStatus(Long id, String status, Long workOrderId);
/**
* 删除问题
*/
boolean deleteProblem(Long id);
/**
* 自动创建工单
*/
boolean autoCreateWorkOrder(Long problemId);
/**
* 获取问题统计信息
*/
ProblemStatistics getProblemStatistics();
class ProblemStatistics {
private int totalProblems;
private int reportedCount;
private int processingCount;
private int completedCount;
private int closedCount;
// Getters and setters
public int getTotalProblems() { return totalProblems; }
public void setTotalProblems(int totalProblems) { this.totalProblems = totalProblems; }
public int getReportedCount() { return reportedCount; }
public void setReportedCount(int reportedCount) { this.reportedCount = reportedCount; }
public int getProcessingCount() { return processingCount; }
public void setProcessingCount(int processingCount) { this.processingCount = processingCount; }
public int getCompletedCount() { return completedCount; }
public void setCompletedCount(int completedCount) { this.completedCount = completedCount; }
public int getClosedCount() { return closedCount; }
public void setClosedCount(int closedCount) { this.closedCount = closedCount; }
}
}
@@ -0,0 +1,118 @@
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.util.*;
@Slf4j
@Service
@RequiredArgsConstructor
public class PatrolService {
private final JdbcTemplate jdbc;
// ========== 路线管理 ==========
public Map<String, Object> createRoute(String routeName, String area, List<Map<String, Object>> points, int estimDuration) {
jdbc.update("INSERT INTO patrol_route (route_name, area, route_points, estim_duration) VALUES (?,?,?::jsonb,?)",
routeName, area, points.toString(), estimDuration);
return Map.of("routeName", routeName, "area", area, "points", points.size());
}
public List<Map<String, Object>> getRoutes(String area) {
return jdbc.queryForList("SELECT * FROM patrol_route WHERE area = ? AND status = 1", area);
}
// ========== 任务管理 ==========
public Map<String, Object> createTask(Long routeId, Long assigneeId, String taskDate) {
jdbc.update(
"INSERT INTO patrol_task (route_id, assignee_id, task_name, task_date, plan_start, plan_end, status) " +
"SELECT ?, ?, route_name, ?, CAST(? AS TIMESTAMP), CAST(? AS TIMESTAMP) + (estim_duration || ' minutes')::INTERVAL, 'pending' " +
"FROM patrol_route WHERE id = ?",
routeId, assigneeId, taskDate, taskDate + " 09:00:00", taskDate + " 09:00:00", routeId);
return Map.of("routeId", routeId, "assigneeId", assigneeId, "date", taskDate, "status", "created");
}
public List<Map<String, Object>> getTodayTasks(Long userId) {
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 AND pt.assignee_id = ? " +
"ORDER BY pt.plan_start", userId);
}
public Map<String, Object> startTask(Long taskId) {
jdbc.update("UPDATE patrol_task SET status = 'in_progress', actual_start = NOW() WHERE id = ?", taskId);
return Map.of("taskId", taskId, "status", "in_progress", "startedAt", new Date());
}
public Map<String, Object> completeTask(Long taskId, double distance) {
jdbc.update(
"UPDATE patrol_task SET status = 'completed', actual_end = NOW(), distance = ? WHERE id = ?",
distance, taskId);
return Map.of("taskId", taskId, "status", "completed", "distance", distance);
}
// ========== 巡检记录 ==========
public Map<String, Object> recordCheck(Long taskId, int pointSeq, Long deviceId,
List<Map<String, Object>> checkItems,
double lng, double lat) {
jdbc.update(
"INSERT INTO patrol_record (task_id, point_seq, device_id, check_items, gps_lng, gps_lat, record_time) " +
"VALUES (?,?,?,?::jsonb,?,?,NOW())",
taskId, pointSeq, deviceId, checkItems.toString(), lng, lat);
return Map.of("taskId", taskId, "pointSeq", pointSeq, "recorded", true);
}
public List<Map<String, Object>> getTaskRecords(Long taskId) {
return jdbc.queryForList(
"SELECT * FROM patrol_record WHERE task_id = ? ORDER BY point_seq", taskId);
}
// ========== 问题上报(巡检APP) ==========
public Map<String, Object> reportIssue(Long taskId, Long deviceId, String issueType,
String description, List<String> photoUrls,
double lng, double lat) {
// 自动创建工单
jdbc.update(
"INSERT INTO patrol_task (task_name, assignee_id, task_date, status) " +
"SELECT CONCAT('问题处理: ', ?), assignee_id, CURRENT_DATE, 'pending' FROM patrol_task WHERE id = ?",
issueType + ": " + description.substring(0, Math.min(description.length(), 50)), taskId);
log.info("Issue reported: type={} desc={}", issueType, description);
return Map.of("reported", true, "issueType", issueType, "photos", photoUrls);
}
// ========== 统计分析 ==========
public Map<String, Object> getStats(String area, LocalDate start, LocalDate end) {
Map<String, Object> stats = new LinkedHashMap<>();
// 任务执行率
stats.put("completionRate", jdbc.queryForMap(
"SELECT COUNT(*) as total, SUM(CASE WHEN status='completed' THEN 1 ELSE 0 END) as completed " +
"FROM patrol_task WHERE task_date BETWEEN ? AND ?", start, end));
// 人员里程
stats.put("personDistance", jdbc.queryForList(
"SELECT u.real_name, SUM(pt.distance) as total_km " +
"FROM patrol_task pt JOIN sys_user u ON pt.assignee_id = u.id " +
"WHERE pt.task_date BETWEEN ? AND ? GROUP BY u.id, u.real_name", start, end));
// 巡检工作量
stats.put("workload", jdbc.queryForList(
"SELECT task_date, COUNT(*) as tasks, SUM(distance) as total_km " +
"FROM patrol_task WHERE task_date BETWEEN ? AND ? GROUP BY task_date ORDER BY task_date",
start, end));
// 问题分类统计
stats.put("issueStats", jdbc.queryForList(
"SELECT SUBSTRING(task_name FROM '^[^:]+') as issue_type, COUNT(*) as count " +
"FROM patrol_task WHERE task_name LIKE '%问题处理:%' AND task_date BETWEEN ? AND ? GROUP BY 1",
start, end));
return stats;
}
}
@@ -0,0 +1,100 @@
package com.water.patrol.service;
import com.water.patrol.entity.WorkOrder;
import java.util.List;
public interface WorkOrderService {
/**
* 从巡检问题创建工单
*/
boolean createFromProblem(PatrolProblem problem);
/**
* 根据ID查询工单
*/
WorkOrder getWorkOrderById(Long id);
/**
* 根据工单编号查询
*/
WorkOrder getWorkOrderByOrderNo(String orderNo);
/**
* 根据问题ID查询工单
*/
WorkOrder getWorkOrderByProblemId(Long problemId);
/**
* 根据状态查询工单列表
*/
List<WorkOrder> getWorkOrdersByStatus(String status);
/**
* 根据处理人查询工单列表
*/
List<WorkOrder> getWorkOrdersByAssigneeId(Long assigneeId);
/**
* 根据处理状态查询工单列表
*/
List<WorkOrder> getWorkOrdersByProcessStatus(String processStatus);
/**
* 更新工单信息
*/
WorkOrder updateWorkOrder(WorkOrder workOrder);
/**
* 更新工单状态
*/
boolean updateWorkOrderStatus(Long id, String status, String processStatus);
/**
* 分派工单
*/
boolean assignWorkOrder(Long id, Long assigneeId, String assigneeName);
/**
* 开始处理工单
*/
boolean startWorkOrder(Long id);
/**
* 完成工单
*/
boolean completeWorkOrder(Long id, String solutionResult);
/**
* 删除工单
*/
boolean deleteWorkOrder(Long id);
/**
* 获取工单统计信息
*/
WorkOrderStatistics getWorkOrderStatistics();
class WorkOrderStatistics {
private int totalOrders;
private int pendingCount;
private int assignedCount;
private int processingCount;
private int completedCount;
private int cancelledCount;
// Getters and setters
public int getTotalOrders() { return totalOrders; }
public void setTotalOrders(int totalOrders) { this.totalOrders = totalOrders; }
public int getPendingCount() { return pendingCount; }
public void setPendingCount(int pendingCount) { this.pendingCount = pendingCount; }
public int getAssignedCount() { return assignedCount; }
public void setAssignedCount(int assignedCount) { this.assignedCount = assignedCount; }
public int getProcessingCount() { return processingCount; }
public void setProcessingCount(int processingCount) { this.processingCount = processingCount; }
public int getCompletedCount() { return completedCount; }
public void setCompletedCount(int completedCount) { this.completedCount = completedCount; }
public int getCancelledCount() { return cancelledCount; }
public void setCancelledCount(int cancelledCount) { this.cancelledCount = cancelledCount; }
}
}
@@ -0,0 +1,109 @@
package com.water.patrol.service.impl;
import com.water.patrol.entity.PatrolProblem;
import com.water.patrol.mapper.PatrolProblemMapper;
import com.water.patrol.service.PatrolProblemService;
import com.water.patrol.service.WorkOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class PatrolProblemServiceImpl implements PatrolProblemService {
@Autowired
private PatrolProblemMapper patrolProblemMapper;
@Autowired
private WorkOrderService workOrderService;
@Override
@Transactional
public PatrolProblem createProblem(PatrolProblem patrolProblem) {
// 生成问题编号
String problemNo = patrolProblemMapper.generateProblemNo();
patrolProblem.setProblemNo(problemNo);
patrolProblem.setReportTime(LocalDateTime.now());
patrolProblem.setStatus("reported");
patrolProblem.setCreatedAt(LocalDateTime.now());
patrolProblem.setUpdatedAt(LocalDateTime.now());
patrolProblemMapper.insert(patrolProblem);
// 自动创建工单
autoCreateWorkOrder(patrolProblem.getId());
return patrolProblem;
}
@Override
public PatrolProblem getProblemById(Long id) {
return patrolProblemMapper.selectById(id);
}
@Override
public PatrolProblem getProblemByProblemNo(String problemNo) {
return patrolProblemMapper.selectByProblemNo(problemNo);
}
@Override
public List<PatrolProblem> getProblemsByTaskId(Long taskId) {
return patrolProblemMapper.selectByTaskId(taskId);
}
@Override
public List<PatrolProblem> getProblemsByStatus(String status) {
return patrolProblemMapper.selectByStatus(status);
}
@Override
public List<PatrolProblem> getProblemsByDeviceId(Long deviceId) {
return patrolProblemMapper.selectByDeviceId(deviceId);
}
@Override
@Transactional
public PatrolProblem updateProblem(PatrolProblem patrolProblem) {
patrolProblem.setUpdatedAt(LocalDateTime.now());
patrolProblemMapper.update(patrolProblem);
return patrolProblem;
}
@Override
@Transactional
public boolean updateProblemStatus(Long id, String status, Long workOrderId) {
LocalDateTime now = LocalDateTime.now();
return patrolProblemMapper.updateStatus(id, status, workOrderId) > 0;
}
@Override
@Transactional
public boolean deleteProblem(Long id) {
return patrolProblemMapper.deleteById(id) > 0;
}
@Override
@Transactional
public boolean autoCreateWorkOrder(Long problemId) {
PatrolProblem problem = getProblemById(problemId);
if (problem == null) {
return false;
}
return workOrderService.createFromProblem(problem);
}
@Override
public ProblemStatistics getProblemStatistics() {
ProblemStatistics statistics = new ProblemStatistics();
statistics.setTotalProblems(patrolProblemMapper.countAll());
statistics.setReportedCount(patrolProblemMapper.countByStatus("reported"));
statistics.setProcessingCount(patrolProblemMapper.countByStatus("processing"));
statistics.setCompletedCount(patrolProblemMapper.countByStatus("completed"));
statistics.setClosedCount(patrolProblemMapper.countByStatus("closed"));
return statistics;
}
}
@@ -0,0 +1,241 @@
package com.water.patrol.service.impl;
import com.water.patrol.entity.PatrolProblem;
import com.water.patrol.entity.WorkOrder;
import com.water.patrol.entity.WorkOrderProcess;
import com.water.patrol.mapper.WorkOrderMapper;
import com.water.patrol.mapper.WorkOrderProcessMapper;
import com.water.patrol.service.WorkOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class WorkOrderServiceImpl implements WorkOrderService {
@Autowired
private WorkOrderMapper workOrderMapper;
@Autowired
private WorkOrderProcessMapper workOrderProcessMapper;
@Override
@Transactional
public boolean createFromProblem(PatrolProblem problem) {
WorkOrder workOrder = new WorkOrder();
workOrder.setOrderNo(workOrderMapper.generateOrderNo());
workOrder.setProblemId(problem.getId());
workOrder.setOrderType(determineOrderType(problem.getProblemType()));
workOrder.setPriority(determinePriority(problem.getProblemLevel()));
workOrder.setTitle(problem.getProblemTitle());
workOrder.setDescription(problem.getProblemDescription());
workOrder.setLocation(problem.getLocation());
workOrder.setReporterId(problem.getReporterId());
workOrder.setReporterName(problem.getReporterName());
workOrder.setStatus("pending");
workOrder.setProcessStatus("created");
workOrder.setEstimatedDuration(30); // 默认30分钟
workOrder.setCreatedAt(LocalDateTime.now());
workOrder.setUpdatedAt(LocalDateTime.now());
int result = workOrderMapper.insert(workOrder);
if (result > 0) {
// 创建处理记录
WorkOrderProcess process = new WorkOrderProcess();
process.setWorkOrderId(workOrder.getId());
process.setProcessStep("created");
process.setProcessorId(problem.getReporterId());
process.setProcessorName(problem.getReporterName());
process.setAction("create");
process.setComment("工单自动创建");
process.setCreatedAt(LocalDateTime.now());
workOrderProcessMapper.insert(process);
// 更新问题状态
workOrderMapper.selectByProblemId(problem.getId()).setWorkOrderId(workOrder.getId());
return true;
}
return false;
}
@Override
public WorkOrder getWorkOrderById(Long id) {
return workOrderMapper.selectById(id);
}
@Override
public WorkOrder getWorkOrderByOrderNo(String orderNo) {
return workOrderMapper.selectByOrderNo(orderNo);
}
@Override
public WorkOrder getWorkOrderByProblemId(Long problemId) {
return workOrderMapper.selectByProblemId(problemId);
}
@Override
public List<WorkOrder> getWorkOrdersByStatus(String status) {
return workOrderMapper.selectByStatus(status);
}
@Override
public List<WorkOrder> getWorkOrdersByAssigneeId(Long assigneeId) {
return workOrderMapper.selectByAssigneeId(assigneeId);
}
@Override
public List<WorkOrder> getWorkOrdersByProcessStatus(String processStatus) {
return workOrderMapper.selectByProcessStatus(processStatus);
}
@Override
@Transactional
public WorkOrder updateWorkOrder(WorkOrder workOrder) {
workOrder.setUpdatedAt(LocalDateTime.now());
workOrderMapper.update(workOrder);
return workOrder;
}
@Override
@Transactional
public boolean updateWorkOrderStatus(Long id, String status, String processStatus) {
LocalDateTime now = LocalDateTime.now();
return workOrderMapper.updateStatus(id, status, processStatus) > 0;
}
@Override
@Transactional
public boolean assignWorkOrder(Long id, Long assigneeId, String assigneeName) {
LocalDateTime now = LocalDateTime.now();
// 更新工单
boolean updated = workOrderMapper.updateAssignee(id, assigneeId, assigneeName) > 0;
if (updated) {
// 创建处理记录
WorkOrder workOrder = getWorkOrderById(id);
WorkOrderProcess process = new WorkOrderProcess();
process.setWorkOrderId(id);
process.setProcessStep("accepted");
process.setProcessorId(assigneeId);
process.setProcessorName(assigneeName);
process.setAction("assign");
process.setComment("工单已分配");
process.setCreatedAt(now);
workOrderProcessMapper.insert(process);
// 更新工单状态
updateWorkOrderStatus(id, "assigned", "accepted");
}
return updated;
}
@Override
@Transactional
public boolean startWorkOrder(Long id) {
LocalDateTime now = LocalDateTime.now();
// 更新工单
boolean updated = workOrderMapper.updateStatus(id, "processing", "in_progress") > 0;
if (updated) {
// 创建处理记录
WorkOrder workOrder = getWorkOrderById(id);
WorkOrderProcess process = new WorkOrderProcess();
process.setWorkOrderId(id);
process.setProcessStep("in_progress");
process.setProcessorId(workOrder.getAssigneeId());
process.setProcessorName(workOrder.getAssigneeName());
process.setAction("start");
process.setComment("开始处理工单");
process.setCreatedAt(now);
workOrderProcessMapper.insert(process);
}
return updated;
}
@Override
@Transactional
public boolean completeWorkOrder(Long id, String solutionResult) {
LocalDateTime now = LocalDateTime.now();
// 更新工单
boolean updated = workOrderMapper.updateCompletion(id, now, now, solutionResult) > 0;
if (updated) {
// 创建处理记录
WorkOrder workOrder = getWorkOrderById(id);
WorkOrderProcess process = new WorkOrderProcess();
process.setWorkOrderId(id);
process.setProcessStep("completed");
process.setProcessorId(workOrder.getAssigneeId());
process.setProcessorName(workOrder.getAssigneeName());
process.setAction("complete");
process.setComment("工单已完成");
process.setCreatedAt(now);
workOrderProcessMapper.insert(process);
// 更新工单状态
updateWorkOrderStatus(id, "completed", "completed");
}
return updated;
}
@Override
@Transactional
public boolean deleteWorkOrder(Long id) {
return workOrderMapper.deleteById(id) > 0;
}
@Override
public WorkOrderStatistics getWorkOrderStatistics() {
WorkOrderStatistics statistics = new WorkOrderStatistics();
statistics.setTotalOrders(workOrderMapper.countAll());
statistics.setPendingCount(workOrderMapper.countByStatus("pending"));
statistics.setAssignedCount(workOrderMapper.countByStatus("assigned"));
statistics.setProcessingCount(workOrderMapper.countByStatus("processing"));
statistics.setCompletedCount(workOrderMapper.countByStatus("completed"));
statistics.setCancelledCount(workOrderMapper.countByStatus("cancelled"));
return statistics;
}
/**
* 根据问题类型确定工单类型
*/
private String determineOrderType(String problemType) {
if (problemType.contains("设备")) {
return "设备维修";
} else if (problemType.contains("水质")) {
return "水质处理";
} else if (problemType.contains("安全")) {
return "安全隐患处理";
} else if (problemType.contains("环境")) {
return "清洁";
} else {
return "其他";
}
}
/**
* 根据问题级别确定优先级
*/
private String determinePriority(String problemLevel) {
if (problemLevel.equals("critical")) {
return "critical";
} else if (problemLevel.equals("high")) {
return "high";
} else if (problemLevel.equals("normal")) {
return "normal";
} else {
return "low";
}
}
}