feat: add project-scoped operations
Generic Container CI/CD / test-build-publish (push) Failing after 6s
Server CI/CD v2 / build-test-publish-and-deploy (push) Failing after 6s

This commit is contained in:
2026-10-22 18:40:20 +08:00
parent d320972d57
commit 1d061f6099
73 changed files with 4384 additions and 228 deletions
+65
View File
@@ -0,0 +1,65 @@
# 凤阳六月爆管模拟与 SCADA 复制
2026-10-22 已完成活动项目 `fengyang_v2`
(`b962c44e-8433-4a10-81e9-a64b70ac1152`)的三段模拟爆管数据及前一天基线复制。
一次性复制脚本及其专属测试已清理,来源、核对结果和备份记录如下:
| 方案名称 | 模型管段 | 模型管径 |
| --- | --- | --- |
| `2026-06-02 Pipe19026 DN200 burst` | `19026` | 200 mm |
| `2026-06-12 Pipe18751 DN160 burst` | `18751` | 160 mm |
| `2026-06-22 Pipe18750 DN160 burst` | `18750` | 160 mm |
方案名称、`burst_ID` 和 `network.pipes.diameter` 必须一致。时间使用方案的
`started_at` 和结果时间戳,历时为 4 小时,包含起止点,15 分钟一个采样。
不使用项目说明中的历史时间窗,也不使用旧的 `5hrs burst scheme` 数据。
源数据为 `analysis.node_results.pressure`,仅复制既有 50 个压力设备
`asset.scada_devices.node_id` 对应的节点。每段必须完整覆盖 17 个时间点,
即 850 条观测,三段共 2,550 条;方案状态必须为 `completed`。
压力源单位来自每个方案的 `parameters.result_units.pressure`,目标单位来自
设备的 `measurement_unit`,并用总水头减节点高程核对压力单位。缺失水头、
高程或单位不一致时拒绝写入。例如真正的 kPa 转米水头使用 `pressure / 9.80665`。
不添加噪声、不平移日期、不插值。写入 `scada.measurements.monitored_value`,
同时将窗口内对应观测的 `cleaned_value` 清空,避免使用旧观测的清洗结果。
其他日期、其他设备及模拟源数据保持原样;原有其他模拟事件也保留。
正常基线直接读取 `realtime.node_results`,保留其原始日期和压力值。每段在线
模拟采用右端不含的时间窗,4 小时共有 16 个采样点,最后一个点比窗口终点
早 15 分钟。三段共 2,400 条观测。基线源单位来自模型配置,并同样校验
总水头减高程;不从爆管结果或旧 SCADA 推导正常值。
| 管段 | 爆管数据时间(北京时间,含末点) | 前一天基线采样时间(北京时间) |
| --- | --- | --- |
| `19026` DN200 | 6 月 2 日 08:00–12:00 | 6 月 1 日 08:00–11:45 |
| `18751` DN160 | 6 月 12 日 12:00–16:00 | 6 月 11 日 12:00–15:45 |
| `18750` DN160 | 6 月 22 日 18:00–22:00 | 6 月 21 日 18:00–21:45 |
定位算法配对比较时应采用共同的 16 个相对采样时刻,或明确使用右端不含的
4 小时时间窗,避免将爆管结果额外的终点与不存在的基线终点配对。
2026-10-22 核查发现 EPANET 输出解析曾将压力枚举 1、2 的单位名称写反。
已改为 `0=PSI, 1=KPA, 2=MTR`,并通过本地原生 EPANET 输出测试验证。
这三个方案的全部 2,550 个压力值都与米制水头减高程一致,元数据已纠正为
`MTR`,原始模拟数值保持不变。SCADA 最终使用设备的 `m` 单位,无额外换算。
更正前的三个方案参数与核查证据保存在忽略目录
`data/fengyang-result-unit-correction/`。
本次最终复制记录为
`data/fengyang-burst-scada/20261022T101442415337Z/report.json`:
2,550 条爆管观测与 2,400 条前一天基线,共替换 4,950 条观测;SCADA 整表
仍为 144,000 条、50 个设备。窗口外指纹不变,独立 SQL 比对六段数据与源值
差异为零,重复只读预览显示 4,950 条均已一致。三个方案均为 `completed`。
本次执行前已锁定观测表并备份整表至忽略目录
`data/fengyang-burst-scada/<UTC时间>/measurements-before.csv`,同时保存
`prepared.json`(来源、设备映射、备份校验和)和 `expected.json`(预期观测)。
目录权限为 0700,文件权限为 0600,不包含数据库凭据。
本次复制在一个事务中完成,逐条比对预期值、核对总行数、校验窗口外数据
指纹不变。提交后使用新连接再次读回校验,已保存 `report.json`。
如需恢复,先核对对应报告的备份校验和,将 CSV 导入临时表,再仅恢复
`expected.json` 中的 `(time, device_id)` 键;不要整表覆盖当前观测。
数据库读回校验不代表已经完成 HTTP、界面回放或爆管定位算法验收。
+2 -2
View File
@@ -1,6 +1,6 @@
# Agent 业务分析 MCP
Agent 原生工具统一使用 `mcp_tjwater_` 前缀。Server目录共35项:25项查询、下表10项分析;合并6项宿主查询后共41项。工具参数来自Server实际MCP schema,不维护第二套参数契约。
Agent 原生工具统一使用 `mcp_tjwater_` 前缀。Server目录共67项:42项查询、12项计算、13项预览式写入;合并6项宿主查询后共73项。下表保留原10项分析,新增服务及边界见 [MCP项目服务](mcp-project-services.md)。工具参数来自Server实际MCP schema,不维护第二套参数契约。
| 工具名(不含前缀) | 能力与边界 | 必要权限 |
| --- | --- | --- |
@@ -54,7 +54,7 @@ Agent 原生工具统一使用 `mcp_tjwater_` 前缀。Server目录共35项:25
## 资源边界与发布
10项计算工具均提供 `timeout_seconds`,由Agent根据模型规模、模拟步数及已有耗时按次选择;它表示实际等待预算,与 `duration` 表示的模拟时间不同。未填写默认120秒。服务端上限由 `TJWATER_MCP_MAX_CALCULATION_TIMEOUT_SECONDS` 控制,默认3600秒,管理员可设120至86400秒;`tools/list` schema 发布实际有效上限,超限在计算前拒绝。
12项计算和13项预览式写入工具均提供 `timeout_seconds`,由Agent根据模型规模、模拟步数及已有耗时按次选择;它表示实际等待预算,与 `duration` 表示的模拟时间不同。未填写默认120秒。服务端上限由 `TJWATER_MCP_MAX_CALCULATION_TIMEOUT_SECONDS` 控制,默认3600秒,管理员可设120至86400秒;`tools/list` schema 发布实际有效上限,超限在计算前拒绝。
例如 `duration=3600, timeout_seconds=900` 表示模拟1小时、允许等待15分钟。Agent的上游HTTP/SDK预算为所选预算加5秒,整个宿主调用另留60秒处理认证、上下文绑定和清理;OpenCode连接上限从工具目录计算并写入宿主连接文件。本地MCP请求关闭Bun空闲断连,仍受上述调用截止时间控制。普通查询继续使用原30秒Server预算与35秒宿主预算。
+65
View File
@@ -0,0 +1,65 @@
# MCP 项目服务
当前 Server 有 67 项工具:42 项查询、12 项计算、13 项预览式写入。Agent 合并六项宿主查询后共 73 项。目录来自 `app/mcp/catalog.py`,Agent 使用独立的有限白名单并在启动时全等核对。工具前缀为 `mcp_tjwater_`。
## 新增能力
| 领域 | 工具(省略前缀) | 权限 |
| --- | --- | --- |
| 区域与连接 | `list_regions`, `get_region`, `get_region_nodes`, `get_node_connections` | `webgis.view` |
| 空间范围与骨干节点 | `query_network_extent`, `list_major_nodes` | `webgis.view` |
| 模型配置 | `get_model_configuration`, `list_model_configurations` | `webgis.view` |
| 项目地名 | `search_project_places`, `reverse_geocode` | `webgis.view` |
| 项目概况与导出 | `get_project_info`, `export_project_model` | `webgis.view` |
| 监测点交付 | `get_sensor_placement_candidate`, `export_sensor_placement` | `optimization.run` |
| 调度与运行历史 | `list_task_schedules`, `get_task_run_history` | `operational_task.view` |
| 需水量分配 | `calculate_demand_allocation` | `webgis.view` |
| 假设爆管水力模拟 | `run_burst_simulation` | `simulation.run` + `scada.view` |
| 模型编辑 | `edit_model_element`, `edit_model_configuration` | `webgis.edit` |
| SCADA 维护 | `clean_scada_readings`, `write_scada_readings`, `delete_scada_readings` | `scada.clean` |
| 正式分析成果 | `save_simulation_scenario` | `simulation.run` + `scada.view` |
| 正式监测点方案 | `create_sensor_placement`, `update_sensor_placement` | `optimization.run` |
| 任务与定时配置 | `create_operational_task`, `create_task_schedule`, `update_task_schedule`, `retry_operational_task`, `cancel_task_run` | `operational_task.write` |
| 写入状态核实 | `get_mutation_status` | `webgis.view`,仅当前身份与会话的执行记录 |
健康预测不在本次接入范围。报告发布、工单/设备动作的提议与审批、消息发送继续使用既有受控插件和业务动作流程。
## 数据与计算边界
- 所有查询使用当前授权项目连接。当前只有 TJWater 提供 GIS 信息;其他项目没有所需 GIS 数据或关系时返回空数据/明确不可用,不借用 TJWater 的图层或数据库。空页不能证明其他数据源没有对象。
- 范围查询直接用 PostGIS `ST_Intersects`,不调用旧的空占位服务。范围和输出坐标为 EPSG:4326,单轴范围最多一度。查询模型几何,不把空间邻近称为供水连通。
- 区域成员优先使用已配置节点关系,未配置时使用区域几何;配置、几何成员与真实供水拓扑需分别解释。
- 模型配置 `data_path` 选择嵌套字段,`limit/offset` 对选中集合分页。模式和曲线可单独列出 ID。编辑字段必须通过现有模型 schema 的可写属性校验;有限业务枚举不接受 SQL、任意路由、命令、身份、数据库地址或文件路径。
- 需水量分配按关联管长权重,只返回建议值,不修改节点需水量;输入输出沿用模型流量单位。区域或全网结果按节点 ID 稳定分页。
- 爆管模拟输入是已说明依据的破口面积(cm²),不是流量。复用隔离模型,`persist_results=False`;这与异常侦测和候选定位是不同工具。
- 保存正式场景会重新执行真实计算并持久化成果。压力调节和关阀正式成果使用宿主绑定的用户名。计算共用原有两项并发槽位,超时后槽位保留至原生线程退出。
- 新建任务限未绑定 Agent 的 operator/agent 事件调查;不能伪造外部工单或租约。重试只重新入队,实际执行仍由原有 Worker 领取。取消、启停和方案点位更新复用现有版本或旧值校验。
## 写入预览和原生许可
13 项新写入工具只对项目成员的 `ordinary_chat` 开放。任务执行、任务协作、只读 reader、个人会话和学习复盘均不能调用。每次校验当前权限及绑定时的权限上限;写入工具不加入只读自动许可名单,管理员拒绝继续优先。交互聊天遵循现有本轮 request/always 策略,always 不改变业务权限。
1. 为具体操作生成 UUID `request_id`,默认 `dry_run=true`,取得预览与 `expected_digest`。预览不运行模拟/优化,也不写业务数据。
2. 保持相同参数和 `request_id`,使用 `dry_run=false` 及返回的 `expected_digest` 请求执行,并经过宿主原生许可。
3. 服务重新读取目标并校验摘要。模型预览复核与修改共用模型变更锁;SCADA 操作在时序库串行化事务中复核相关数据。数据变化返回冲突,须重新预览。
4. 执行前在现有 `workbench.mcp_query_receipts` 保存 started 记录,完成后保存结果。同一会话、身份、项目和请求键的完成操作返回原结果,已启动但状态不明确的操作不会再次执行。可用 `get_mutation_status` 核实记录,并通过目标查询核实实际数据。超时/进程退出可能已有部分结果,不能解释为回滚,也不能自动换键重试。
计算容量已满且原生函数尚未调用时,返回 `CALCULATION_CAPACITY_EXCEEDED`。
这类明确未执行的请求记为 `rejected`、`retryable=true`;状态查询同时返回具体
错误。容量释放后可用原身份、会话、项目、参数和 `request_id` 重试,仍需通过
原生许可和预览摘要复核。重试时继续锁定请求记录,成功后的重复调用只返回
已保存结果。其他 503、算法异常、超时或取消仍保留 `started`,不能据此自动重试。
SCADA 维护必须明确列出设备,窗口最多 24 小时。清洗/删除最多 10000 条,补录/修正最多 500 条,来源依据必填;不开放 `all`。数据写入清洗值或实测表,不从模拟结果构造实测。涉及 BizDB 和 TimescaleDB 时不承诺跨库事务原子性,执行日志中的 started 与业务修改之间也不承诺分布式 exactly-once。
## 导出与会话下载
`export_project_model` 从数据库导出 ZIP(INP 或 ChangeSet,含顶点和 SCADA 元素),`export_sensor_placement` 导出已有方案 XLSX。单文件最多 5 MiB;模型通过 ZIP 压缩传输。Server 返回长度及 SHA-256;Agent 验证后只写当前会话 `workspace/files/`,返回相对路径和下载链接,模型消息不包含文件正文或宿主绝对路径。导出暂不支持超过该预算的文件分片。
下载接口为 Agent `GET /api/v1/session/{session_id}/files/{filename}`。它复用身份与项目校验,验证会话所有权,仅接受服务生成的文件名格式,拒绝符号链接和跨会话目录。前端通过已认证的 Agent client 下载;链接和磁盘文件均不是公开资源。文件随会话持久化和删除,不引入独立结果引用存储。
无需数据库迁移、新环境变量或新密钥。地名搜索沿用项目配置与已有天地图服务凭据。需同步发布 Server、Agent、前端,再重载 Server 与 Agent 工具目录;不要以代码目录一致代替线上重载验收。
## 验证
运行 focused pytest、Agent MCP/策略/导出测试及类型检查、前端协议与下载测试、浏览器下载覆盖及构建。另用隔离 MCP fixture 验证两端 SDK 目录、上下文绑定和原生工具调用。测试不运行真实项目模拟、优化、SCADA 清洗或任何业务写入;真实项目运算与数据效果仍须按项目验收。
+4 -4
View File
@@ -1,6 +1,6 @@
# 产品 MCP 查询:本地实施与启用
实现使用 MCP 2026-07-28、Python mcp 2.3.0、TypeScript @modelcontextprotocol/client 与 @modelcontextprotocol/server 2.3.1。Server 提供 `/mcp` 和35项工具(25项查询、10项受控分析);Agent 将其投影合并到本地 MCP,由 OpenCode 原生发现为 `mcp_tjwater_*`,普通项目会话、调查主 Agent 和已验证的直接只读子 Agent 共享25项查询;10项分析仅向非reader会话开放。个人会话不能获取项目凭据。旧 CLI 只作为能力清单,没有运行时 CLI 桥接。
实现使用 MCP 2026-07-28、Python mcp 2.3.0、TypeScript @modelcontextprotocol/client 与 @modelcontextprotocol/server 2.3.1。Server 提供 `/mcp` 和67项工具(42项查询、12项计算、13项预览式写入);Agent 将其投影合并到本地 MCP,由 OpenCode 原生发现为 `mcp_tjwater_*`,普通项目会话、调查主 Agent 和已验证的直接只读子 Agent 共享42项查询;12项计算仅向非reader会话开放,13项写入仅向项目成员的普通聊天开放。个人会话不能获取项目凭据。旧 CLI 只作为能力清单,没有运行时 CLI 桥接。
`run_simulation` 是在线重算,复用 REST 在线计算服务而不复制数据库;按水力步长更新模型条件和实时基线,不创建正式方案。MCP 时长为秒,原 REST 时长仍为分钟。其 annotation 为 `readOnlyHint=false`、`destructiveHint=true`;只读 reader 不能调用,其他方案模拟继续使用隔离临时模型。详细边界见 [MCP 分析工具说明](mcp-analysis-tools.md)。
@@ -76,11 +76,11 @@ TJWATER_MCP_CLIENT_SECRET=<由身份平台注入>
安装 Agent 更新的 Bun 依赖。本地 MCP 使用官方 Server SDK,独立监听127.0.0.1临时端口,先于原生工具发现就绪。宿主只把公开端点写入 `SESSION_METADATA_STORAGE_DIR` 的兄弟目录 `mcp-query/connection.json`,目录0700、文件0600;不保存令牌或 client secret。embedded 自动注入连接路径,OpenCode 环境仍剔除全部 `TJWATER_MCP_*`。client 模式须同网络空间、同版本可信插件、同一内部密钥,并共享 `TJWATER_AGENT_MCP_CONNECTION_PATH` 指向的连接文件,Agent 重启后同步重载原生连接。默认路径 `data/mcp-query/connection.json`。不要把 OAuth 配置注入外部 OpenCode。
项目及宿主只读工具统一由 MCP 提供,插件只配置原生连接与权限;额外六项是地理编码、会话搜索、调查任务列表与详情、消息读取、待办读取,合并后共41项。公开网页搜索使用 OpenCode 原生 `websearch`,搜索服务及凭据由 OpenCode 管理;Server 的 Bocha 接口、配置和 MCP 搜索入口已移除。`memory_manager` 和 `skill_manager` 保留完整读写插件,写入、展示工具保持插件形式。旧项目和任务查询工具及其内部 HTTP 查询桥已移除;未启用产品 MCP 时项目数据不可用,不回退。
项目及宿主只读工具统一由 MCP 提供,插件只配置原生连接与权限;额外六项是地理编码、会话搜索、调查任务列表与详情、消息读取、待办读取,合并后共73项。公开网页搜索使用 OpenCode 原生 `websearch`,搜索服务及凭据由 OpenCode 管理;Server 的 Bocha 接口、配置和 MCP 搜索入口已移除。`memory_manager` 和 `skill_manager` 保留完整读写插件,报告/消息/现场业务动作与展示工具保持插件形式;新增模型和数据维护写入通过预览式MCP提供。旧项目和任务查询工具及其内部 HTTP 查询桥已移除;未启用产品 MCP 时项目数据不可用,不回退。
原生调用的 `_meta["ai.opencode/sessionID"]` 仅用于关联宿主会话,本地服务先核验内部密钥和本机来源,再核验所有权及直接父子关系。原生工具发现、权限、元数据传递与待办 RPC 已由 `bun run tests/integration/query-mcp-smoke.ts` 验证,不调用模型。任务上下文保留可选 `report_revision`,缺省使用协作会话绑定修订号。隔离启动器显式设置自己的 `TJWATER_AGENT_MCP_CONNECTION_PATH`,两端共同读取该文件,不继承其他实例的连接文件。
6. 先用测试项目分别验证普通项目会话、worker 调查和直接 reader 子会话,再开启生产。Agent 启动先发现 Server 目录并与宿主35项白名单对齐,再验证 OpenCode 的真实工具 ID;失败不进入 ready。切换开关需重启 Agent。Server 未迁移项目的绑定接口返回503。
6. 先用测试项目分别验证普通项目会话、worker 调查和直接 reader 子会话,再开启生产。Agent 启动先发现 Server 目录并与宿主67项白名单对齐,再验证 OpenCode 的真实工具 ID;失败不进入 ready。切换开关需重启 Agent。Server 未迁移项目的绑定接口返回503。
client_credentials 与 token exchange 均发送标准 `resource=TJWATER_MCP_RESOURCE_URI`;exchange 同时保留配置的 audience。部署前必须验证身份平台接受该参数,且两种 grant 都将规范资源映射到 Server 要求的专用 audience。若所用 Keycloak 拒绝该参数,会报告配置不兼容,需升级或提供兼容适配;代码不会移除 resource 后重试。目录的服务 grant 与用户/worker 的 exchange 均不会退回共享管理员 Bearer或直接转发产品 Bearer给MCP。[Keycloak 官方说明](https://www.keycloak.org/securing-apps/token-exchange)。外部交互客户端的授权码流程和第三方客户端绑定不包含在本次交付中。
@@ -103,6 +103,6 @@ bun test tests/product/mcpAuthDiscovery.test.ts tests/product/mcpQueryClient.tes
MCP_TEST_PYTHON=<隔离Server环境Python> MCP_TEST_SERVER_ROOT=<Server仓库绝对路径> bun tests/integration/mcp-query-smoke.ts
```
HTTP smoke 只启动自己的 loopback fixture,使用真实两端 SDK,无生产数据库、Keycloak或模型推理。它验证新版发现、真实资源元数据 HTTP 读取、授权服务器元数据及带 resource 的两种 grant(后两者为桩)、35项工具、主机绑定、查询及撤销;真实环境验收记录见上方实际运行环境一节,不能用 fixture 结果代替。前端严格解析已接受这些 MCP 快照来源,未加入浏览器 MCP 凭据或新 UI。
HTTP smoke 只启动自己的 loopback fixture,使用真实两端 SDK,无生产数据库、Keycloak或模型推理。它验证新版发现、真实资源元数据 HTTP 读取、授权服务器元数据及带 resource 的两种 grant(后两者为桩)、67项工具、主机绑定、查询及撤销;真实环境验收记录见上方实际运行环境一节,不能用 fixture 结果代替。前端严格解析已接受这些 MCP 快照来源,未加入浏览器 MCP 凭据或新 UI。
查询限制回归使用 `python -m pytest tests/unit/test_mcp_query_limits.py tests/unit/test_mcp_query.py tests/unit/test_timeseries_history.py tests/unit/test_timeseries_history_schema.py -q`。实际 PostgreSQL SQL 验证可设置 `MCP_QUERY_TEST_DSN` 后运行 `python -m pytest tests/integration/test_mcp_query_limits_postgres.py -q`;该 DSN 必须指向空的、可丢弃的独立数据库,测试创建临时业务同名 schema 并在事务结束时回滚。它验证数据点跨序列翻页、证据路径/Unicode分页、任务摘要与指定报告、章节稳定路径、项目/任务隔离、既有scope边界、字节门槛和合成摘要,不代替真实项目验收。
+4 -1
View File
@@ -28,7 +28,8 @@ conda run -n server python scripts/run_operational_worker.py
| 变量 | 用途 |
| --- | --- |
| `OPERATIONAL_WORKER_PROJECT_ID` | 唯一授权项目 UUID |
| `OPERATIONAL_WORKER_PROJECT_ID` | 单项目模式的授权项目 UUID |
| `OPERATIONAL_WORKER_PROJECT_IDS` | 可选,显式授权项目 UUID 列表,逗号分隔;非空时优先于单项目配置,同步器和调查 worker 均按列表运行 |
| `OPERATIONAL_WORKER_SERVER_URL` | 主后端地址,默认本机 8000 |
| `OPERATIONAL_WORKER_AGENT_URL` | Agent 地址,默认本机 8787 |
| `OPERATIONAL_WORKER_RECONCILE_SECONDS` | SSE 中断或漏通知时的对账间隔,默认 30 秒 |
@@ -42,6 +43,8 @@ conda run -n server python scripts/run_operational_worker.py
工作身份必须能通过现有产品身份与项目成员权限校验。配置令牌获取不等于自动创建产品用户、项目成员或授权。权限错误会保留任务用于后续重试,不会绕过认证。
多项目模式为每个项目维护独立事件游标、会话、消息游标及 OAuth 缓存,单项目暂时失败不阻止其他项目对账。`OPERATIONAL_WORKER_MAX_CONCURRENCY` 是每个项目的上限,总上限随显式配置的项目数增加;需要控制整体模型用量时相应调低。进程不自动枚举所有项目或扩大服务身份权限。修改项目列表后需同时重启 `tjwater-operational-worker.service` 和 `tjwater-peripheral-sync.service`。
调查数据读取沿用账号已有的项目权限,不再因 `service` 身份额外移除 `scada.view` 和
`simulation.view`。上下文的 `approved_data_scopes` 与数据查询采用同一权限集合;已有读取
权限无需逐任务数据审批或委托凭据。此规则不新增角色权限、不放开跨项目读取,也不改变
+24
View File
@@ -4,6 +4,30 @@
浏览器通过 `TJWATER_PERIPHERAL_SAMPLE_API_BASE_URL` 连接样板服务。服务复用主系统的 Bearer 身份和 `X-Project-Id`,读取需要 `simulation.view`,写入需要 `simulation.run`。
生产环境不需要为外围服务新增域名。前端设置
`TJWATER_PERIPHERAL_SAMPLE_API_BASE_URL=/peripheral`,在现有前端 NPMplus Proxy Host 中增加以下
Custom Location:
| 配置项 | 值 |
| --- | --- |
| Location | `/peripheral/`,必须保留末尾斜杠 |
| Scheme | `http` |
| Forward Hostname / IP | NPMplus 能访问的应用机地址,通常与现有前端上游相同 |
| Forward Port | `8011` |
| Forward Path | 留空 |
NPMplus 会去掉 `/peripheral/` 前缀,因此浏览器请求
`/peripheral/api/sample/v1/context` 时,上游收到 `/api/sample/v1/context`。不要在 Forward
Hostname 后再追加 `/api/sample/v1`。外围服务应只向 NPMplus 所在网络开放 `8011`,不要直接开放公网。
本机开发和预览模式也提供等价的 `/peripheral` 代理,可通过
`TJWATER_PERIPHERAL_SAMPLE_UPSTREAM` 覆盖默认上游 `http://127.0.0.1:8011`。
外围 API 支持已授权项目切换。每次请求先由主系统验证项目成员身份和权限,再从 Metadata DB 解析该项目的 `biz_data` 路由;历史观测另解析 `iot_data` 路由。逻辑项目代码、业务物理库名和时序物理库名可以不同,连接沿用各自完整 DSN。路由缺失返回 `503 PROJECT_STORAGE_NOT_CONFIGURED`,不会回退到其他项目数据库。未配置存储表的项目仍需单独完成结构部署;API 不自动创建表或复制演示数据。
`PERIPHERAL_SAMPLE_PROJECT_CODE` 和 `PERIPHERAL_SAMPLE_DB_NAME` 仅供显式运行的种子、导出等脚本选择默认库,不再限制 HTTP 请求。工单变更在当前项目已有 `workbench.domain_event_log` 时于同一事务发布事件;未配置事件表时不自动建表,实时同步仍需项目工作台事件结构。
后台同步器和调查 worker 支持 `OPERATIONAL_WORKER_PROJECT_IDS`(项目 UUID,逗号分隔),每个项目维护独立凭据、事件游标和会话状态;未配置时继续使用原 `OPERATIONAL_WORKER_PROJECT_ID`。多项目配置只选择运行范围,不新增项目成员权限,服务身份必须已获各项目授权。修改后需重启两个常驻进程,参见 [工作进程配置](operational-task-workers.md)。
主要接口包括:
- `/api/sample/v1/context` 与 `/scenario`
+109
View File
@@ -0,0 +1,109 @@
# 项目地名搜索配置
地名搜索的项目配置保存在 Metadata DB 的 `public.projects.map_config.place_search`。
复用现有 JSONB 字段,不向各项目 BizDB 或 TimescaleDB 新增表。后端天地图 Key
仍通过 `TIANDITU_TOKEN` 提供,不写入项目地图配置或返回浏览器。
```json
{
"place_search": {
"enabled": true,
"provider": "tianditu",
"admin_code": "341126",
"region": "凤阳县"
}
}
```
`admin_code` 接受六位行政区编码或带 `156` 前缀的天地图编码。省级编码以
`0000` 结尾,市级编码以 `00` 结尾,其余按区县校验。`region` 是配置范围的
显示名称;区县级配置须与天地图的区县名称一致。市级搜索可返回不同区县的
候选,候选的 `district` 保留实际区县,响应的 `region` 返回配置范围。
配置缺失、关闭或无效时前端禁用查询;后端拒绝未配置项目的搜索请求。
前端启用条件、区域文案及结果校验均来自项目元数据,不再判断 GeoServer
工作空间。后端按经授权的 `X-Project-Id` 读取配置,搜索请求只接受 `keyword`,
调用方不能覆盖项目区域或分页限制。每次搜索最多返回 10 个候选。
## 管理接口
具备现有管理员权限的调用方可使用 `PATCH /api/v1/admin/projects/{project_id}`:
```json
{
"map_config": {
"place_search": {
"enabled": true,
"provider": "tianditu",
"admin_code": "310000",
"region": "上海市"
}
}
}
```
更新会合并 `map_config` 的顶层配置项,保留已有 `legend`、`coordinate_reference`
等字段。`place_search` 作为完整配置项替换;传 `null` 可清除此项,
`map_config` 自身不能为 `null`。项目元数据和管理接口均返回 `map_config`。
修改配置后刷新工作台以重新读取。
初始配置使用的一次性脚本已清理。后续通过上述管理接口维护项目配置;
初次写入前的逐项目行备份保存在忽略目录
`data/project-map-config/<project-id>-<timestamp>/before.json`。
## 2026-10-21 实际配置
| 项目代码 | 搜索范围 | 行政区编码 |
| --- | --- | --- |
| `tjwater_v2`、`tjwater` | 奉贤区 | `310120` |
| `fengyang_v2`、`fengyang` | 凤阳县 | `341126` |
| `md_v2`、`md` | 吴中区 | `320506` |
| `szh_v2`(苏州河) | 上海市 | `310000` |
| `zjb` | 湛江市 | `440800` |
共配置 8 条项目记录;3 个旧项目仍为 inactive。SZH 的 8 个实际模型节点经天地图
逆地理编码落在松江、青浦、闵行,因此采用上海市范围;木渎采用吴中区,湛江项目
采用市级范围,避免将近似配准的工程坐标当作区县边界依据。
配置前已备份完整元数据库并通过 `pg_restore --list` 校验:
`data/project-map-config/metadata-before-20261021T080257Z/system_hub.dump`。
SHA-256:`77dc13f874f81699a96d4584ac1f67f1b2048fe7c68175be8154c5e277c8689f`。
同时保留逐项目的行备份,核对除 `map_config` 与 `updated_at` 外所有字段保持原值,
已有图例、坐标参考和项目状态均保留。数据库字段注释同步为当前配置用途。
验证涵盖后端服务/接口/元数据配置、前端单元测试、奉贤桌面/窄屏搜索、凤阳项目
切换后搜索定位及未配置禁用。5 个活动项目均通过真实天地图搜索;浏览器测试使用
隔离接口数据,不代表部署环境的真实身份验收。Server OpenAPI 与供水 Agent 的
类型快照同步更新。配置已写入数据库,代码未提交、未推送或部署。
## 地图取点反编码
地址定位面板提供“地图取点”按钮。启用后点击地图查询该点附近的地名和完整地址,
首次查询成功后自动结束取点并保留结果和地图标记;失败时可重新取点。“结束取点”、
Escape、关闭工具或切换项目也会退出取点模式。
取点时暂停管网要素选择和绘图标注选择,并停止当前测量、绘图,退出后恢复常规选择。
后端接口为 `POST /api/v1/reverse-geocoding-requests`:
```json
{"longitude": 117.570309, "latitude": 32.868029}
```
坐标采用 EPSG:4326,复用授权项目的 `map_config.place_search` 和服务端
`TIANDITU_TOKEN`,无需另加数据库配置。返回规范化的 `PlaceSearchItem`,包括
`name`、`address`、`longitude`、`latitude`、`district`、`category` 与 `id`。
坐标始终保留输入的地图取点坐标,附近 POI 的坐标不替代点击位置。新结果显示标记,
不自动移动地图;用户可主动点击“重新定位”。连续取点取消前次请求,过期结果不能
覆盖新点;工具关闭、切换项目和退出取点时也会取消未完成请求。
反编码遵守相同的行政区范围校验:区县范围校验区县,市级范围允许所属不同区县。
范围外返回 422,无可用地址返回 404,服务异常或无效响应返回 502,配置缺失返回
503,超时返回 504。错误信息不包含服务商 Key 或带凭据的请求 URL。
使用天地图逆地理编码服务的 `postStr={"lon":...,"lat":...,"ver":1}` 与
`type=geocode` 参数。5 个活动项目均已用数据库实际配置通过真实接口查询,
并核对输出坐标保持输入值。桌面和 320px 浏览器测试使用隔离接口数据验证交互。
本次最终检查中,现有本地 `127.0.0.1:8000` 实例的 OpenAPI 请求连续两次超时。
真实天地图检查通过独立服务调用完成,浏览器使用隔离 API;既有运行实例的 HTTP
联调尚待该实例恢复响应后核对。本次未重启既有进程。
+18
View File
@@ -4,6 +4,24 @@
## 当前基线
### 2026-10-21 凤阳结构扩展
维护者明确要求将 `tjwater_v2` 的表结构和业务配置复制到 `fengyang_v2`,同时保留凤阳现有管网及数据。本次单独扩展凤阳,不向其他项目批量写入。以下旧迁移状态为当时记录,以本节的实际核对结果为准。
新增 `workbench` 的告警事件、告警状态、任务访问申请、任务进度、Agent 消息共 5 张表,补齐 `peripheral_sample` 的 8 张空表、`source_gis` 的 4 张空表及通用 `gis.water_quality_monitoring_points` 视图。同步主项目的约束、索引与业务表列注释,事件主题约束支持调查进度和展示事件。时序库的应用表结构已经一致,本次未迁移或写入时序数据。
本次迁移已完成,一次性执行脚本已清理。当时先生成差异报告,再做全库备份、核对 `pg_restore --list`、在强制回滚事务中演练,最后单事务应用。固定源、目标物理库为 `tjwater_v2` 和 `fengyang_v2`。生成的迁移 SQL、备份及审计报告保留供追溯;后续结构变更仍按本文门禁单独核对范围。
本次备份与审计记录位于忽略目录 `data/project-rollout/fengyang_v2-20261021T041825Z/`,包含 `before.dump`、`backup.list`、`migration.sql` 和 `report.json`。备份摘要:`f0f68f689239e3fde7fda10b6e7a0cbfdf4f8ee625ab87d3f714162af9391f87`。不提交备份或生成数据。
回滚演练及应用时均逐表核对已有 `network`、`asset`、`gis` 基础表的行数和无序内容摘要。凤阳的 31,143 个节点、31,219 条连接、50 个 SCADA 设备保持原状;未复制奉贤模型、原始 GIS、演示工单、历史结果、项目成员或地图范围。复查差异报告中缺失对象、约束差异和索引差异均为空。
外围 API 现按已验证项目的 Metadata DB 路由选库,不再固定为 `tjwater_v2`。事件生产事务排序锁在当前连接的项目库内生效,不创建数据库对象或业务数据;外围工单仅在该项目已配置事件表时发布事务事件。后台进程支持显式 `OPERATIONAL_WORKER_PROJECT_IDS` 列表,既有部署配置和服务身份成员权限未自动扩展。前端使用选中项目路由;上线后仍需按实际身份完成 Server/Agent 的跨项目联调验收。
已从当前前端配置的 GeoServer 核对两个工作空间的 WFS 数据。凤阳返回 31,137 个 junction、30,016 条 pipe,其余节点和连接类型仍使用凤阳原模型;这些分类数量与模型总节点/连接数量不是同一指标。
若结构迁移失败,事务直接回滚。若提交后的恢复需要使用 `before.dump`,先停止该项目写入并按既有数据库恢复流程处理,不能在仍有新业务写入时直接覆盖恢复。
截至 2026-09-24,`014_operational_tasks.sql` 和 `015_workbench_events_and_task_runs.sql` 已应用到 `tjwater_v2`、`szh_v2`、`md_v2`、`fengyang_v2` 和 `zjb`。这是已有基线,不代表后续迁移可以继续批量应用。
`017_workbench_schema_comments.sql` 只为 `workbench` schema 及其 17 张表和关键列增加数据库可见注释。它先在 `tjwater_v2` 应用;其他项目数据库保持原状,直到 `tjwater_v2` 验证通过并获得明确扩展确认。