diff --git a/AGENTS.md b/AGENTS.md index 96c9bcc..beb0e5d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,6 +22,11 @@ npm run start `npm run dev` starts the Refine/Next development server. `npm run lint` runs ESLint. `npm test` runs Jest. `npm run build` creates the production build. +When the Server API changes, sync `contracts/server-v1.openapi.json` from the +backend source of truth, update its SHA-256 in `contracts/manifest.json`, then +run `npm run api:generate` and `npm run api:check` so the generated types and +contract mirror remain aligned. + ## Coding Style & Naming Conventions Use TypeScript and React function components. Follow ESLint and Next.js conventions. Use `PascalCase` for React component files and component names. Use `camelCase` for ordinary TypeScript modules, hooks, stores, providers, utilities, variables, and functions. Next.js route directories under `src/app` use `kebab-case`; route groups and dynamic segments keep the Next.js syntax such as `(main)` and `[...nextauth]`. Keep backend/Agent boundary fields and query parameters in the shape required by the API, typically `snake_case`, and do not translate third-party SDK fields. Prefer MUI components and existing design tokens/patterns for UI. Keep operational screens dense, clear, and task-focused. diff --git a/contracts/manifest.json b/contracts/manifest.json index d250204..bebdee3 100644 --- a/contracts/manifest.json +++ b/contracts/manifest.json @@ -7,7 +7,7 @@ }, "server": { "file": "server-v1.openapi.json", - "sha256": "404a196c0177faed2aa5b46ee86430a034dfe990e0a77a43428a727748a882b6" + "sha256": "d0364fb08c6f18fac2ea9c9980ef21115fc01f110cd10f25f90d97d0bc0e6367" } } } diff --git a/contracts/server-v1.openapi.json b/contracts/server-v1.openapi.json index a53537d..0e9bc10 100644 --- a/contracts/server-v1.openapi.json +++ b/contracts/server-v1.openapi.json @@ -2149,6 +2149,176 @@ "title": "PumpFailureState", "type": "object" }, + "RealtimeLinkBatchItem": { + "properties": { + "flow": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Flow" + }, + "friction": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Friction" + }, + "headloss": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Headloss" + }, + "id": { + "title": "Id", + "type": "string" + }, + "quality": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Quality" + }, + "reaction": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Reaction" + }, + "setting": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Setting" + }, + "status": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Status" + }, + "time": { + "format": "date-time", + "title": "Time", + "type": "string" + }, + "velocity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Velocity" + } + }, + "required": [ + "time", + "id" + ], + "title": "RealtimeLinkBatchItem", + "type": "object" + }, + "RealtimeNodeBatchItem": { + "properties": { + "actual_demand": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Actual Demand" + }, + "id": { + "title": "Id", + "type": "string" + }, + "pressure": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Pressure" + }, + "quality": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Quality" + }, + "time": { + "format": "date-time", + "title": "Time", + "type": "string" + }, + "total_head": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Total Head" + } + }, + "required": [ + "time", + "id" + ], + "title": "RealtimeNodeBatchItem", + "type": "object" + }, "RunSimulationManuallyByDateRest": { "properties": { "duration": { @@ -2170,6 +2340,48 @@ "title": "RunSimulationManuallyByDateRest", "type": "object" }, + "ScadaReadingBatchItem": { + "properties": { + "cleaned_value": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Cleaned Value" + }, + "device_id": { + "minLength": 1, + "title": "Device Id", + "type": "string" + }, + "monitored_value": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Monitored Value" + }, + "time": { + "format": "date-time", + "title": "Time", + "type": "string" + } + }, + "required": [ + "time", + "device_id" + ], + "title": "ScadaReadingBatchItem", + "type": "object" + }, "SchedulingAnalysisRest": { "properties": { "pump_control": { @@ -6204,6 +6416,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -6950,6 +7173,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -7278,6 +7512,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "201": { "content": { @@ -7605,6 +7850,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -7932,6 +8188,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -8144,6 +8411,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -8246,6 +8524,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -8918,6 +9207,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -9016,6 +9316,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -11088,6 +11399,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -11642,6 +11964,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "204": { "description": "Successful Response" @@ -11731,6 +12064,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "201": { "content": { @@ -11948,6 +12292,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -12945,6 +13300,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "204": { "description": "Successful Response" @@ -13034,6 +13400,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -13130,6 +13507,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "201": { "content": { @@ -13687,6 +14075,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -14003,6 +14402,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -18095,6 +18505,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "201": { "content": { @@ -18422,6 +18843,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -18520,6 +18952,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -18715,7 +19158,7 @@ }, "/api/v1/pipeline-health-predictions": { "get": { - "description": "预测管道健康状况\n\n根据管网名称和当前时间,查询管道信息和实时数据,\n使用随机生存森林模型预测管道的生存概率。\n\nArgs:\n query_time: 查询时间\n network_name: 管网名称(或数据库名称)\n timescale_conn: TimescaleDB连接\n\nReturns:\n 预测结果列表,每个元素包含 link_id 和对应的生存函数\n\nRaises:\n HTTPException: 当模型文件不存在返回404错误,其他错误返回400或500错误", + "description": "预测管道健康状况\n\n根据管网名称和当前时间,查询管道信息和实时数据,\n使用随机生存森林模型预测管道的生存概率。\n\nArgs:\n query_time: 查询时间\n timescale_conn: TimescaleDB连接\n\nReturns:\n 预测结果列表,每个元素包含 link_id 和对应的生存函数\n\nRaises:\n HTTPException: 当模型文件不存在返回404错误,其他错误返回400或500错误", "operationId": "get_pipeline_health_predictions", "parameters": [ { @@ -20652,6 +21095,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -21435,345 +21889,6 @@ ] } }, - "/api/v1/project-codes": { - "get": { - "description": "获取服务器上所有可用的供水管网项目名称列表。", - "operationId": "get_project_codes", - "parameters": [ - { - "in": "query", - "name": "limit", - "required": false, - "schema": { - "default": 100, - "maximum": 1000, - "minimum": 1, - "title": "Limit", - "type": "integer" - } - }, - { - "in": "query", - "name": "offset", - "required": false, - "schema": { - "default": 0, - "minimum": 0, - "title": "Offset", - "type": "integer" - } - }, - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Page_str_" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "获取项目列表", - "tags": [ - "Project" - ] - } - }, - "/api/v1/project-conversions": { - "post": { - "description": "将 EPANET 3.0 格式的 INP 内容转换为 2.x 格式。", - "operationId": "post_project_conversions", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "转换 INP V3 为 V2", - "tags": [ - "Project" - ] - } - }, - "/api/v1/project-copies": { - "post": { - "description": "将现有项目复制为新项目。", - "operationId": "post_project_copies", - "parameters": [ - { - "description": "管网名称(或数据库名称)", - "in": "query", - "name": "source", - "required": true, - "schema": { - "description": "管网名称(或数据库名称)", - "title": "Source", - "type": "string" - } - }, - { - "description": "管网名称(或数据库名称)", - "in": "query", - "name": "target", - "required": true, - "schema": { - "description": "管网名称(或数据库名称)", - "title": "Target", - "type": "string" - } - }, - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "复制项目", - "tags": [ - "Project" - ] - } - }, "/api/v1/project-managements": { "post": { "description": "高级版本的项目管理,通过JSON请求体提供详细的控制参数,包括泵控制策略、水箱初始水位和区域需水量控制。", @@ -22082,95 +22197,6 @@ } }, "/api/v1/projects": { - "delete": { - "description": "永久删除指定的供水管网项目。此操作不可恢复。", - "operationId": "delete_projects", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "删除项目", - "tags": [ - "Project" - ] - }, "get": { "description": "获取当前用户有权限的所有项目列表", "operationId": "get_projects", @@ -22280,194 +22306,9 @@ "tags": [ "Metadata" ] - }, - "post": { - "description": "创建一个新的供水管网项目。如果项目已存在,可能会覆盖或报错(取决于底层实现)。", - "operationId": "post_projects", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "创建新项目", - "tags": [ - "Project" - ] } }, "/api/v1/projects/current": { - "delete": { - "description": "将指定项目从内存中卸载,释放资源。", - "operationId": "delete_projects_current", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "关闭项目", - "tags": [ - "Project" - ] - }, "get": { "description": "从数据库获取项目的详细信息,包括地图范围等。", "operationId": "get_projects_current", @@ -22563,102 +22404,6 @@ "tags": [ "Project" ] - }, - "post": { - "description": "将指定项目加载到内存中,并初始化数据库连接池。", - "operationId": "post_projects_current", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "打开项目", - "tags": [ - "Project" - ] } }, "/api/v1/projects/current/database-health": { @@ -22868,716 +22613,6 @@ ] } }, - "/api/v1/projects/current/exports/inp": { - "post": { - "description": "将项目当前状态保存为 INP 文件到服务器文件系统。", - "operationId": "post_projects_current_exports_inp", - "parameters": [ - { - "description": "目标文件名", - "in": "query", - "name": "inp", - "required": true, - "schema": { - "description": "目标文件名", - "title": "Inp", - "type": "string" - } - }, - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "title": "Response Post Projects Current Exports Inp", - "type": "boolean" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "导出项目到 INP 文件", - "tags": [ - "Project" - ] - } - }, - "/api/v1/projects/current/files/inp": { - "get": { - "description": "从服务器数据目录下载指定的 INP 文件。", - "operationId": "get_projects_current_files_inp", - "parameters": [ - { - "description": "文件名", - "in": "query", - "name": "name", - "required": true, - "schema": { - "description": "文件名", - "title": "Name", - "type": "string" - } - }, - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "下载 INP 文件", - "tags": [ - "Project" - ] - } - }, - "/api/v1/projects/current/imports": { - "post": { - "description": "从服务器文件系统中读取指定的 INP 文件并加载到项目中。", - "operationId": "post_projects_current_imports", - "parameters": [ - { - "description": "INP 文件名 (不包含路径)", - "in": "query", - "name": "inp", - "required": true, - "schema": { - "description": "INP 文件名 (不包含路径)", - "title": "Inp", - "type": "string" - } - }, - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "title": "Response Post Projects Current Imports", - "type": "boolean" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "读取 INP 文件到项目", - "tags": [ - "Project" - ] - } - }, - "/api/v1/projects/current/lock": { - "delete": { - "description": "释放对项目的锁定。", - "operationId": "delete_projects_current_lock", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "解锁项目", - "tags": [ - "Project" - ] - }, - "get": { - "description": "检查指定项目是否处于锁定状态。", - "operationId": "get_projects_current_lock", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "检查项目是否被锁定", - "tags": [ - "Project" - ] - }, - "post": { - "description": "锁定指定项目以防止并发修改。", - "operationId": "post_projects_current_lock", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "锁定项目", - "tags": [ - "Project" - ] - } - }, - "/api/v1/projects/current/lock/ownership": { - "get": { - "description": "检查指定项目是否被当前访问地址 (IP) 锁定。", - "operationId": "get_projects_current_lock_ownership", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "检查项目是否被当前用户锁定", - "tags": [ - "Project" - ] - } - }, "/api/v1/projects/current/metadata": { "get": { "description": "获取当前项目的元数据和配置信息", @@ -23676,202 +22711,6 @@ ] } }, - "/api/v1/projects/current/status": { - "get": { - "description": "检查指定项目是否已被加载到内存中。", - "operationId": "get_projects_current_status", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "检查项目是否已打开", - "tags": [ - "Project" - ] - } - }, - "/api/v1/projects/existence": { - "get": { - "description": "检查指定名称的项目是否存在。", - "operationId": "get_projects_existence", - "parameters": [ - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "检查项目是否存在", - "tags": [ - "Project" - ] - } - }, "/api/v1/pump-failure-events": { "post": { "description": "记录和管理泵的故障状态,包括故障发生时间和受影响的泵列表。系统将记录故障日志并更新泵状态。", @@ -25058,6 +23897,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -25264,6 +24114,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -25362,6 +24223,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -25558,6 +24430,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "204": { "description": "Successful Response" @@ -25764,6 +24647,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -25859,6 +24753,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "201": { "content": { @@ -27506,6 +26411,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -28177,6 +27093,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -29809,6 +28736,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -29905,6 +28843,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "201": { "content": { @@ -30234,6 +29183,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -30553,6 +29513,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -30772,6 +29743,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -33517,6 +32499,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -34306,6 +33299,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -34679,126 +33683,6 @@ ] } }, - "/api/v1/timeseries/analysis/runs/{run_id}/results": { - "post": { - "operationId": "post_timeseries_analysis_runs_run_id_results", - "parameters": [ - { - "description": "分析运行 ID", - "in": "path", - "name": "run_id", - "required": true, - "schema": { - "description": "分析运行 ID", - "format": "uuid", - "title": "Run Id", - "type": "string" - } - }, - { - "in": "header", - "name": "X-Project-Id", - "required": true, - "schema": { - "title": "X-Project-Id", - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "title": "Payload", - "type": "object" - } - } - }, - "required": true - }, - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JsonValue" - } - } - }, - "description": "Successful Response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Authentication required" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Insufficient permission" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource not found" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Resource conflict" - }, - "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Validation error" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - }, - "description": "Dependency unavailable" - } - }, - "security": [ - { - "OAuth2PasswordBearer": [] - } - ], - "summary": "Store Analysis Results", - "tags": [ - "TimescaleDB - Analysis" - ] - } - }, "/api/v1/timeseries/analysis/runs/{run_id}/values": { "get": { "operationId": "get_timeseries_analysis_runs_run_id_values", @@ -35189,9 +34073,9 @@ "content": { "application/json": { "schema": { - "description": "管道数据列表,每项包含管道ID、时间戳等信息", + "description": "同一时间点的管道快照数据", "items": { - "type": "object" + "$ref": "#/components/schemas/RealtimeLinkBatchItem" }, "title": "Data", "type": "array" @@ -35680,9 +34564,9 @@ "content": { "application/json": { "schema": { - "description": "节点数据列表,每项包含节点ID、时间戳等信息", + "description": "同一时间点的节点快照数据", "items": { - "type": "object" + "$ref": "#/components/schemas/RealtimeNodeBatchItem" }, "title": "Data", "type": "array" @@ -36564,8 +35448,10 @@ "schema": { "description": "SCADA设备监测数据列表", "items": { - "type": "object" + "$ref": "#/components/schemas/ScadaReadingBatchItem" }, + "maxItems": 10000, + "minItems": 1, "title": "Data", "type": "array" } @@ -37606,6 +36492,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -39431,6 +38328,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { @@ -39999,6 +38907,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "204": { "description": "Successful Response" @@ -40088,6 +39007,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "201": { "content": { @@ -40415,6 +39345,17 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Payload", + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { diff --git a/src/components/chat/ChatToolCallBlock.tsx b/src/components/chat/ChatToolCallBlock.tsx index 9b16f12..712b553 100644 --- a/src/components/chat/ChatToolCallBlock.tsx +++ b/src/components/chat/ChatToolCallBlock.tsx @@ -30,6 +30,7 @@ import { describeApplyLayerStyle, parseApplyLayerStylePayload, } from "./toolCallStyleHelpers"; +import { buildViewHistoryAction } from "./historyToolAction"; /* ------------------------------------------------------------------ */ /* Interactive card rendered inside a chat bubble for tool actions */ @@ -428,17 +429,7 @@ function buildAction(toolCall: ToolCall): ChatToolAction | null { }; } case "view_history": { - const historyRange = resolveTimeRange(); - return { - type: "view_history", - featureInfos: - (params.feature_infos as [string, string][] | undefined) ?? [], - dataType: - (params.data_type as "realtime" | "scheme" | "none" | undefined) ?? - "realtime", - startTime: historyRange.startTime, - endTime: historyRange.endTime, - }; + return buildViewHistoryAction(params); } case "view_scada": { const scadaRange = resolveTimeRange(); diff --git a/src/components/chat/historyToolAction.test.ts b/src/components/chat/historyToolAction.test.ts new file mode 100644 index 0000000..b85458a --- /dev/null +++ b/src/components/chat/historyToolAction.test.ts @@ -0,0 +1,36 @@ +import { buildViewHistoryAction } from "./historyToolAction"; + +describe("buildViewHistoryAction", () => { + it("preserves the scheme run ID from Agent tool parameters", () => { + expect( + buildViewHistoryAction({ + feature_infos: [["P-1", "pipe"]], + data_type: "scheme", + run_id: " 99dd4142-368b-54cb-bfca-d59ee48f6298 ", + }), + ).toEqual( + expect.objectContaining({ + featureInfos: [["P-1", "pipe"]], + dataType: "scheme", + runId: "99dd4142-368b-54cb-bfca-d59ee48f6298", + }), + ); + }); + + it("normalizes invalid history parameters", () => { + expect( + buildViewHistoryAction({ + feature_infos: [["", "pipe"], "invalid"], + data_type: "unexpected", + runId: " ", + }), + ).toEqual({ + type: "view_history", + featureInfos: [], + dataType: "realtime", + runId: undefined, + startTime: undefined, + endTime: undefined, + }); + }); +}); diff --git a/src/components/chat/historyToolAction.ts b/src/components/chat/historyToolAction.ts new file mode 100644 index 0000000..272305e --- /dev/null +++ b/src/components/chat/historyToolAction.ts @@ -0,0 +1,43 @@ +import type { ChatToolAction } from "@/store/chatToolStore"; + +type ViewHistoryAction = Extract; + +const readOptionalString = (value: unknown) => + typeof value === "string" && value.trim() ? value.trim() : undefined; + +export const buildViewHistoryAction = ( + params: Record, +): ViewHistoryAction => { + const rawFeatureInfos = Array.isArray(params.feature_infos) + ? params.feature_infos + : []; + const featureInfos = rawFeatureInfos + .filter( + (item): item is [unknown, unknown] => + Array.isArray(item) && item.length >= 2, + ) + .map( + ([id, type]) => [String(id).trim(), String(type).trim()] as [string, string], + ) + .filter(([id, type]) => id.length > 0 && type.length > 0); + const rawDataType = params.data_type; + const dataType = + rawDataType === "realtime" || + rawDataType === "scheme" || + rawDataType === "none" + ? rawDataType + : "realtime"; + + return { + type: "view_history", + featureInfos, + dataType, + runId: readOptionalString(params.run_id ?? params.runId), + startTime: readOptionalString( + params.start_time ?? params.startTime ?? params.from ?? params.start, + ), + endTime: readOptionalString( + params.end_time ?? params.endTime ?? params.to ?? params.end, + ), + }; +}; diff --git a/src/components/chat/hooks/useAgentToolActions.ts b/src/components/chat/hooks/useAgentToolActions.ts index e5c5e8e..2145ca1 100644 --- a/src/components/chat/hooks/useAgentToolActions.ts +++ b/src/components/chat/hooks/useAgentToolActions.ts @@ -10,6 +10,7 @@ import { describeApplyLayerStyle, parseApplyLayerStylePayload, } from "../toolCallStyleHelpers"; +import { buildViewHistoryAction } from "../historyToolAction"; type ToolCallEvent = StreamEvent & { type: "tool_call" }; @@ -253,21 +254,12 @@ const buildToolAction = ( } if (tool === "view_history") { - const featureInfos = (params.feature_infos as [string, string][] | undefined) ?? []; - const { startTime, endTime } = resolveTimeRange(params); + const action = buildViewHistoryAction(params); return { - action: { - type: "view_history", - featureInfos, - dataType: - (params.data_type as "realtime" | "scheme" | "none" | undefined) ?? - "realtime", - startTime, - endTime, - }, + action, kind: "panel", title: "打开计算结果曲线", - description: compactNames(featureInfos.map(([id]) => id)), + description: compactNames(action.featureInfos.map(([id]) => id)), }; } diff --git a/src/components/olmap/BurstDetection/AnalysisParameters.test.ts b/src/components/olmap/BurstDetection/AnalysisParameters.test.ts index e25dbc8..55d2de0 100644 --- a/src/components/olmap/BurstDetection/AnalysisParameters.test.ts +++ b/src/components/olmap/BurstDetection/AnalysisParameters.test.ts @@ -1,12 +1,26 @@ import dayjs from "dayjs"; +import { api } from "@/lib/api"; import { buildBurstDetectionRequest, createBurstDetectionAnalysisParametersState, + fetchPressureSamplingInterval, parseScadaFrequencyMinutes, resolvePressureSamplingInterval, } from "./AnalysisParameters"; +jest.mock("@/lib/api", () => ({ + api: { + get: jest.fn(), + }, +})); + +const get = api.get as jest.Mock; + describe("burst detection request", () => { + beforeEach(() => { + get.mockReset(); + }); + it("requests the latest complete monitoring time by default", () => { const state = createBurstDetectionAnalysisParametersState(); state.schemeName = " latest-case "; @@ -45,11 +59,27 @@ describe("burst detection request", () => { expect(parseScadaFrequencyMinutes("1:00:00")).toBe(60); expect( resolvePressureSamplingInterval([ - { type: "pressure", transmission_frequency: "0:15:00" }, - { type: "pressure", transmission_frequency: "0:15:00" }, - { type: "pressure", transmission_frequency: "0:30:00" }, - { type: "pipe_flow", transmission_frequency: "1:00:00" }, + { device_type: "pressure", transmission_frequency: "0:15:00" }, + { device_type: "pressure", transmission_frequency: "0:15:00" }, + { device_type: "pressure", transmission_frequency: "0:30:00" }, + { device_type: "pipe_flow", transmission_frequency: "1:00:00" }, ]), ).toBe(15); }); + + it("loads the pressure frequency from the pooled SCADA metadata endpoint", async () => { + get.mockResolvedValue({ + data: { + success: true, + count: 2, + data: [ + { device_type: "pressure", transmission_frequency: "0:30:00" }, + { device_type: "pipe_flow", transmission_frequency: "0:15:00" }, + ], + }, + }); + + await expect(fetchPressureSamplingInterval()).resolves.toBe(30); + expect(get).toHaveBeenCalledWith("/api/v1/scada-info/database-view"); + }); }); diff --git a/src/components/olmap/BurstDetection/AnalysisParameters.tsx b/src/components/olmap/BurstDetection/AnalysisParameters.tsx index d35ef9f..f381697 100644 --- a/src/components/olmap/BurstDetection/AnalysisParameters.tsx +++ b/src/components/olmap/BurstDetection/AnalysisParameters.tsx @@ -46,10 +46,14 @@ export interface BurstDetectionAnalysisParametersState { } interface ScadaInfoItem { - type?: string; + device_type?: string; transmission_frequency?: string | number | null; } +interface ScadaInfoResponse { + data?: ScadaInfoItem[]; +} + const currentQuarterHour = () => { const now = dayjs().second(0).millisecond(0); return now.minute(Math.floor(now.minute() / 15) * 15); @@ -86,7 +90,7 @@ export const parseScadaFrequencyMinutes = ( export const resolvePressureSamplingInterval = (items: ScadaInfoItem[]) => { const counts = new Map(); items - .filter((item) => item.type?.toLowerCase() === "pressure") + .filter((item) => item.device_type?.toLowerCase() === "pressure") .forEach((item) => { const minutes = parseScadaFrequencyMinutes(item.transmission_frequency); if (minutes && 1440 % minutes === 0) { @@ -99,6 +103,15 @@ export const resolvePressureSamplingInterval = (items: ScadaInfoItem[]) => { )[0]?.[0] ?? 15; }; +export const fetchPressureSamplingInterval = async () => { + const response = await api.get( + "/api/v1/scada-info/database-view", + ); + return resolvePressureSamplingInterval( + Array.isArray(response.data.data) ? response.data.data : [], + ); +}; + export const buildBurstDetectionRequest = ( parameters: BurstDetectionAnalysisParametersState, ) => ({ @@ -141,13 +154,9 @@ const AnalysisParameters: React.FC = ({ if (samplingIntervalSource !== "metadata") return; let active = true; setFrequencyLoading(true); - api - .get("/api/v1/scada-info") - .then((response) => { + fetchPressureSamplingInterval() + .then((interval) => { if (!active) return; - const interval = resolvePressureSamplingInterval( - response.data as ScadaInfoItem[], - ); setParametersState((previous) => previous.samplingIntervalSource === "metadata" ? { ...previous, samplingIntervalMinutes: interval } diff --git a/src/components/olmap/BurstDetection/BurstDetectionPanel.tsx b/src/components/olmap/BurstDetection/BurstDetectionPanel.tsx index d4ab172..6e0e0b6 100644 --- a/src/components/olmap/BurstDetection/BurstDetectionPanel.tsx +++ b/src/components/olmap/BurstDetection/BurstDetectionPanel.tsx @@ -21,6 +21,7 @@ import SchemeQuery, { createBurstDetectionSchemeQueryState, type BurstDetectionSchemeQueryState, } from "./SchemeQuery"; +import { useData } from "@components/olmap/core/MapComponent"; import { BurstDetectionResult, BurstDetectionSchemeRecord } from "./types"; const TabPanel = ({ @@ -38,6 +39,7 @@ const TabPanel = ({ ); const BurstDetectionPanel: React.FC = () => { + const setSchemeRunId = useData()?.setSchemeRunId; const [open, setOpen] = useState(true); const [tab, setTab] = useState(0); const [result, setResult] = useState(null); @@ -58,10 +60,23 @@ const BurstDetectionPanel: React.FC = () => { const drawerWidth = 450; const panelTitle = "爆管侦测"; - const handleResult = useCallback((payload: BurstDetectionResult) => { - setResult(payload); - setTab(2); - }, []); + const handleResult = useCallback( + (payload: BurstDetectionResult) => { + setSchemeRunId?.(""); + setResult(payload); + setTab(2); + }, + [setSchemeRunId], + ); + + const handleViewResult = useCallback( + (payload: BurstDetectionResult, runId: string) => { + setSchemeRunId?.(runId); + setResult(payload); + setTab(2); + }, + [setSchemeRunId], + ); return ( <> @@ -166,7 +181,7 @@ const BurstDetectionPanel: React.FC = () => { void; + onViewResult: (result: BurstDetectionResult, runId: string) => void; schemes?: BurstDetectionSchemeRecord[]; onSchemesChange?: (schemes: BurstDetectionSchemeRecord[]) => void; state?: BurstDetectionSchemeQueryState; @@ -174,7 +174,7 @@ const SchemeQuery: React.FC = ({ throw new Error("方案详情缺少侦测结果数据"); } - onViewResult(normalizedResult); + onViewResult(normalizedResult, runId); open?.({ type: "success", message: "方案加载成功", diff --git a/src/components/olmap/BurstLocation/BurstLocationPanel.tsx b/src/components/olmap/BurstLocation/BurstLocationPanel.tsx index f97b87c..9db01ec 100644 --- a/src/components/olmap/BurstLocation/BurstLocationPanel.tsx +++ b/src/components/olmap/BurstLocation/BurstLocationPanel.tsx @@ -19,6 +19,7 @@ import SchemeQuery, { type BurstLocationSchemeQueryState, } from "./SchemeQuery"; import { BurstLocationResult, BurstSchemeRecord } from "./types"; +import { useData } from "@components/olmap/core/MapComponent"; const TabPanel = ({ value, @@ -35,6 +36,7 @@ const TabPanel = ({ ); const BurstLocationPanel: React.FC = () => { + const setSchemeRunId = useData()?.setSchemeRunId; const [open, setOpen] = useState(true); const [tab, setTab] = useState(0); const [result, setResult] = useState(null); @@ -51,15 +53,23 @@ const BurstLocationPanel: React.FC = () => { const drawerWidth = 450; const panelTitle = "爆管定位"; - const handleResult = useCallback((payload: BurstLocationResult) => { - setResult(payload); - setTab(2); - }, []); + const handleResult = useCallback( + (payload: BurstLocationResult) => { + setSchemeRunId?.(""); + setResult(payload); + setTab(2); + }, + [setSchemeRunId], + ); - const handleViewResult = useCallback((payload: BurstLocationResult) => { - setResult(payload); - setTab(2); - }, []); + const handleViewResult = useCallback( + (payload: BurstLocationResult, runId: string) => { + setSchemeRunId?.(runId); + setResult(payload); + setTab(2); + }, + [setSchemeRunId], + ); return ( <> diff --git a/src/components/olmap/BurstLocation/SchemeQuery.tsx b/src/components/olmap/BurstLocation/SchemeQuery.tsx index 49684ac..7776067 100644 --- a/src/components/olmap/BurstLocation/SchemeQuery.tsx +++ b/src/components/olmap/BurstLocation/SchemeQuery.tsx @@ -46,7 +46,7 @@ import { useSchemeCreatorName } from "@components/olmap/core/useSchemeCreatorNam import { SchemeQueryEmptyState } from "@components/olmap/common/PanelEmptyState"; interface Props { - onViewResult: (result: BurstLocationResult) => void; + onViewResult: (result: BurstLocationResult, runId: string) => void; schemes?: BurstSchemeRecord[]; onSchemesChange?: (schemes: BurstSchemeRecord[]) => void; state?: BurstLocationSchemeQueryState; @@ -285,7 +285,7 @@ const SchemeQuery: React.FC = ({ if (!normalizedResult) { throw new Error("方案详情缺少定位结果数据"); } - onViewResult(enrichResultWithSimulationBurstIds(normalizedResult)); + onViewResult(enrichResultWithSimulationBurstIds(normalizedResult), runId); open?.({ type: "success", message: "方案加载成功", diff --git a/src/components/olmap/DMALeakDetection/DMALeakDetectionPanel.tsx b/src/components/olmap/DMALeakDetection/DMALeakDetectionPanel.tsx index 1321323..e6168c5 100644 --- a/src/components/olmap/DMALeakDetection/DMALeakDetectionPanel.tsx +++ b/src/components/olmap/DMALeakDetection/DMALeakDetectionPanel.tsx @@ -17,7 +17,7 @@ import { ChevronRight, FormatListBulleted, } from "@mui/icons-material"; -import { useMap } from "@components/olmap/core/MapComponent"; +import { useData, useMap } from "@components/olmap/core/MapComponent"; import StyleLegend from "@components/olmap/core/Controls/StyleLegend"; import AnalysisParameters, { createDMALeakAnalysisParametersState, @@ -50,6 +50,7 @@ const DMA_AREA_INDEX_PROPERTY = "dma_area_index"; const DMALeakDetectionPanel: React.FC = () => { const map = useMap(); + const setSchemeRunId = useData()?.setSchemeRunId; const [open, setOpen] = useState(true); const [tab, setTab] = useState(0); const [result, setResult] = useState(null); @@ -79,15 +80,23 @@ const DMALeakDetectionPanel: React.FC = () => { [activeAreas.length], ); - const handleAnalysisResult = useCallback((res: LeakageResultDetail) => { - setResult(res); - }, []); + const handleAnalysisResult = useCallback( + (res: LeakageResultDetail) => { + setSchemeRunId?.(""); + setResult(res); + }, + [setSchemeRunId], + ); - const handleViewResult = useCallback((res: LeakageResultDetail) => { - setResult(res); - setLoadedResult(res); - setTab(2); - }, []); + const handleViewResult = useCallback( + (res: LeakageResultDetail, runId: string) => { + setSchemeRunId?.(runId); + setResult(res); + setLoadedResult(res); + setTab(2); + }, + [setSchemeRunId], + ); useEffect(() => { if (!map) return; diff --git a/src/components/olmap/DMALeakDetection/SchemeQuery.tsx b/src/components/olmap/DMALeakDetection/SchemeQuery.tsx index bfc42bc..7e1ef06 100644 --- a/src/components/olmap/DMALeakDetection/SchemeQuery.tsx +++ b/src/components/olmap/DMALeakDetection/SchemeQuery.tsx @@ -27,13 +27,17 @@ import { listAnalysisSchemes, } from "@/lib/analysisRuns"; import { useControllableObjectState } from "@components/olmap/core/useControllableState"; -import { LeakageResultDetail, LeakageSchemeRecord } from "./types"; +import { + LeakageResultDetail, + LeakageSchemeRecord, + normalizeLeakageRows, +} from "./types"; import { FLOW_DISPLAY_UNIT, toM3h } from "@utils/units"; import { useSchemeCreatorName } from "@components/olmap/core/useSchemeCreatorName"; import { SchemeQueryEmptyState } from "@components/olmap/common/PanelEmptyState"; interface Props { - onViewResult: (result: LeakageResultDetail) => void; + onViewResult: (result: LeakageResultDetail, runId: string) => void; schemes?: LeakageSchemeRecord[]; onSchemesChange?: (schemes: LeakageSchemeRecord[]) => void; state?: DMALeakSchemeQueryState; @@ -131,14 +135,18 @@ const SchemeQuery: React.FC = ({ if (!result) { throw new Error("方案详情缺少漏损识别结果"); } - onViewResult({ - ...result, - scheme_name: scheme.scheme_name, - scheme_detail: scheme.scheme_detail, - scheme_start_time: scheme.scheme_start_time, - create_time: scheme.create_time, - username: scheme.username, - } as LeakageResultDetail); + onViewResult( + { + ...result, + rows: normalizeLeakageRows(result), + scheme_name: scheme.scheme_name, + scheme_detail: scheme.scheme_detail, + scheme_start_time: scheme.scheme_start_time, + create_time: scheme.create_time, + username: scheme.username, + } as LeakageResultDetail, + runId, + ); } catch (error: any) { open?.({ type: "error", diff --git a/src/components/olmap/DMALeakDetection/types.test.ts b/src/components/olmap/DMALeakDetection/types.test.ts new file mode 100644 index 0000000..a2ffa95 --- /dev/null +++ b/src/components/olmap/DMALeakDetection/types.test.ts @@ -0,0 +1,27 @@ +import { normalizeLeakageRows } from "./types"; + +const migratedRow = { + Area: "DMA-01", + LeakageRatioRaw: 0.12, + LeakageRatio: 0.1, + LeakageFlow_m3_per_s: 0.02, +}; + +describe("normalizeLeakageRows", () => { + it("restores migrated DMA results stored under result_rows", () => { + expect(normalizeLeakageRows({ result_rows: [migratedRow] })).toEqual([ + migratedRow, + ]); + }); + + it("prefers the canonical rows field when both shapes are present", () => { + const canonicalRow = { ...migratedRow, Area: "DMA-02" }; + + expect( + normalizeLeakageRows({ + rows: [canonicalRow], + result_rows: [migratedRow], + }), + ).toEqual([canonicalRow]); + }); +}); diff --git a/src/components/olmap/DMALeakDetection/types.ts b/src/components/olmap/DMALeakDetection/types.ts index 5ff165b..2313491 100644 --- a/src/components/olmap/DMALeakDetection/types.ts +++ b/src/components/olmap/DMALeakDetection/types.ts @@ -5,6 +5,18 @@ export interface LeakageRow { LeakageFlow_m3_per_s: number; } +export const normalizeLeakageRows = ( + payload: Record, +): LeakageRow[] => { + if (Array.isArray(payload.rows)) { + return payload.rows as LeakageRow[]; + } + + return Array.isArray(payload.result_rows) + ? (payload.result_rows as LeakageRow[]) + : []; +}; + export interface LeakageSchemeRecord { scheme_id: string; scheme_name: string; diff --git a/src/components/olmap/MonitoringPlaceOptimization/schemeApi.test.ts b/src/components/olmap/MonitoringPlaceOptimization/schemeApi.test.ts index ac94d00..329a66a 100644 --- a/src/components/olmap/MonitoringPlaceOptimization/schemeApi.test.ts +++ b/src/components/olmap/MonitoringPlaceOptimization/schemeApi.test.ts @@ -31,9 +31,12 @@ const run = { describe("sensor placement runs API adapter", () => { beforeEach(() => jest.clearAllMocks()); - it("lists paged runs and maps run fields to the existing screen model", async () => { + it.each([ + ["contract page", { items: [run], total: 1, limit: 1000, offset: 0 }], + ["interceptor-unwrapped page", [run]], + ])("maps runs from the %s response", async (_label, data) => { jest.mocked(api.get).mockResolvedValue({ - data: { items: [run], total: 1, limit: 1000, offset: 0 }, + data, }); await expect(listSensorPlacementSchemes()).resolves.toEqual([ diff --git a/src/components/olmap/MonitoringPlaceOptimization/schemeApi.ts b/src/components/olmap/MonitoringPlaceOptimization/schemeApi.ts index c8239a0..475e618 100644 --- a/src/components/olmap/MonitoringPlaceOptimization/schemeApi.ts +++ b/src/components/olmap/MonitoringPlaceOptimization/schemeApi.ts @@ -26,6 +26,13 @@ type SensorPlacementRunResponse = { can_edit: boolean; }; +type SensorPlacementRunPage = { + items: SensorPlacementRunResponse[]; + total: number; + limit: number; + offset: number; +}; + const toSensorPlacementScheme = ( run: SensorPlacementRunResponse, ): SensorPlacementScheme => ({ @@ -53,12 +60,16 @@ export const optimizeSensorPlacement = async ( export const listSensorPlacementSchemes = async (): Promise< SensorPlacementScheme[] > => { - const response = await api.get<{ - items: SensorPlacementRunResponse[]; - }>(`${config.BACKEND_URL}/api/v1/sensor-placement-runs`, { - params: { limit: 1000, offset: 0 }, - }); - return response.data.items.map(toSensorPlacementScheme); + const response = await api.get< + SensorPlacementRunPage | SensorPlacementRunResponse[] + >( + `${config.BACKEND_URL}/api/v1/sensor-placement-runs`, + { params: { limit: 1000, offset: 0 } }, + ); + const runs = Array.isArray(response.data) + ? response.data + : response.data.items; + return runs.map(toSensorPlacementScheme); }; export const getSensorPlacementScheme = async ( diff --git a/src/components/olmap/core/Controls/HistoryDataPanel.test.ts b/src/components/olmap/core/Controls/HistoryDataPanel.test.ts new file mode 100644 index 0000000..51e7fb1 --- /dev/null +++ b/src/components/olmap/core/Controls/HistoryDataPanel.test.ts @@ -0,0 +1,110 @@ +import { apiFetch } from "@/lib/apiFetch"; +import { fetchHistoryData } from "./HistoryDataPanel"; + +jest.mock("@/lib/apiFetch", () => ({ + apiFetch: jest.fn(), +})); + +jest.mock("@components/olmap/core/MapComponent", () => ({ + useData: () => null, +})); + +const jsonResponse = (payload: unknown, status = 200) => + ({ + ok: status >= 200 && status < 300, + status, + json: jest.fn().mockResolvedValue(payload), + }) as unknown as Response; + +const range = { + from: new Date("2026-08-17T09:00:00.000Z"), + to: new Date("2026-08-17T10:00:00.000Z"), +}; + +describe("fetchHistoryData", () => { + beforeEach(() => jest.clearAllMocks()); + + it("queries SCADA readings once per selected network element", async () => { + jest.mocked(apiFetch).mockImplementation(async (input) => { + const url = new URL(String(input)); + const elementId = url.searchParams.get("element_id") ?? ""; + return jsonResponse({ + [elementId]: [{ time: range.from.toISOString(), value: 1 }], + }); + }); + + await fetchHistoryData( + [ + ["J-1", "junction"], + ["J-2", "junction"], + ], + range, + "none", + ); + + const elementIds = jest + .mocked(apiFetch) + .mock.calls.map(([input]) => + new URL(String(input)).searchParams.get("element_id"), + ); + expect(elementIds).toEqual(["J-1", "J-2", "J-1", "J-2"]); + }); + + it("uses the analysis run ID for historical scheme simulation data", async () => { + jest.mocked(apiFetch).mockResolvedValue(jsonResponse({ "P-1": [] })); + + await fetchHistoryData( + [["P-1", "pipe"]], + range, + "scheme", + "99dd4142-368b-54cb-bfca-d59ee48f6298", + ); + + const simulationUrls = jest + .mocked(apiFetch) + .mock.calls.map(([input]) => new URL(String(input))) + .filter((url) => url.pathname.endsWith("/element-simulations")); + + expect(simulationUrls).toHaveLength(2); + expect( + simulationUrls.map((url) => url.searchParams.get("run_id")), + ).toEqual([null, "99dd4142-368b-54cb-bfca-d59ee48f6298"]); + }); + + it("rejects oversized element selections before issuing requests", async () => { + jest.mocked(apiFetch).mockResolvedValue(jsonResponse({})); + const featureInfos = Array.from( + { length: 201 }, + (_, index) => [`J-${index}`, "junction"] as [string, string], + ); + + await expect( + fetchHistoryData(featureInfos, range, "none"), + ).rejects.toThrow("历史数据一次最多查询 200 个管网元素"); + expect(apiFetch).not.toHaveBeenCalled(); + }); + + it("limits concurrent SCADA requests for multi-element history", async () => { + let activeRequests = 0; + let peakRequests = 0; + jest.mocked(apiFetch).mockImplementation(async (input) => { + activeRequests += 1; + peakRequests = Math.max(peakRequests, activeRequests); + await new Promise((resolve) => setTimeout(resolve, 0)); + activeRequests -= 1; + const elementId = new URL(String(input)).searchParams.get("element_id") ?? ""; + return jsonResponse({ [elementId]: [] }); + }); + + await fetchHistoryData( + Array.from( + { length: 20 }, + (_, index) => [`J-${index}`, "junction"] as [string, string], + ), + range, + "none", + ); + + expect(peakRequests).toBeLessThanOrEqual(8); + }); +}); diff --git a/src/components/olmap/core/Controls/HistoryDataPanel.tsx b/src/components/olmap/core/Controls/HistoryDataPanel.tsx index a23192b..faf630c 100644 --- a/src/components/olmap/core/Controls/HistoryDataPanel.tsx +++ b/src/components/olmap/core/Controls/HistoryDataPanel.tsx @@ -53,10 +53,6 @@ export interface SCADADataPanelProps { featureInfos: [string, string][]; /** 数据类型: realtime-查询模拟值和监测值, none-仅查询监测值, scheme-查询策略模拟值和监测值 */ type?: "realtime" | "scheme" | "none"; - /** 策略类型 */ - scheme_type?: string; - /** 策略名称 */ - scheme_name?: string; /** 默认展示的选项卡 */ defaultTab?: "chart" | "table"; /** Y 轴数值的小数位数 */ @@ -65,6 +61,8 @@ export interface SCADADataPanelProps { start_time?: string; /** 外部传入结束时间(ISO8601 字符串),用于初始化并触发查询 */ end_time?: string; + /** 方案分析运行 ID;方案数据必须显式指定,避免读取到其他页面残留的全局方案。 */ + runId?: string; /** 关闭面板 */ onClose: () => void; } @@ -73,6 +71,10 @@ type PanelTab = "chart" | "table"; type LoadingState = "idle" | "loading" | "success" | "error"; +const MAX_HISTORY_ELEMENTS = 200; +const MAX_HISTORY_ELEMENT_ID_LENGTH = 128; +const HISTORY_SCADA_CONCURRENCY = 4; + const panelHeaderActionSx = { color: "primary.contrastText", backgroundColor: "rgba(255,255,255,0.08)", @@ -81,51 +83,126 @@ const panelHeaderActionSx = { }, }; -/** - * 从后端 API 获取 SCADA 数据 - */ -const fetchFromBackend = async ( +const buildApiUrl = ( + path: string, + params: Record, +) => { + const searchParams = new URLSearchParams(); + Object.entries(params).forEach(([key, value]) => { + searchParams.set(key, String(value)); + }); + return `${config.BACKEND_URL}${path}?${searchParams.toString()}`; +}; + +const fetchOptionalJson = async (url: string, signal?: AbortSignal) => { + const response = await apiFetch(url, { signal }); + if (response.status === 404) return null; + if (!response.ok) { + throw new Error(`历史数据请求失败: HTTP ${response.status}`); + } + return response.json(); +}; + +const mapWithConcurrency = async ( + items: Input[], + limit: number, + mapper: (item: Input, index: number) => Promise, +): Promise => { + const results = new Array(items.length); + let nextIndex = 0; + const workerCount = Math.min(limit, items.length); + + await Promise.all( + Array.from({ length: workerCount }, async () => { + while (nextIndex < items.length) { + const currentIndex = nextIndex; + nextIndex += 1; + results[currentIndex] = await mapper(items[currentIndex], currentIndex); + } + }), + ); + + return results; +}; + +/** 从后端 API 获取管网元素的监测、实时模拟和方案模拟数据。 */ +export const fetchHistoryData = async ( featureInfos: [string, string][], range: { from: Date; to: Date }, type: "realtime" | "scheme" | "none", - scheme_type?: string, - scheme_name?: string + schemeRunId?: string, + signal?: AbortSignal, ): Promise => { if (featureInfos.length === 0) { return []; } + if (featureInfos.length > MAX_HISTORY_ELEMENTS) { + throw new Error(`历史数据一次最多查询 ${MAX_HISTORY_ELEMENTS} 个管网元素`); + } + if ( + featureInfos.some( + ([id]) => + id.trim().length === 0 || id.length > MAX_HISTORY_ELEMENT_ID_LENGTH, + ) + ) { + throw new Error("历史数据包含无效的管网元素 ID"); + } - // 提取设备 ID 列表 - const featureIds = featureInfos.map(([id]) => id); + const uniqueFeatureInfos = Array.from( + new Map(featureInfos.map((featureInfo) => [featureInfo[0], featureInfo])).values(), + ); + const featureIds = uniqueFeatureInfos.map(([id]) => id); - const feature_ids = featureIds.join(","); const start_time = dayjs(range.from).toISOString(); const end_time = dayjs(range.to).toISOString(); // 将 featureInfos 转换为后端期望的格式: id1:type1,id2:type2 - const feature_infos = featureInfos + const feature_infos = uniqueFeatureInfos .map(([id, type]) => `${id}:${type}`) .join(","); - // 监测值数据接口(use_cleaned=false) - const rawDataUrl = `${config.BACKEND_URL}/api/v1/timeseries/views/element-scada-readings?element_id=${feature_ids}&start_time=${start_time}&end_time=${end_time}&use_cleaned=false`; - // 清洗数据接口(use_cleaned=true) - const cleanedDataUrl = `${config.BACKEND_URL}/api/v1/timeseries/views/element-scada-readings?element_id=${feature_ids}&start_time=${start_time}&end_time=${end_time}&use_cleaned=true`; - // 模拟数据接口 - const simulationDataUrl = `${config.BACKEND_URL}/api/v1/timeseries/views/element-simulations?feature_infos=${feature_infos}&start_time=${start_time}&end_time=${end_time}`; - // 策略模拟数据接口 - const schemeSimulationDataUrl = `${config.BACKEND_URL}/api/v1/timeseries/views/element-simulations?feature_infos=${feature_infos}&start_time=${start_time}&end_time=${end_time}&scheme_type=${scheme_type}&scheme_name=${scheme_name}`; + const fetchElementScadaData = async (useCleaned: boolean) => { + const results = await mapWithConcurrency( + featureIds, + HISTORY_SCADA_CONCURRENCY, + (elementId) => + fetchOptionalJson( + buildApiUrl("/api/v1/timeseries/views/element-scada-readings", { + element_id: elementId, + start_time, + end_time, + use_cleaned: useCleaned, + }), + signal, + ), + ); + return Object.assign({}, ...results.filter(Boolean)); + }; + + const simulationDataUrl = buildApiUrl( + "/api/v1/timeseries/views/element-simulations", + { feature_infos, start_time, end_time }, + ); + + if (type === "scheme" && !schemeRunId) { + throw new Error("历史方案缺少分析运行 ID,无法读取方案时序数据"); + } + + const schemeSimulationDataUrl = schemeRunId + ? buildApiUrl("/api/v1/timeseries/views/element-simulations", { + feature_infos, + start_time, + end_time, + run_id: schemeRunId, + }) + : null; try { if (type === "none") { // 查询清洗值和监测值 const [cleanedRes, rawRes] = await Promise.all([ - apiFetch(cleanedDataUrl) - .then((r) => (r.ok ? r.json() : null)) - .catch(() => null), - apiFetch(rawDataUrl) - .then((r) => (r.ok ? r.json() : null)) - .catch(() => null), + fetchElementScadaData(true), + fetchElementScadaData(false), ]); const cleanedData = transformBackendData(cleanedRes, featureIds); @@ -143,18 +220,10 @@ const fetchFromBackend = async ( } else if (type === "scheme") { // 查询策略模拟值、实时模拟值、清洗值和监测值 const [cleanedRes, rawRes, simulationRes, schemeSimRes] = await Promise.all([ - apiFetch(cleanedDataUrl) - .then((r) => (r.ok ? r.json() : null)) - .catch(() => null), - apiFetch(rawDataUrl) - .then((r) => (r.ok ? r.json() : null)) - .catch(() => null), - apiFetch(simulationDataUrl) - .then((r) => (r.ok ? r.json() : null)) - .catch(() => null), - apiFetch(schemeSimulationDataUrl) - .then((r) => (r.ok ? r.json() : null)) - .catch(() => null), + fetchElementScadaData(true), + fetchElementScadaData(false), + fetchOptionalJson(simulationDataUrl, signal), + fetchOptionalJson(schemeSimulationDataUrl!, signal), ]); const cleanedData = transformBackendData(cleanedRes, featureIds); @@ -176,15 +245,9 @@ const fetchFromBackend = async ( } else { // realtime: 查询模拟值、清洗值和监测值 const [cleanedRes, rawRes, simulationRes] = await Promise.all([ - apiFetch(cleanedDataUrl) - .then((r) => (r.ok ? r.json() : null)) - .catch(() => null), - apiFetch(rawDataUrl) - .then((r) => (r.ok ? r.json() : null)) - .catch(() => null), - apiFetch(simulationDataUrl) - .then((r) => (r.ok ? r.json() : null)) - .catch(() => null), + fetchElementScadaData(true), + fetchElementScadaData(false), + fetchOptionalJson(simulationDataUrl, signal), ]); const cleanedData = transformBackendData(cleanedRes, featureIds); @@ -425,12 +488,11 @@ const emptyStateMessages: Record< const SCADADataPanel: React.FC = ({ featureInfos, type = "none", - scheme_type = "burst_analysis", - scheme_name, defaultTab = "chart", fractionDigits = 2, start_time, end_time, + runId, onClose, }) => { // 从 featureInfos 中提取设备 ID 列表 @@ -465,6 +527,7 @@ const SCADADataPanel: React.FC = ({ "raw" | "clean" | "sim" | "all" >(() => (featureInfos.length === 1 ? "all" : "clean")); const draggableRef = useRef(null); + const requestControllerRef = useRef(null); useEffect(() => { setActiveTab(defaultTab); @@ -503,6 +566,7 @@ const SCADADataPanel: React.FC = ({ const handleFetch = useCallback( async (reason: string) => { if (!hasDevices) { + requestControllerRef.current?.abort(); setTimeSeries([]); setLoadingState("idle"); setError(null); @@ -511,26 +575,43 @@ const SCADADataPanel: React.FC = ({ setLoadingState("loading"); setError(null); + requestControllerRef.current?.abort(); + const requestController = new AbortController(); + requestControllerRef.current = requestController; try { const { from: rangeFrom, to: rangeTo } = normalizedRange; - const result = await fetchFromBackend( + const result = await fetchHistoryData( featureInfos, { from: rangeFrom.toDate(), to: rangeTo.toDate(), }, type, - scheme_type, - scheme_name + runId, + requestController.signal, ); + if (requestControllerRef.current !== requestController) return; setTimeSeries(result); setLoadingState("success"); } catch (err) { + if ( + requestControllerRef.current !== requestController || + (err instanceof Error && err.name === "AbortError") + ) { + return; + } setError(err instanceof Error ? err.message : "未知错误"); setLoadingState("error"); } }, - [featureInfos, hasDevices, normalizedRange, type, scheme_type, scheme_name] + [featureInfos, hasDevices, normalizedRange, runId, type], + ); + + useEffect( + () => () => { + requestControllerRef.current?.abort(); + }, + [], ); // 设备变化时自动查询 diff --git a/src/components/olmap/core/Controls/Toolbar.tsx b/src/components/olmap/core/Controls/Toolbar.tsx index 5d52106..b6c1ccf 100644 --- a/src/components/olmap/core/Controls/Toolbar.tsx +++ b/src/components/olmap/core/Controls/Toolbar.tsx @@ -123,6 +123,7 @@ const Toolbar: React.FC = ({ const [chatPanelType, setChatPanelType] = useState< "realtime" | "scheme" | "none" >("none"); + const [chatPanelRunId, setChatPanelRunId] = useState(null); const [chatPanelTimeRange, setChatPanelTimeRange] = useState<{ startTime?: string; endTime?: string; @@ -142,6 +143,7 @@ const Toolbar: React.FC = ({ setHighlightFeatures, setChatPanelFeatureInfos, setChatPanelType, + setChatPanelRunId, setChatPanelTimeRange, setShowHistoryPanel, setShowStyleEditor, @@ -349,6 +351,7 @@ const Toolbar: React.FC = ({ setHighlightFeatures([]); } setChatPanelFeatureInfos(null); + setChatPanelRunId(null); setChatPanelTimeRange(null); break; } @@ -934,12 +937,14 @@ const Toolbar: React.FC = ({ { deactivateTool("history"); setActiveTools((prev) => prev.filter((t) => t !== "history")); diff --git a/src/components/olmap/core/Controls/ToolbarHistoryPanel.tsx b/src/components/olmap/core/Controls/ToolbarHistoryPanel.tsx index cfed455..21fac82 100644 --- a/src/components/olmap/core/Controls/ToolbarHistoryPanel.tsx +++ b/src/components/olmap/core/Controls/ToolbarHistoryPanel.tsx @@ -10,6 +10,7 @@ import HistoryDataPanel from "./HistoryDataPanel"; type ToolbarHistoryPanelProps = { showHistoryPanel: boolean; chatPanelType: "realtime" | "scheme" | "none"; + chatPanelRunId: string | null; chatPanelFeatureInfos: [string, string][] | null; chatPanelTimeRange: { startTime?: string; @@ -19,18 +20,21 @@ type ToolbarHistoryPanelProps = { HistoryPanel?: React.ComponentType; schemeName?: string; queryType?: string; + schemeRunId?: string; onClose: () => void; }; const ToolbarHistoryPanel: React.FC = ({ showHistoryPanel, chatPanelType, + chatPanelRunId, chatPanelFeatureInfos, chatPanelTimeRange, highlightFeatures, HistoryPanel, schemeName, queryType, + schemeRunId, onClose, }) => { const featureInfos = useMemo( @@ -75,8 +79,6 @@ const ToolbarHistoryPanel: React.FC = ({ return ( = ({ } start_time={chatPanelTimeRange?.startTime} end_time={chatPanelTimeRange?.endTime} + runId={chatPanelFeatureInfos ? chatPanelRunId ?? undefined : schemeRunId} onClose={onClose} /> ); diff --git a/src/components/olmap/core/Controls/useToolbarChatActions.ts b/src/components/olmap/core/Controls/useToolbarChatActions.ts index 994834d..4cd8d02 100644 --- a/src/components/olmap/core/Controls/useToolbarChatActions.ts +++ b/src/components/olmap/core/Controls/useToolbarChatActions.ts @@ -20,6 +20,7 @@ type UseToolbarChatActionsParams = { setHighlightFeatures: Dispatch>; setChatPanelFeatureInfos: Dispatch>; setChatPanelType: Dispatch>; + setChatPanelRunId: Dispatch>; setChatPanelTimeRange: Dispatch< SetStateAction<{ startTime?: string; endTime?: string } | null> >; @@ -37,6 +38,7 @@ export const useToolbarChatActions = ({ setHighlightFeatures, setChatPanelFeatureInfos, setChatPanelType, + setChatPanelRunId, setChatPanelTimeRange, setShowHistoryPanel, setShowStyleEditor, @@ -126,6 +128,7 @@ export const useToolbarChatActions = ({ case "view_history": { setChatPanelFeatureInfos(action.featureInfos); setChatPanelType(action.dataType); + setChatPanelRunId(action.runId ?? null); setChatPanelTimeRange({ startTime: action.startTime, endTime: action.endTime, @@ -136,6 +139,7 @@ export const useToolbarChatActions = ({ case "view_scada": { setChatPanelFeatureInfos(action.featureInfos); setChatPanelType("none"); + setChatPanelRunId(null); setChatPanelTimeRange({ startTime: action.startTime, endTime: action.endTime, @@ -249,6 +253,7 @@ export const useToolbarChatActions = ({ setChatPanelFeatureInfos, setChatPanelTimeRange, setChatPanelType, + setChatPanelRunId, setHighlightFeatures, setShowHistoryPanel, setShowStyleEditor, diff --git a/src/contexts/ProjectContext.test.tsx b/src/contexts/ProjectContext.test.tsx index 6734321..b18a477 100644 --- a/src/contexts/ProjectContext.test.tsx +++ b/src/contexts/ProjectContext.test.tsx @@ -85,7 +85,7 @@ describe("ProjectProvider authentication boundary", () => { expect(mockApiFetch).not.toHaveBeenCalled(); }); - it("restores and opens a saved project after authentication", async () => { + it("restores a saved project and reads its current metadata", async () => { seedSavedProject(); mockUseSession.mockReturnValue({ status: "authenticated" }); @@ -95,7 +95,10 @@ describe("ProjectProvider authentication boundary", () => { , ); - await waitFor(() => expect(mockApiFetch).toHaveBeenCalledTimes(2)); + await waitFor(() => expect(mockApiFetch).toHaveBeenCalledTimes(1)); + expect(mockApiFetch).toHaveBeenCalledWith( + "http://backend.test/api/v1/projects/current", + ); expect(mockSetCurrentProjectId).toHaveBeenCalledWith( "a2d67c84-fd9d-4feb-a500-c357244b2760", ); diff --git a/src/contexts/ProjectContext.tsx b/src/contexts/ProjectContext.tsx index 4cba7ac..6377e58 100644 --- a/src/contexts/ProjectContext.tsx +++ b/src/contexts/ProjectContext.tsx @@ -73,14 +73,6 @@ export const ProjectProvider: React.FC<{ children: React.ReactNode }> = ({ setIsConfigured(true); try { - const openResponse = await apiFetch( - `${config.BACKEND_URL}/api/v1/projects/current`, - { method: "POST" }, - ); - if (!openResponse.ok) { - throw new Error(`Failed to open project: HTTP ${openResponse.status}`); - } - const infoResponse = await apiFetch( `${config.BACKEND_URL}/api/v1/projects/current`, ); @@ -130,11 +122,13 @@ export const ProjectProvider: React.FC<{ children: React.ReactNode }> = ({ // If we have saved config, use it. if (savedWorkspace && savedNetwork) { - applyConfig( - savedProjectId || savedNetwork || savedWorkspace, - savedWorkspace, - savedNetwork, - savedExtent ? savedExtent.split(",").map(Number) : config.MAP_EXTENT, + void Promise.resolve().then(() => + applyConfig( + savedProjectId || savedNetwork || savedWorkspace, + savedWorkspace, + savedNetwork, + savedExtent ? savedExtent.split(",").map(Number) : config.MAP_EXTENT, + ), ); } }, [applyConfig, status]); diff --git a/src/generated/serverApi.ts b/src/generated/serverApi.ts index 3fb04ad..58b806c 100644 --- a/src/generated/serverApi.ts +++ b/src/generated/serverApi.ts @@ -2327,7 +2327,6 @@ export interface paths { * * Args: * query_time: 查询时间 - * network_name: 管网名称(或数据库名称) * timescale_conn: TimescaleDB连接 * * Returns: @@ -2625,66 +2624,6 @@ export interface paths { patch?: never; trace?: never; }; - "/api/v1/project-codes": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * 获取项目列表 - * @description 获取服务器上所有可用的供水管网项目名称列表。 - */ - get: operations["get_project_codes"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/v1/project-conversions": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * 转换 INP V3 为 V2 - * @description 将 EPANET 3.0 格式的 INP 内容转换为 2.x 格式。 - */ - post: operations["post_project_conversions"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/v1/project-copies": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * 复制项目 - * @description 将现有项目复制为新项目。 - */ - post: operations["post_project_copies"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; "/api/v1/project-managements": { parameters: { query?: never; @@ -2758,16 +2697,8 @@ export interface paths { */ get: operations["get_projects"]; put?: never; - /** - * 创建新项目 - * @description 创建一个新的供水管网项目。如果项目已存在,可能会覆盖或报错(取决于底层实现)。 - */ - post: operations["post_projects"]; - /** - * 删除项目 - * @description 永久删除指定的供水管网项目。此操作不可恢复。 - */ - delete: operations["delete_projects"]; + post?: never; + delete?: never; options?: never; head?: never; patch?: never; @@ -2786,16 +2717,8 @@ export interface paths { */ get: operations["get_projects_current"]; put?: never; - /** - * 打开项目 - * @description 将指定项目加载到内存中,并初始化数据库连接池。 - */ - post: operations["post_projects_current"]; - /** - * 关闭项目 - * @description 将指定项目从内存中卸载,释放资源。 - */ - delete: operations["delete_projects_current"]; + post?: never; + delete?: never; options?: never; head?: never; patch?: never; @@ -2841,114 +2764,6 @@ export interface paths { patch?: never; trace?: never; }; - "/api/v1/projects/current/exports/inp": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * 导出项目到 INP 文件 - * @description 将项目当前状态保存为 INP 文件到服务器文件系统。 - */ - post: operations["post_projects_current_exports_inp"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/v1/projects/current/files/inp": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * 下载 INP 文件 - * @description 从服务器数据目录下载指定的 INP 文件。 - */ - get: operations["get_projects_current_files_inp"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/v1/projects/current/imports": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * 读取 INP 文件到项目 - * @description 从服务器文件系统中读取指定的 INP 文件并加载到项目中。 - */ - post: operations["post_projects_current_imports"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/v1/projects/current/lock": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * 检查项目是否被锁定 - * @description 检查指定项目是否处于锁定状态。 - */ - get: operations["get_projects_current_lock"]; - put?: never; - /** - * 锁定项目 - * @description 锁定指定项目以防止并发修改。 - */ - post: operations["post_projects_current_lock"]; - /** - * 解锁项目 - * @description 释放对项目的锁定。 - */ - delete: operations["delete_projects_current_lock"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/v1/projects/current/lock/ownership": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * 检查项目是否被当前用户锁定 - * @description 检查指定项目是否被当前访问地址 (IP) 锁定。 - */ - get: operations["get_projects_current_lock_ownership"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; "/api/v1/projects/current/metadata": { parameters: { query?: never; @@ -2969,46 +2784,6 @@ export interface paths { patch?: never; trace?: never; }; - "/api/v1/projects/current/status": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * 检查项目是否已打开 - * @description 检查指定项目是否已被加载到内存中。 - */ - get: operations["get_projects_current_status"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/v1/projects/existence": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * 检查项目是否存在 - * @description 检查指定名称的项目是否存在。 - */ - get: operations["get_projects_existence"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; "/api/v1/pump-failure-events": { parameters: { query?: never; @@ -4269,23 +4044,6 @@ export interface paths { patch?: never; trace?: never; }; - "/api/v1/timeseries/analysis/runs/{run_id}/results": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** Store Analysis Results */ - post: operations["post_timeseries_analysis_runs_run_id_results"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; "/api/v1/timeseries/analysis/runs/{run_id}/values": { parameters: { query?: never; @@ -6097,6 +5855,50 @@ export interface components { */ time: string; }; + /** RealtimeLinkBatchItem */ + RealtimeLinkBatchItem: { + /** Flow */ + flow?: number | null; + /** Friction */ + friction?: number | null; + /** Headloss */ + headloss?: number | null; + /** Id */ + id: string; + /** Quality */ + quality?: number | null; + /** Reaction */ + reaction?: number | null; + /** Setting */ + setting?: number | null; + /** Status */ + status?: number | null; + /** + * Time + * Format: date-time + */ + time: string; + /** Velocity */ + velocity?: number | null; + }; + /** RealtimeNodeBatchItem */ + RealtimeNodeBatchItem: { + /** Actual Demand */ + actual_demand?: number | null; + /** Id */ + id: string; + /** Pressure */ + pressure?: number | null; + /** Quality */ + quality?: number | null; + /** + * Time + * Format: date-time + */ + time: string; + /** Total Head */ + total_head?: number | null; + }; /** RunSimulationManuallyByDateRest */ RunSimulationManuallyByDateRest: { /** @@ -6110,6 +5912,20 @@ export interface components { */ start_time: string; }; + /** ScadaReadingBatchItem */ + ScadaReadingBatchItem: { + /** Cleaned Value */ + cleaned_value?: number | null; + /** Device Id */ + device_id: string; + /** Monitored Value */ + monitored_value?: number | null; + /** + * Time + * Format: date-time + */ + time: string; + }; /** SchedulingAnalysisRest */ SchedulingAnalysisRest: { /** @@ -8754,7 +8570,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -9242,7 +9062,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -9400,7 +9224,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 201: { @@ -9714,7 +9542,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -9952,7 +9784,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -10109,7 +9945,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -10187,7 +10027,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -10665,7 +10509,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -10741,7 +10589,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -12114,7 +11966,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -12510,7 +12366,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 201: { @@ -12586,7 +12446,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 204: { @@ -12741,7 +12605,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -13452,7 +13320,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 201: { @@ -13528,7 +13400,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 204: { @@ -13602,7 +13478,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -13994,7 +13874,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -14228,7 +14112,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -17202,7 +17090,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 201: { @@ -17516,7 +17408,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -17592,7 +17488,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -19036,7 +18936,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -19584,242 +19488,6 @@ export interface operations { }; }; }; - get_project_codes: { - parameters: { - query?: { - limit?: number; - offset?: number; - }; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Page_str_"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - post_project_conversions: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - post_project_copies: { - parameters: { - query: { - /** @description 管网名称(或数据库名称) */ - source: string; - /** @description 管网名称(或数据库名称) */ - target: string; - }; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; post_project_managements: { parameters: { query?: never; @@ -20129,156 +19797,6 @@ export interface operations { }; }; }; - post_projects: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - delete_projects: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 204: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; get_projects_current: { parameters: { query?: never; @@ -20355,156 +19873,6 @@ export interface operations { }; }; }; - post_projects_current: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - delete_projects_current: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 204: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; get_projects_current_database_health: { parameters: { query?: never; @@ -20660,545 +20028,6 @@ export interface operations { }; }; }; - post_projects_current_exports_inp: { - parameters: { - query: { - /** @description 目标文件名 */ - inp: string; - }; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": boolean; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - get_projects_current_files_inp: { - parameters: { - query: { - /** @description 文件名 */ - name: string; - }; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - post_projects_current_imports: { - parameters: { - query: { - /** @description INP 文件名 (不包含路径) */ - inp: string; - }; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": boolean; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - get_projects_current_lock: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - post_projects_current_lock: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - delete_projects_current_lock: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 204: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - get_projects_current_lock_ownership: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; get_projects_current_metadata: { parameters: { query?: never; @@ -21275,158 +20104,6 @@ export interface operations { }; }; }; - get_projects_current_status: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; - get_projects_existence: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; post_pump_failure_events: { parameters: { query?: never; @@ -22238,7 +20915,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -22393,7 +21074,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -22469,7 +21154,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -22700,7 +21389,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 201: { @@ -22776,7 +21469,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 204: { @@ -22850,7 +21547,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -23972,7 +22673,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -24448,7 +23153,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -25556,7 +24265,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 201: { @@ -25709,7 +24422,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -25946,7 +24663,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -26179,7 +24900,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -26336,7 +25061,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -28187,7 +26916,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -28743,7 +27476,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -28976,89 +27713,6 @@ export interface operations { }; }; }; - post_timeseries_analysis_runs_run_id_results: { - parameters: { - query?: never; - header: { - "X-Project-Id": string; - }; - path: { - /** @description 分析运行 ID */ - run_id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": Record; - }; - }; - responses: { - /** @description Successful Response */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["JsonValue"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Insufficient permission */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Resource conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Validation error */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - /** @description Dependency unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ProblemDetails"]; - }; - }; - }; - }; get_timeseries_analysis_runs_run_id_values: { parameters: { query: { @@ -29312,7 +27966,7 @@ export interface operations { }; requestBody: { content: { - "application/json": Record[]; + "application/json": components["schemas"]["RealtimeLinkBatchItem"][]; }; }; responses: { @@ -29638,7 +28292,7 @@ export interface operations { }; requestBody: { content: { - "application/json": Record[]; + "application/json": components["schemas"]["RealtimeNodeBatchItem"][]; }; }; responses: { @@ -30214,7 +28868,7 @@ export interface operations { }; requestBody: { content: { - "application/json": Record[]; + "application/json": components["schemas"]["ScadaReadingBatchItem"][]; }; }; responses: { @@ -30872,7 +29526,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -32081,7 +30739,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -32477,7 +31139,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 201: { @@ -32553,7 +31219,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 204: { @@ -32785,7 +31455,11 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": Record; + }; + }; responses: { /** @description Successful Response */ 200: { diff --git a/src/store/chatToolStore.ts b/src/store/chatToolStore.ts index 6147ee7..51cdc51 100644 --- a/src/store/chatToolStore.ts +++ b/src/store/chatToolStore.ts @@ -26,6 +26,7 @@ export type ChatToolAction = type: "view_history"; featureInfos: [string, string][]; dataType: "realtime" | "scheme" | "none"; + runId?: string; startTime?: string; endTime?: string; }