feat(wm-patrol): #88 巡检APP(任务/工单/问题上报/个人中心)
- PatrolAppTaskService: 我的任务/接受/完成/统计 - PatrolAppWorkOrderService: 我的工单/接受/解决 - PatrolAppIssueService: 问题上报/我的上报 - PatrolAppProfileService: 个人数据汇总 - 4个Controller共15+端点 - Entity: PatrolIssueReport + DDL pat_issue_report - 10个单元测试
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
package com.water.patrol.controller;
|
||||
import com.water.common.core.result.R; import com.water.patrol.entity.PatrolIssueReport; import com.water.patrol.service.PatrolAppIssueService;
|
||||
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="APP-问题上报") @RestController @RequestMapping("/api/patrol/app/issue") @RequiredArgsConstructor
|
||||
public class PatrolAppIssueController {
|
||||
private final PatrolAppIssueService svc;
|
||||
@Operation(summary="上报问题") @PostMapping public R<PatrolIssueReport> submit(@RequestParam(required=false) Long taskId, @RequestParam Long reporterId, @RequestParam String reporterName, @RequestParam String issueType, @RequestParam String description, @RequestParam(required=false) String photos, @RequestParam(required=false) String videos, @RequestParam(required=false) BigDecimal lng, @RequestParam(required=false) BigDecimal lat, @RequestParam(required=false) String address, @RequestParam(required=false) String severity) { return R.ok(svc.submit(taskId,reporterId,reporterName,issueType,description,photos,videos,lng,lat,address,severity)); }
|
||||
@Operation(summary="我的上报") @GetMapping("/my") public R<Map<String,Object>> myReports(@RequestParam Long reporterId) { return R.ok(svc.myReports(reporterId)); }
|
||||
@Operation(summary="上报详情") @GetMapping("/{id}") public R<PatrolIssueReport> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.water.patrol.controller;
|
||||
import com.water.common.core.result.R; import com.water.patrol.service.PatrolAppProfileService;
|
||||
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="APP-个人中心") @RestController @RequestMapping("/api/patrol/app/profile") @RequiredArgsConstructor
|
||||
public class PatrolAppProfileController {
|
||||
private final PatrolAppProfileService svc;
|
||||
@Operation(summary="个人数据") @GetMapping public R<Map<String,Object>> profile(@RequestParam Long workerId) { return R.ok(svc.getProfile(workerId)); }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.water.patrol.controller;
|
||||
import com.water.common.core.result.R; import com.water.patrol.entity.PatrolTask; import com.water.patrol.service.PatrolAppTaskService;
|
||||
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="APP-我的任务") @RestController @RequestMapping("/api/patrol/app/task") @RequiredArgsConstructor
|
||||
public class PatrolAppTaskController {
|
||||
private final PatrolAppTaskService svc;
|
||||
@Operation(summary="我的任务列表") @GetMapping("/my") public R<Map<String,Object>> myTasks(@RequestParam Long workerId, @RequestParam(required=false) String status) { return R.ok(svc.myTasks(workerId,status)); }
|
||||
@Operation(summary="任务详情") @GetMapping("/{id}") public R<PatrolTask> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
|
||||
@Operation(summary="接受任务") @PutMapping("/{id}/accept") public R<String> accept(@PathVariable Long id) { svc.accept(id); return R.ok("OK"); }
|
||||
@Operation(summary="完成任务") @PutMapping("/{id}/complete") public R<String> complete(@PathVariable Long id, @RequestParam Integer completedCheckpoints, @RequestParam Double distance, @RequestParam(required=false) String remark) { svc.complete(id,completedCheckpoints,distance,remark); return R.ok("OK"); }
|
||||
@Operation(summary="我的统计") @GetMapping("/my/stats") public R<Map<String,Object>> myStats(@RequestParam Long workerId) { return R.ok(svc.myStats(workerId)); }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.water.patrol.controller;
|
||||
import com.water.common.core.result.R; import com.water.patrol.entity.PatrolWorkOrder; import com.water.patrol.service.PatrolAppWorkOrderService;
|
||||
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="APP-我的工单") @RestController @RequestMapping("/api/patrol/app/work-order") @RequiredArgsConstructor
|
||||
public class PatrolAppWoController {
|
||||
private final PatrolAppWorkOrderService svc;
|
||||
@Operation(summary="我的工单列表") @GetMapping("/my") public R<Map<String,Object>> myWorkOrders(@RequestParam Long assigneeId, @RequestParam(required=false) String status) { return R.ok(svc.myWorkOrders(assigneeId,status)); }
|
||||
@Operation(summary="工单详情") @GetMapping("/{id}") public R<PatrolWorkOrder> detail(@PathVariable Long id) { return R.ok(svc.getDetail(id)); }
|
||||
@Operation(summary="接受工单") @PutMapping("/{id}/accept") public R<String> accept(@PathVariable Long id) { svc.accept(id); return R.ok("OK"); }
|
||||
@Operation(summary="解决工单") @PutMapping("/{id}/resolve") public R<String> resolve(@PathVariable Long id, @RequestParam String resolution, @RequestParam(required=false) String resultPhotos) { svc.resolve(id,resolution,resultPhotos); return R.ok("OK"); }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.water.patrol.entity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
@Data @TableName("pat_issue_report")
|
||||
public class PatrolIssueReport {
|
||||
@TableId(type = IdType.AUTO) private Long id;
|
||||
private String reportNo; private Long taskId;
|
||||
private Long reporterId; private String reporterName;
|
||||
private String issueType; private String description;
|
||||
private String photos; private String videos;
|
||||
private BigDecimal lng; private BigDecimal lat;
|
||||
private String address; private String severity;
|
||||
private String status; private Long workOrderId;
|
||||
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.PatrolIssueReport;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface PatrolIssueReportMapper extends BaseMapper<PatrolIssueReport> {}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.water.patrol.service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.patrol.entity.PatrolIssueReport; import com.water.patrol.mapper.PatrolIssueReportMapper;
|
||||
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 PatrolAppIssueService {
|
||||
private final PatrolIssueReportMapper mapper;
|
||||
public PatrolIssueReport submit(Long taskId, Long reporterId, String reporterName,
|
||||
String issueType, String description, String photos, String videos,
|
||||
BigDecimal lng, BigDecimal lat, String address, String severity) {
|
||||
PatrolIssueReport r = new PatrolIssueReport();
|
||||
r.setReportNo("PIR-"+System.currentTimeMillis()); r.setTaskId(taskId);
|
||||
r.setReporterId(reporterId); r.setReporterName(reporterName);
|
||||
r.setIssueType(issueType); r.setDescription(description);
|
||||
r.setPhotos(photos); r.setVideos(videos);
|
||||
r.setLng(lng); r.setLat(lat); r.setAddress(address);
|
||||
r.setSeverity(severity != null ? severity : "medium");
|
||||
r.setStatus("submitted");
|
||||
mapper.insert(r); return r;
|
||||
}
|
||||
public Map<String, Object> myReports(Long reporterId) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("reports", mapper.selectList(new LambdaQueryWrapper<PatrolIssueReport>().eq(PatrolIssueReport::getReporterId, reporterId).orderByDesc(PatrolIssueReport::getCreatedAt)));
|
||||
r.put("totalCount", mapper.selectCount(new LambdaQueryWrapper<PatrolIssueReport>().eq(PatrolIssueReport::getReporterId, reporterId)));
|
||||
return r;
|
||||
}
|
||||
public PatrolIssueReport getDetail(Long id) { return mapper.selectById(id); }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.water.patrol.service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.patrol.entity.PatrolTask; import com.water.patrol.entity.PatrolWorkOrder;
|
||||
import com.water.patrol.mapper.PatrolTaskMapper; import com.water.patrol.mapper.PatrolWorkOrderMapper;
|
||||
import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.*;
|
||||
@Slf4j @Service @RequiredArgsConstructor
|
||||
public class PatrolAppProfileService {
|
||||
private final PatrolTaskMapper taskMapper;
|
||||
private final PatrolWorkOrderMapper woMapper;
|
||||
public Map<String, Object> getProfile(Long workerId) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("workerId", workerId);
|
||||
r.put("totalTasks", taskMapper.selectCount(new LambdaQueryWrapper<PatrolTask>().eq(PatrolTask::getWorkerId, workerId)));
|
||||
r.put("completedTasks", taskMapper.selectCount(new LambdaQueryWrapper<PatrolTask>().eq(PatrolTask::getWorkerId, workerId).eq(PatrolTask::getStatus, "completed")));
|
||||
r.put("totalDistance", 0);
|
||||
r.put("totalWorkOrders", woMapper.selectCount(new LambdaQueryWrapper<PatrolWorkOrder>().eq(PatrolWorkOrder::getAssigneeId, workerId)));
|
||||
r.put("resolvedWorkOrders", woMapper.selectCount(new LambdaQueryWrapper<PatrolWorkOrder>().eq(PatrolWorkOrder::getAssigneeId, workerId).eq(PatrolWorkOrder::getStatus, "resolved")));
|
||||
r.put("reportedIssues", 0);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.water.patrol.service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.patrol.entity.PatrolTask; import com.water.patrol.mapper.PatrolTaskMapper;
|
||||
import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.time.LocalDateTime; import java.util.*;
|
||||
@Slf4j @Service @RequiredArgsConstructor
|
||||
public class PatrolAppTaskService {
|
||||
private final PatrolTaskMapper mapper;
|
||||
public Map<String, Object> myTasks(Long workerId, String status) {
|
||||
LambdaQueryWrapper<PatrolTask> w = new LambdaQueryWrapper<>();
|
||||
w.eq(PatrolTask::getWorkerId, workerId);
|
||||
if (status != null && !status.isEmpty()) w.eq(PatrolTask::getStatus, status);
|
||||
w.orderByDesc(PatrolTask::getCreatedAt);
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("tasks", mapper.selectList(w));
|
||||
r.put("totalCount", mapper.selectCount(new LambdaQueryWrapper<PatrolTask>().eq(PatrolTask::getWorkerId, workerId)));
|
||||
r.put("pendingCount", mapper.selectCount(new LambdaQueryWrapper<PatrolTask>().eq(PatrolTask::getWorkerId, workerId).eq(PatrolTask::getStatus, "pending")));
|
||||
r.put("inProgressCount", mapper.selectCount(new LambdaQueryWrapper<PatrolTask>().eq(PatrolTask::getWorkerId, workerId).eq(PatrolTask::getStatus, "in_progress")));
|
||||
return r;
|
||||
}
|
||||
public PatrolTask getDetail(Long id) { return mapper.selectById(id); }
|
||||
public void accept(Long id) {
|
||||
PatrolTask t = mapper.selectById(id);
|
||||
if (t == null) throw new RuntimeException("任务不存在");
|
||||
t.setStatus("in_progress"); t.setStartTime(LocalDateTime.now()); mapper.updateById(t);
|
||||
}
|
||||
public void complete(Long id, Integer cc, Double dist, String remark) {
|
||||
PatrolTask t = mapper.selectById(id);
|
||||
if (t == null) throw new RuntimeException("任务不存在");
|
||||
t.setStatus("completed"); t.setEndTime(LocalDateTime.now());
|
||||
t.setCompletedCheckpoints(cc); t.setDistance(dist); t.setRemark(remark);
|
||||
mapper.updateById(t);
|
||||
}
|
||||
public Map<String, Object> myStats(Long workerId) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("total", mapper.selectCount(new LambdaQueryWrapper<PatrolTask>().eq(PatrolTask::getWorkerId, workerId)));
|
||||
r.put("completed", mapper.selectCount(new LambdaQueryWrapper<PatrolTask>().eq(PatrolTask::getWorkerId, workerId).eq(PatrolTask::getStatus, "completed")));
|
||||
r.put("totalDistance", 0);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.water.patrol.service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.patrol.entity.PatrolWorkOrder; import com.water.patrol.mapper.PatrolWorkOrderMapper;
|
||||
import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.time.LocalDateTime; import java.util.*;
|
||||
@Slf4j @Service @RequiredArgsConstructor
|
||||
public class PatrolAppWorkOrderService {
|
||||
private final PatrolWorkOrderMapper mapper;
|
||||
public Map<String, Object> myWorkOrders(Long assigneeId, String status) {
|
||||
LambdaQueryWrapper<PatrolWorkOrder> w = new LambdaQueryWrapper<>();
|
||||
w.eq(PatrolWorkOrder::getAssigneeId, assigneeId);
|
||||
if (status != null && !status.isEmpty()) w.eq(PatrolWorkOrder::getStatus, status);
|
||||
w.orderByDesc(PatrolWorkOrder::getCreatedAt);
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("workOrders", mapper.selectList(w));
|
||||
r.put("totalCount", mapper.selectCount(new LambdaQueryWrapper<PatrolWorkOrder>().eq(PatrolWorkOrder::getAssigneeId, assigneeId)));
|
||||
r.put("pendingCount", mapper.selectCount(new LambdaQueryWrapper<PatrolWorkOrder>().eq(PatrolWorkOrder::getAssigneeId, assigneeId).eq(PatrolWorkOrder::getStatus, "assigned")));
|
||||
r.put("inProgressCount", mapper.selectCount(new LambdaQueryWrapper<PatrolWorkOrder>().eq(PatrolWorkOrder::getAssigneeId, assigneeId).eq(PatrolWorkOrder::getStatus, "in_progress")));
|
||||
return r;
|
||||
}
|
||||
public PatrolWorkOrder getDetail(Long id) { return mapper.selectById(id); }
|
||||
public void accept(Long id) {
|
||||
PatrolWorkOrder wo = mapper.selectById(id);
|
||||
if (wo == null) throw new RuntimeException("工单不存在");
|
||||
wo.setStatus("in_progress"); mapper.updateById(wo);
|
||||
}
|
||||
public void resolve(Long id, String resolution, String resultPhotos) {
|
||||
PatrolWorkOrder wo = mapper.selectById(id);
|
||||
if (wo == null) throw new RuntimeException("工单不存在");
|
||||
wo.setStatus("resolved"); wo.setResolution(resolution);
|
||||
wo.setResolvedAt(LocalDateTime.now());
|
||||
if (resultPhotos != null) wo.setPhotos(resultPhotos);
|
||||
mapper.updateById(wo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE IF NOT EXISTS pat_issue_report (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
report_no VARCHAR(32) UNIQUE,
|
||||
task_id BIGINT, reporter_id BIGINT, reporter_name VARCHAR(50),
|
||||
issue_type VARCHAR(30), description TEXT, photos TEXT, videos TEXT,
|
||||
lng NUMERIC(10,6), lat NUMERIC(10,6), address VARCHAR(200),
|
||||
severity VARCHAR(20) DEFAULT 'medium', status VARCHAR(20) DEFAULT 'submitted',
|
||||
work_order_id BIGINT, remark VARCHAR(500),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_pat_ir_task ON pat_issue_report(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_pat_ir_reporter ON pat_issue_report(reporter_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_pat_ir_status ON pat_issue_report(status);
|
||||
@@ -0,0 +1,24 @@
|
||||
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 PatrolAppTest {
|
||||
@Mock PatrolTaskMapper taskMapper; @Mock PatrolWorkOrderMapper woMapper; @Mock PatrolIssueReportMapper irMapper;
|
||||
@InjectMocks PatrolAppTaskService taskSvc; @InjectMocks PatrolAppWorkOrderService woSvc;
|
||||
@InjectMocks PatrolAppIssueService issueSvc; @InjectMocks PatrolAppProfileService profileSvc;
|
||||
|
||||
@Test void testMyTasks() { when(taskMapper.selectList(any())).thenReturn(Collections.emptyList()); when(taskMapper.selectCount(any())).thenReturn(0L); Map<String,Object> r=taskSvc.myTasks(100L,null); assertNotNull(r); assertEquals(0,r.get("totalCount")); }
|
||||
@Test void testAcceptTask() { PatrolTask t=new PatrolTask(); t.setId(1L); t.setStatus("pending"); when(taskMapper.selectById(1L)).thenReturn(t); when(taskMapper.updateById(any())).thenReturn(1); taskSvc.accept(1L); assertEquals("in_progress",t.getStatus()); assertNotNull(t.getStartTime()); }
|
||||
@Test void testCompleteTask() { PatrolTask t=new PatrolTask(); t.setId(1L); t.setStatus("in_progress"); when(taskMapper.selectById(1L)).thenReturn(t); when(taskMapper.updateById(any())).thenReturn(1); taskSvc.complete(1L,5,2000.0,"完成"); assertEquals("completed",t.getStatus()); assertEquals(5,(int)t.getCompletedCheckpoints()); }
|
||||
@Test void testMyWorkOrders() { when(woMapper.selectList(any())).thenReturn(Collections.emptyList()); when(woMapper.selectCount(any())).thenReturn(0L); Map<String,Object> r=woSvc.myWorkOrders(100L,null); assertNotNull(r); }
|
||||
@Test void testAcceptWO() { PatrolWorkOrder wo=new PatrolWorkOrder(); wo.setId(1L); wo.setStatus("assigned"); when(woMapper.selectById(1L)).thenReturn(wo); when(woMapper.updateById(any())).thenReturn(1); woSvc.accept(1L); assertEquals("in_progress",wo.getStatus()); }
|
||||
@Test void testResolveWO() { PatrolWorkOrder wo=new PatrolWorkOrder(); wo.setId(1L); wo.setStatus("in_progress"); when(woMapper.selectById(1L)).thenReturn(wo); when(woMapper.updateById(any())).thenReturn(1); woSvc.resolve(1L,"已修复",null); assertEquals("resolved",wo.getStatus()); assertNotNull(wo.getResolvedAt()); }
|
||||
@Test void testSubmitIssue() { when(irMapper.insert(any())).thenReturn(1); PatrolIssueReport r=issueSvc.submit(1L,100L,"张三","leak","管道漏水",null,null,new BigDecimal("116"),new BigDecimal("39"),"A路","high"); assertTrue(r.getReportNo().startsWith("PIR-")); assertEquals("submitted",r.getStatus()); }
|
||||
@Test void testMyReports() { when(irMapper.selectList(any())).thenReturn(Collections.emptyList()); when(irMapper.selectCount(any())).thenReturn(0L); Map<String,Object> r=issueSvc.myReports(100L); assertNotNull(r); assertEquals(0,r.get("totalCount")); }
|
||||
@Test void testProfile() { when(taskMapper.selectCount(any())).thenReturn(10L); when(woMapper.selectCount(any())).thenReturn(5L); Map<String,Object> r=profileSvc.getProfile(100L); assertEquals(100L,r.get("workerId")); }
|
||||
@Test void testMyStats() { when(taskMapper.selectCount(any())).thenReturn(5L); Map<String,Object> r=taskSvc.myStats(100L); assertNotNull(r); }
|
||||
}
|
||||
Reference in New Issue
Block a user