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 张工单),所以「当前工单」只能是全量口径。
+ *
+ *
三个刻意的取舍:
+ *
+ * - {@code total} 走 {@code count(wrapper)} 独立查询,不由本页条数反推(票 61 纪律);
+ * - 排序与设备维度一致(创建时间倒序)——两个入口返回同一张表时顺序必须相同;
+ * - 关联解析仍走 {@link #toAlarmSummary}:全量列表里「是否关联报警」逐张如实标,
+ * 不因为换了个查询入口就一律标 true/false。
+ *
+ */
+ @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