diff --git a/pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitWorkOrderReadController.java b/pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitWorkOrderReadController.java index f85f8df3..8464ba77 100644 --- a/pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitWorkOrderReadController.java +++ b/pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitWorkOrderReadController.java @@ -4,6 +4,7 @@ import com.zksy.cockpit.foundation.CockpitDeviceNotFoundException; import com.zksy.cockpit.foundation.CockpitErrorPayload; import com.zksy.cockpit.foundation.CockpitInvalidRequestException; import com.zksy.cockpit.foundation.CockpitReadException; +import com.zksy.cockpit.workorder.CockpitWorkOrderListResponse; import com.zksy.cockpit.workorder.CockpitWorkOrderReadResponse; import com.zksy.cockpit.workorder.CockpitWorkOrderReadService; import com.zksy.cockpit.workorder.CockpitWorkOrderTimelineResponse; @@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.util.UriUtils; @@ -62,6 +64,31 @@ public class CockpitWorkOrderReadController { return ResponseEntity.ok(AjaxResult.success(response)); } + /** + * 全量工单分页(票 65:报警效率面板的「当前工单列表」)。 + * + *

为什么单开一个路径而不是复用 `/equipments/{code}/work-orders`:报警族的工单跨设备, + * 面板要的是「当前所有工单」;实测该设备维度能查到 7 张工单,而 + * `handling-efficiency` 的 linkedWorkOrderCount=0 —— 两者粒度不同,不能互相代替。 + * + *

路径挂在 `/api/cockpit/v1/**` 下:免鉴权白名单只按**路径前缀**放行 + * (`SecurityConfig: antMatchers(GET, "/api/cockpit/v1/**").permitAll()`), + * 运营侧 `/api/ops/**` 需要登录态,驾驶舱不能直接用。 + * + *

三个查询参数都可缺省:`page` 默认 1、`pageSize` 默认 10(上限 100)、 + * `stateCode` 缺省不筛。夹取在 service,控制器不做第二套校验。 + */ + @GetMapping("/api/cockpit/v1/work-orders") + public ResponseEntity workOrderList( + @RequestParam(value = "page", required = false) Integer page, + @RequestParam(value = "pageSize", required = false) Integer pageSize, + @RequestParam(value = "stateCode", required = false) Integer stateCode, + @RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) { + CockpitWorkOrderListResponse response = workOrderService.loadAll( + resolveRequestId(requestId), page, pageSize, stateCode); + return ResponseEntity.ok(AjaxResult.success(response)); + } + @ExceptionHandler(CockpitInvalidRequestException.class) public ResponseEntity handleInvalidRequest(HttpServletRequest request) { return error(HttpStatus.BAD_REQUEST, workOrderService.invalidRequest( diff --git a/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/BusinessCockpitWorkOrderReadAdapter.java b/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/BusinessCockpitWorkOrderReadAdapter.java index dc285df6..ecb14d4c 100644 --- a/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/BusinessCockpitWorkOrderReadAdapter.java +++ b/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/BusinessCockpitWorkOrderReadAdapter.java @@ -76,6 +76,45 @@ public class BusinessCockpitWorkOrderReadAdapter implements CockpitWorkOrderRead .collect(Collectors.toList()); } + /** + * 全量工单分页(票 65:报警效率面板的「当前工单列表」)。 + * + *

与设备维度同一条读取路径,只是**不加 device_id 约束**:报警族的工单在库里 + * 本就跨设备存在(实测该端点 24 条报警事件在 handling-efficiency 里 + * linkedWorkOrderCount=0,但设备维度能查到 7 张工单),所以「当前工单」只能是全量口径。 + * + *

三个刻意的取舍: + *

+ */ + @Override + public CockpitWorkOrderListSnapshot loadAll(Integer stateCode, int limit, int offset) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (stateCode != null) { + wrapper.eq(WorkOrder::getOrderStatus, stateCode); + } + long total = workOrderService.count(wrapper); + List orders = total <= offset + ? Collections.emptyList() + : workOrderService.list(wrapper + .orderByDesc(WorkOrder::getCreateTime) + .last("LIMIT " + limit + " OFFSET " + offset)); + // 空页时不再打一次 association 查询:alarmsByOrderId 对空集合本来就早退,但显式一点更省一次调用栈。 + List summaries = orders.isEmpty() + ? Collections.emptyList() + : toAlarmSummaries(orders); + return new CockpitWorkOrderListSnapshot( + summaries, + (int) total, + limit <= 0 ? 1 : (offset / limit) + 1, + limit, + stateCode); + } + /** * 按对外报警标识解析报警:先按 {@code alarm_no}(真实单号),再退回主键 {@code id}。 * diff --git a/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/CockpitWorkOrderReadAdapter.java b/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/CockpitWorkOrderReadAdapter.java index b461d3f2..3df39ccc 100644 --- a/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/CockpitWorkOrderReadAdapter.java +++ b/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/CockpitWorkOrderReadAdapter.java @@ -6,4 +6,17 @@ public interface CockpitWorkOrderReadAdapter { CockpitWorkOrderReadSnapshot loadByEvent(String eventSource, String eventCode); CockpitWorkOrderTimelineSnapshot loadTimeline(String workOrderCode); -} \ No newline at end of file + + /** + * 全量工单分页(票 65:报警效率面板要列「当前工单列表」,不按设备也不按事件筛)。 + * + *

与设备维度共用同一条读取路径(运营侧 {@code WorkOrderService})与同一份 + * {@link CockpitWorkOrderSummary} 映射,区别只在不加 {@code device_id} 约束。 + * 排序口径与设备维度一致(创建时间倒序),前端才能稳定翻页。 + * + * @param stateCode 工单状态码 0–7;{@code null} 表示不筛 + * @param limit 本页条数(调用方已夹到合法区间) + * @param offset 偏移量,{@code (page-1)*limit} + */ + CockpitWorkOrderListSnapshot loadAll(Integer stateCode, int limit, int offset); +} diff --git a/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/CockpitWorkOrderReadService.java b/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/CockpitWorkOrderReadService.java index e898dba3..40d7acd4 100644 --- a/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/CockpitWorkOrderReadService.java +++ b/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/CockpitWorkOrderReadService.java @@ -18,6 +18,12 @@ import java.util.List; public class CockpitWorkOrderReadService { private static final List VALID_EVENT_SOURCES = Arrays.asList("ALARM", "WARNING"); + /** 全量工单列表的默认页大小(与驾驶舱其它列表端点一致)。 */ + private static final int DEFAULT_PAGE_SIZE = 10; + + /** 单次最多取多少张:防一个 pageSize 把全库拉下来。 */ + private static final int MAX_PAGE_SIZE = 100; + private final CockpitWorkOrderReadAdapter adapter; private final Clock clock; @@ -64,6 +70,44 @@ public class CockpitWorkOrderReadService { return error(requestId, CockpitErrorCategory.INVALID_REQUEST, "工单查询参数不合法"); } + /** + * 全量工单分页(票 65:报警效率面板的「当前工单列表」,不按设备也不按事件)。 + * + *

翻页参数在这里**夹到合法区间**,不信任入参直接下到 SQL 的 LIMIT/OFFSET: + * 非法页码轻则返回错页、重则合成语法错误的 SQL。夹取后回显实际生效值, + * 前端据此纠偏(与票 61「越界页码夹回」同一条纪律,只是这次发生在服务端入口)。 + * + * @param page 页码 1 起(null/越界 → 1) + * @param pageSize 每页条数(null/越界 → 10;上限 100,避免一次拉全库) + * @param stateCode 工单状态码 0–7(null 或越界 → 不筛) + */ + public CockpitWorkOrderListResponse loadAll( + String requestId, + Integer page, + Integer pageSize, + Integer stateCode) { + int safePage = page == null || page < 1 ? 1 : page; + int safeSize = pageSize == null || pageSize < 1 ? DEFAULT_PAGE_SIZE : Math.min(pageSize, MAX_PAGE_SIZE); + Integer safeState = stateCode != null && stateCode >= 0 && stateCode <= 7 ? stateCode : null; + CockpitWorkOrderListSnapshot snapshot = adapter.loadAll( + safeState, safeSize, (safePage - 1) * safeSize); + if (snapshot == null) { + throw new CockpitReadException("work order list unavailable"); + } + List workOrders = snapshot.getWorkOrders() == null + ? new ArrayList<>() : snapshot.getWorkOrders(); + boolean empty = workOrders.isEmpty(); + return new CockpitWorkOrderListResponse( + requestId, + "v1", + true, + empty ? "EMPTY" : "READY", + empty ? "NO_WORK_ORDERS" : "READY", + CockpitWorkOrderStateDictionary.states(), + workOrders, + new CockpitWorkOrderListResponse.Pagination(safePage, safeSize, snapshot.getTotal())); + } + public CockpitErrorPayload notFound(String requestId) { return error(requestId, CockpitErrorCategory.NOT_FOUND, "工单或关联对象不存在"); } diff --git a/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/EmptyCockpitWorkOrderReadAdapter.java b/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/EmptyCockpitWorkOrderReadAdapter.java index 7f8a694d..85ef228a 100644 --- a/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/EmptyCockpitWorkOrderReadAdapter.java +++ b/pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/workorder/EmptyCockpitWorkOrderReadAdapter.java @@ -18,4 +18,9 @@ public class EmptyCockpitWorkOrderReadAdapter implements CockpitWorkOrderReadAda public CockpitWorkOrderTimelineSnapshot loadTimeline(String workOrderCode) { return null; } + + @Override + public CockpitWorkOrderListSnapshot loadAll(Integer stateCode, int limit, int offset) { + return null; + } } \ No newline at end of file