refactor(db)!: finalize pooled WNDB v2 migration

This commit is contained in:
2026-08-27 17:26:22 +08:00
parent fa188af0b1
commit b74799a39d
105 changed files with 4988 additions and 5565 deletions
+22 -20
View File
@@ -1,29 +1,31 @@
# TJWater 数据库改造说明与当前结构
> 本文记录 2026-08-25 的数据库实际状态。结构、约束、行数、TimescaleDB chunk 和策略均直接读取数据库,不以仓库中的 SQL 脚本为依据。文中不包含主机、端口、账号、密码或 DSN。
> 本文记录截至 2026-08-27 的数据库实际状态。结构、约束、行数、TimescaleDB chunk 和策略均直接读取数据库,不以仓库中的 SQL 脚本为依据。文中不包含主机、端口、账号、密码或 DSN。
## 改造范围与当前状态
本次改造保留原 `tjwater` 业务库和时序库,新建 `tjwater_next` 作为隔离验证环境。元数据库仍为 `system_hub`新项目通过 `biz_data``iot_data` 两条路由分别关联业务库与时序库。项目完成迁移和联调后已切换为 `active`,原数据库没有被覆盖,仍可用于对照和回退。
本次改造保留原 `tjwater` 业务库和时序库,新建的隔离库完成验证后已正式重命名为 `tjwater_v2`。元数据库仍为 `system_hub`逻辑项目 `tjwater_next` 通过 `biz_data``iot_data` 两条路由分别关联 `tjwater_v2` 业务库与时序库。项目已切换为 `active`,原数据库没有被覆盖,仍可用于对照和回退。
命名约定:正式物理库名为 `tjwater_v2`WNDB 版本模板固定为 `tjwater_v2_template`。元数据库中的逻辑项目代码和 GeoServer 工作空间仍为 `tjwater_next`;这些是路由与图层限定名,不是物理库名。模板已创建并清空项目模型、SCADA、分析运行和物化视图数据,仅保留数据库结构、PostGIS 对象和必要配置键种子,普通连接已关闭。
已经完成的数据库修改包括:
- 新建并迁移 `tjwater_next` 业务库,将旧 `public` 中混合存放的管网、GIS、SCADA 配置和分析数据按领域拆分。
- 新建并迁移 `tjwater_next` 时序库,将 SCADA、实时计算和分析计算结果分开存放。
- 新建并迁移 `tjwater_v2` 业务库,将旧 `public` 中混合存放的管网、GIS、SCADA 配置和分析数据按领域拆分。
- 新建并迁移 `tjwater_v2` 时序库,将 SCADA、实时计算和分析计算结果分开存放。
- `realtime` 采用冷热数据策略,72 小时后的 chunk 自动转为有序列存。
- `analysis``stored_at` 分区,入库满 24 小时的 chunk 自动转为有序列存。
- GIS 查询层改用物化视图,当前 7 张物化视图均已填充。
- GeoServer 已建立 `tjwater_next` 工作空间和同名数据存储,业务库 `gis` schema 发布 7 个图层。GeoWebCache 的服务端与客户端缓存有效期均为 300 秒。
- GeoServer 已建立 `tjwater_next` 工作空间和同名数据存储,数据存储连接 `tjwater_v2` 业务库 `gis` schema 发布 7 个图层。GeoWebCache 的服务端与客户端缓存有效期均为 300 秒。
- 旧库中的 `operation``current_operation``batch_operation``operation_table``restore_operation``snapshot_operation` 没有进入新业务库。
- `system_hub.public` 补充了项目数据库外键、数据库路由约束、连接池约束、必要的非空约束,以及 5 张表和 44 个字段的中文数据库注释。
- 用户角色和项目角色仍是可扩展字符串,没有增加枚举检查约束。
- `audit_logs.user_id``audit_logs.project_id` 仍为逻辑关联,没有增加外键。
- 业务库 48 个表或物化视图、192 个字段,以及时序库 7 张表、47 个字段均已写入中文数据库注释。
- 后端已对接新 schema。WNDB、PostgreSQL 管理连接和同步 TimescaleDB 访问均使用有界连接池,闲置项目按最近使用顺序回收,实时覆盖写入使用单一事务
- 后端已对接新 schema。WNDB、PostgreSQL 管理连接和同步 TimescaleDB 访问均使用有界连接池,闲置项目按最近使用顺序回收;动态异步池采用代际切换,配置更新不会中断旧借用或阻塞新请求
- 后端批量元素查询读取 GIS 物化视图,模型增删改和 INP 导入提交后执行并发刷新;批量事务只刷新一次。
- `pattern_values``pattern_flow_samples``curve_points``demands``link_vertices` 的顺序号按所属父对象编号,主键已改为父对象 ID 与 `sequence_no` 的复合键。
`tjwater_next` 当前为 `active`。排水项目 `lingang` 已迁入 `system_hub.public`,供水和排水后端共用同一套项目、成员、数据库路由和审计表。
逻辑项目 `tjwater_next` 当前为 `active`。排水项目 `lingang` 已迁入 `system_hub.public`,供水和排水后端共用同一套项目、成员、数据库路由和审计表。
## 数据库总体关系
@@ -38,9 +40,9 @@ flowchart LR
OT["tjwater 时序库<br/>scada / realtime / scheme"]
end
subgraph NEXT["隔离验证数据库"]
NB["tjwater_next 业务库<br/>network / gis / asset / analysis"]
NT["tjwater_next 时序库<br/>scada / realtime / analysis"]
subgraph V2["v2 正式数据库"]
NB["tjwater_v2 业务库<br/>network / gis / asset / analysis"]
NT["tjwater_v2 时序库<br/>scada / realtime / analysis"]
end
GS["GeoServer<br/>tjwater_next 工作空间"]
@@ -48,8 +50,8 @@ flowchart LR
MP -->|"tjwater 的 biz_data"| OB
MP -->|"tjwater 的 iot_data"| OT
MP -->|"tjwater_next 的 biz_data"| NB
MP -->|"tjwater_next 的 iot_data"| NT
MP -->|"项目 tjwater_next 的 biz_data"| NB
MP -->|"项目 tjwater_next 的 iot_data"| NT
MP -->|"lingang 的两条数据库路由"| DRAIN["排水项目数据库"]
NB -->|"gis 物化视图"| GS -->|"WFS / WMTS"| WEB
```
@@ -146,7 +148,7 @@ erDiagram
## 相对原数据库的结构变化
以下对比以当前仍保留的原业务库 `tjwater`、原时序库 `tjwater` 与新库 `tjwater_next` 的实际对象为准。对象名称的对应关系表示业务实体或数据职责的迁移方向,不表示所有字段均一对一复制。
以下对比以当前仍保留的原业务库 `tjwater`、原时序库 `tjwater` 与新库 `tjwater_v2` 的实际对象为准。对象名称的对应关系表示业务实体或数据职责的迁移方向,不表示所有字段均一对一复制。
### 归并和调整
@@ -167,7 +169,7 @@ erDiagram
- 云端操作记录相关的 `operation``current_operation``batch_operation``operation_table``restore_operation``snapshot_operation` 未进入新业务库。该设计不再作为业务数据模型的一部分。
- 临时处理表 `temp_link_1``temp_link_2``temp_node``temp_region``temp_vd_topology` 未迁入。它们属于历史处理过程的中间对象,不应成为长期库结构。
- 原库中的 `_node``_link``_pattern``_curve``_region` 等内部或过渡表不再单独存在。新库以明确的领域表和外键关系表达同一类数据。
- 原库的 `tiger``topology` 扩展 schema 未在新库安装,`tjwater_next` 仅保留 PostGIS 及其 `public` 系统对象。
- 原库的 `tiger``topology` 扩展 schema 未在新库安装,`tjwater_v2` 仅保留 PostGIS 及其 `public` 系统对象。
### 尚未完整承接的范围
@@ -183,7 +185,7 @@ erDiagram
- `realtime` 两张 hypertable 已启用 72 小时后的列存压缩策略,`analysis` 两张 hypertable 按入库时间执行 24 小时冷热转换。`scada` 保持独立行存。
- 元数据库的 `public.project_databases` 增加项目外键、数据库角色和类型约束,以及连接池上下限约束,用于保证每个项目的业务库和时序库路由有效。
## 新业务库 tjwater_next
## 新业务库 tjwater_v2
新业务库使用 PostgreSQL 和 PostGIS。业务对象分布在 4 个 schema 中,`public` 只保留 PostGIS 提供的系统对象。
@@ -316,7 +318,7 @@ flowchart LR
### GeoServer 与前端图层
`system_hub.public.projects` 中的 `tjwater_next` 项目已配置 `gs_workspace=tjwater_next`,当前状态为 `active`。GeoServer 的 `tjwater_next` 数据存储连接同名业务库并限定到 `gis` schema,图层名称直接采用物化视图名称。7 个图层使用相同的项目管网发布边界,空图层和视口内没有要素的瓦片会返回空 MVT,不会产生越界错误。
`system_hub.public.projects` 中的 `tjwater_next` 项目已配置 `gs_workspace=tjwater_next`,当前状态为 `active`。GeoServer 的 `tjwater_next` 数据存储连接 `tjwater_v2` 业务库并限定到 `gis` schema,图层名称直接采用物化视图名称。7 个图层使用相同的项目管网发布边界,空图层和视口内没有要素的瓦片会返回空 MVT,不会产生越界错误。
| 前端数据源 | GeoServer 图层 | 几何 | 当前要素数 |
| --- | --- | --- | ---: |
@@ -384,7 +386,7 @@ erDiagram
这些表均为行存,没有启用压缩策略。方案结果使用 `scheme_name` 关联业务库中的方案记录。
## 新时序库 tjwater_next
## 新时序库 tjwater_v2
新时序库仍运行 TimescaleDB 2.21.3。业务时序数据分为 `scada``realtime``analysis`,迁移过程状态单独放在 `migration`
@@ -404,7 +406,7 @@ erDiagram
### realtime
`realtime.node_results``realtime.link_results` 保存当前实时计算窗口。主键保证同一时刻、同一元素只能有一条记录。相同时间窗口由后端先删除、再通过 `COPY` 批量插入;节点和连接两次替换位于同一个最外层事务,其中任一步失败都会整体回滚
`realtime.node_results``realtime.link_results` 保存当前实时计算窗口。主键保证同一时刻、同一元素只能有一条记录。每个批次强制只包含一个归一化时间戳;相同时间由后端在一个最外层事务中先同时删除节点和连接旧快照,再通过 `COPY` 写入非空侧。某一侧为空也会删除其旧数据,符合“同时间点整体覆盖”语义
两张表当前各有 19 个 chunk,均已转为列存,因为现有数据都早于 72 小时热窗口。数据库每小时执行一次策略检查,将 72 小时以前的 chunk 转为有序列存。节点结果按 `node_id, time DESC` 排序,连接结果按 `link_id, time DESC` 排序。新写入的数据使用 1 天 chunk,并在 72 小时内保持行存。
@@ -422,9 +424,9 @@ erDiagram
## 后端连接与事务
元数据库通过 SQLAlchemy 异步连接池访问;项目请求按 `system_hub.public.project_databases` 路由到业务库和时序库。异步业务查询和异步时序查询由项目级动态池管理,原生 WNDB 同步访问使用按数据库缓存的 `psycopg_pool.ConnectionPool`,数据库创建、复制和删除使用独立的 PostgreSQL 管理池,同步 TimescaleDB 访问也使用按数据库缓存的连接池。应用目录中已没有直接调用 `psycopg.connect` 的业务代码。
元数据库通过 SQLAlchemy 异步连接池访问;项目请求按 `system_hub.public.project_databases` 路由到业务库和时序库。异步业务查询和异步时序查询由项目级动态池管理,池条目记录借用数;配置更新建立新一代池,旧池只在已有借用归还后关闭。原生 WNDB 同步访问使用按数据库缓存且同样带借用计数`psycopg_pool.ConnectionPool`,数据库创建、复制和删除使用独立的 PostgreSQL 管理池及数据库级 advisory lock,同步 TimescaleDB 访问也使用按数据库缓存的连接池。应用目录中已没有直接调用 `psycopg.connect` 的业务代码。
WNDB 批量修改和 INP 数据导入在同一条池连接和同一事务中执行,提交后刷新 GIS 物化视图。实时节点和连接结果在一个事务中执行先删后写,同一结果时间使用事务级锁避免并发覆盖竞态;分析结果按 `run_id` 加事务级锁,防止同一运行被并发写入两次
WNDB 批量修改和模拟参数准备在同一条池连接和同一事务中执行,提交后刷新一次 GIS 物化视图。普通写入和 INP 整体替换使用同一项目级事务锁;INP 先在唯一暂存库校验,再从一致性快照事务替换当前库的 `network/gis` 模型表。临时分析库由固定模板建结构后复制当前项目模型、SCADA 映射并刷新视图。实时节点和连接结果在一个事务中执行整体先删后写,同一结果时间使用事务级锁;分析结果按 `run_id` 加事务级锁。Timescale 复合查询按节点/管段批量读取,SCADA 清洗使用单条集合更新,不再逐点往返
自动化真实数据库测试分别执行 64 次业务库和 64 次时序库并发借用,查询结果一致,连接均能归还池中。嵌套 WNDB 写入和分析运行生命周期测试会在外层强制回滚,数据库没有残留记录。`DatabaseCommand` 的 pattern 新增、修改、删除也在同一池化事务中完成,并验证了五张明细表的复合主键、级联解除需求模式关联、结果变更和整体回滚。实时覆盖测试确认第二批数据替换第一批数据,外层回滚后测试记录为 0。
+41 -9
View File
@@ -6,7 +6,9 @@
当前结构适合继续维护。WNDB 已按连接基础设施、管网模型、GIS、INP 和命令执行分组,原来的编号文件名、根目录聚合门面和星号导入已经移除。WDA、SCADA 资产查询和测压点选址也已离开底层模型目录。
本次调整了代码文件、导入关系、命令分派和 WNDB 内部命令对象,没有改变 HTTP 接口。历史撤销日志已从数据库中移除,内部接口不再保留无效的兼容字段。真实库回归时发现五张明细表错误地把局部顺序号设成全局主键,已在 `tjwater_next` 中改为父对象 ID 与 `sequence_no` 的复合主键。
本次调整了代码文件、导入关系、命令分派、项目生命周期接口和 WNDB 内部命令对象。无状态服务不再发布“打开、关闭、是否打开项目”三个旧 HTTP 操作,数据库连接在请求中按需从池借用。历史撤销日志已从数据库中移除,内部接口不再保留无效的兼容字段。真实库回归时发现五张明细表错误地把局部顺序号设成全局主键,已在 `tjwater_v2` 中改为父对象 ID 与 `sequence_no` 的复合主键。
`tjwater_v2` 是 v2 业务库和时序库的正式物理库名。元数据库中的逻辑项目代码仍为 `tjwater_next`,由项目路由指向 `tjwater_v2`;两者不必同名。版本模板固定为 `tjwater_v2_template`,不按项目代码动态派生。目前该模板已从实际 v2 结构创建、清空项目数据、刷新空物化视图并封存,压缩后约 19 MB。
## 当前目录
@@ -16,6 +18,7 @@ app/native/wndb/
├── core/
│ ├── connection.py
│ ├── database.py
│ ├── model_replace.py
│ └── projects.py
├── model/
│ ├── elements.py
@@ -28,6 +31,8 @@ app/native/wndb/
│ ├── patterns.py
│ ├── curves.py
│ ├── options.py
│ ├── options_v2.py
│ ├── options_v3.py
│ └── 其他 EPANET 模型模块
├── gis/
│ ├── coordinates.py
@@ -35,6 +40,7 @@ app/native/wndb/
│ ├── labels.py
│ ├── backdrop.py
│ ├── regions.py
│ ├── network_views.py
│ └── region_geometry.py
├── inp/
│ ├── sections.py
@@ -46,7 +52,7 @@ app/native/wndb/
└── executor.py
```
目录内共有 47 个 Python 文件,约 7,100 行`app/native/wndb/__init__.py` 只保留包说明,不再统一导出所有函数。调用方需要从具体职责模块导入,依赖来源可以直接从文件头确认。
目录内共有 49 个 Python 文件。`app/native/wndb/__init__.py` 只保留包说明,不再统一导出所有函数。调用方需要从具体职责模块导入,依赖来源可以直接从文件头确认。
## 各目录的职责
@@ -56,27 +62,44 @@ app/native/wndb/
`database.py` 提供 `ChangeSet``DatabaseCommand`、参数化查询和物化视图刷新。`DatabaseCommand` 只保存待执行的 SQL 和执行成功后返回给调用方的变更列表,不再生成或保存撤销 SQL。模型直接修改时按需刷新视图;批量命令在外层事务提交后只刷新一次。物化视图保留模型坐标 `x``y`,同时将供 GeoServer 使用的 `geom` 转换为 `EPSG:3857`,WNDB 查询不会把发布坐标误当成模型坐标。
`projects.py` 只负责项目数据库的创建、复制、打开、关闭和删除,不再混入模型查询
`projects.py` 只负责项目数据库的创建、复制、删除和异常安全的临时库上下文,不再保存“项目已打开”状态,也不混入模型查询。`postgres`、模板库、旧 WNDB 模板库 `project` 和元数据库均属于保护对象;模板复制源只能精确匹配 `WNDB_TEMPLATE_DB_NAME`,不能重新引入每项目 `_template`。批量清理不再扫描并删除服务器上的未知数据库,调用方必须显式提供每一个目标库名。数据库级 advisory lock 与 `datallowconn` 共同串行化多 worker 下的复制和删除。普通项目复制若复制源仍有其他会话会直接失败,不再主动终止正常请求
`model_replace.py` 在源库可重复读快照中读取 `network``gis` 基表,并按外键拓扑顺序复制到目标业务库。替换在单一事务内完成,不再删除并重建整个业务库;普通模型修改和整体替换共用同一项目级事务锁。INP 替换时,`analysis.results` 保留历史记录,只有新模型中不存在的元素引用会置空,`asset.scada_devices` 保留仍能匹配新节点或管段的设备;临时分析库则从当前项目复制模型和有效 SCADA 映射。
### model:管网模型和仿真配置
`model` 按业务实体命名,不再使用 `s2_junctions.py` 这类 INP 章节编号。节点、连接、模式、曲线、需求、规则和仿真设置都能从文件名直接定位。
`options_v2.py``options_v3.py` 分别负责 EPANET V2、V3 的 `[OPTIONS]` 章节导入导出;数据库中的 `engine_version = 'legacy'` 仍表示 V2 配置,仅作为现有存储标识保留。
每个实体模块保留三类紧密相关的函数:读取实体、生成并执行实体变更、转换该实体对应的一行或一段 INP 内容。完整文件的读取顺序、事务和项目生命周期由 `inp` 目录负责。因此,实体级编解码仍靠近实体定义,跨章节编排已经集中。
`elements.py` 保存节点、连接、模式、曲线和区域的通用类型判断及拓扑查询。它不再承担业务算法。
### gis:空间数据和区域几何
`coordinates.py``vertices.py``labels.py``backdrop.py` 对应原始 GIS 数据。`regions.py` 负责区域持久化,`region_geometry.py` 负责边界、凸包、膨胀和区域内元素查询。
`coordinates.py``vertices.py``labels.py``backdrop.py` 对应原始 GIS 数据。`regions.py` 负责区域持久化,`region_geometry.py` 负责边界、凸包、膨胀和区域内元素查询。`network_views.py` 是 GIS 查询视图的只读适配层,负责统一节点、链路、拓扑和需求投影的批量读取。坐标读取不会补写默认几何;缺少坐标时仅向调用方返回 `(0, 0)`,真实坐标的初始化仍由新增、导入或修复命令负责。
管网实体修改会调用坐标 SQL 辅助函数,区域几何也会读取管网拓扑。这里存在明确的模型与 GIS 协作,但没有模块导入环。现阶段继续拆出抽象接口只会增加层级,没有实际收益。
### GIS 统一查询视图
`tjwater_v2` 以现有 GIS 物化视图为基础增加了两个不保存重复数据的普通视图:
| 视图 | 来源 | 用途 |
| --- | --- | --- |
| `gis.network_nodes` | `gis.junctions``gis.reservoirs``gis.tanks` | 统一返回 `id`、模型坐标 `x/y``node_type` |
| `gis.network_links` | `gis.pipes``gis.pumps``gis.valves` | 统一返回 `id`、起止节点和 `link_type` |
普通视图始终读取底层物化视图当前内容,不需要单独刷新。管网修改提交后仍由 `gis.refresh_all_materialized_views` 刷新节点、管道、泵、阀门等物化视图,两个统一视图会随之得到最新结果。视图和全部字段均已在数据库中写入说明。
旧服务先读取节点或链路 ID,再逐个查询坐标、类型和端点,会对完整管网产生数万次往返。当前节点坐标、主干节点、全部链路、主干管道、区域边界链路、区域拓扑和用水量汇总均改为视图批量查询。没有调用方的 `get_nodes_in_extent``get_links_in_extent`,以及仅服务于旧聚合过程的四个模型辅助函数已删除;单元素详情接口仍保留原始模型查询。
### inp:文件级导入导出
`sections.py` 只保存 INP 章节名称和输出顺序。旧文件中混放的 `s1_title``s2_junction` 等命令类型常量已经移除。
`importer.py` 负责文件分段、导入顺序、项目事务、版本转换和导入后的物化视图刷新。`exporter.py` 负责按 EPANET 版本组织各章节并写出文件或 `ChangeSet`
`importer.py` 负责文件分段、导入顺序、项目事务、版本转换和导入后的物化视图刷新。INP 更新先从 `tjwater_v2_template` 创建唯一暂存库并完成解析,再在当前业务库中事务替换模型表。模型提交后即使暂存库清理失败也仍会刷新物化视图;清理失败会记录日志,不再遮蔽主操作。ChangeSet 导入使用每请求唯一临时文件并在 `finally` 删除,避免同项目并发导入互相覆盖。`exporter.py` 负责按 EPANET 版本组织各章节并写出文件或 `ChangeSet`
### commands:批量修改和级联关系
@@ -100,7 +123,9 @@ flowchart LR
EXECUTE --> COMMIT --> REFRESH
```
实体模块根据请求生成 `DatabaseCommand`,其中 `sql` 是要执行的语句,`changes` 是成功后的变更结果。批量命令先在同一个项目事务中展开级联关系,再依次执行 SQL;任何一步失败都会回滚整个事务。事务提交后统一刷新物化视图,避免一次批量修改触发多次刷新。直接调用单个实体修改时,如果当前没有外层项目事务,则由 `execute_command` 完成提交并按影响范围刷新视图。
实体模块根据请求生成 `DatabaseCommand`,其中 `sql` 是要执行的语句,`changes` 是成功后的变更结果。批量命令先在同一个项目事务中展开级联关系,再依次执行 SQL;任何写入步骤失败都会回滚整个事务。事务提交后统一刷新物化视图,避免一次批量修改触发多次刷新。直接调用单个实体修改时,如果当前没有外层项目事务,则由 `execute_command` 完成提交并按影响范围刷新视图。
物化视图采用并发刷新,因此刷新位于模型事务提交之后。若刷新失败,模型修改已经持久化,代码会抛出 `MaterializedViewRefreshAfterCommitError`。HTTP 层返回专用 Problem Details、`503``X-TJWater-Changes-Committed: true`,明确提示不能盲目重放原始写入。
旧实现中的 `DbChangeSet` 同时保存执行和撤销两套 SQL、两套变更结果,但数据库已经不再提供 operation 或 snapshot 撤销日志,这些字段没有消费者。当前代码已经删除 `undo_sql``undo_cs` 及各实体模块中的撤销 SQL 构造,也删除了只为撤销结果读取旧记录的查询和辅助方法。局部修改仍会读取一次当前记录,用于补齐请求中未提供的字段;这类读取属于更新语义,不是撤销机制。
@@ -117,6 +142,12 @@ flowchart LR
`tjnetwork.py` 从约 995 行缩减到约 320 行。它不再通过 WNDB 根包获得全部函数,只显式导入当前服务使用的能力。过时的 `scripts/test_tjnetwork.py` 依赖已移除的 operation、snapshot、DMA 和旧 SCADA API,已经一并删除。
### HTTP 执行边界
WNDB 当前使用同步 `psycopg` 连接池。`network/``components/` 和同步 EPANET 仿真接口统一声明为同步处理函数;公开 REST 路由的异步适配器把这些函数送入线程池,并把项目路由上下文传入工作线程,不会在事件循环线程上阻塞数据库或求解器。异步业务库和时序库访问使用带借用计数和代际切换的项目池:活跃旧池不会被 LRU 淘汰或强制关闭,配置变化后新请求立即使用新池,旧池在已有借用归还后关闭。元数据库保持独立 SQLAlchemy 异步池。
临时分析库先由固定 `tjwater_v2_template` 提供结构,再从当前项目的一致性快照复制 `network/gis` 模型与 SCADA 映射并刷新物化视图。V3→V2 格式转换不需要项目模型,单独使用空模板临时库。旧 `online_Analysis.py`、restore 和 open/close 项目脚本已经删除,不再保留每项目模板与 operation 恢复入口。
## 依赖方向
```mermaid
@@ -162,7 +193,8 @@ WNDB 根包不再作为依赖汇聚点。上层若只需要管道查询,应直
## 验证结果
- 本地 conda 环境全量测试:239 项通过,10 项按条件跳过。
- Docker 镜像构建成功,镜像内全量测试结果一致
- `tjwater_next` 真实数据库测试:8 项通过,覆盖业务库和时序库并发借用、嵌套事务回滚、分析运行生命周期、恶意标识符转义、五张明细表的复合主键,以及 WNDB pattern 增删改、级联解除需求关联和整体回滚
- 本地 conda 环境单元、鉴权和 API 测试:286 项通过,2 项按条件跳过。
- 一次性实库从 `tjwater_v2_template` 创建后,通过 INP 暂存解析和事务替换得到 11 个节点、13 条连接、11 条坐标及 9 条 junction 物化视图记录,验证后已完整删除
- `tjwater_v2` 统一视图覆盖 87,907 个节点和 91,054 条链路,与六个来源物化视图的合计数量一致。实测完整节点读取约 0.17 秒、完整链路读取约 0.10 秒、完整拓扑两次批量查询约 1.12 秒;耗时仅作为当前环境基线,不作为固定性能承诺
- `tjwater_v2` 真实数据库测试:11 项通过,覆盖业务库和时序库并发借用、失效连接自动重建、临时库模型/SCADA/视图完整克隆与清理、嵌套事务回滚、分析运行生命周期、恶意标识符转义、明细表复合主键、统一 GIS 查询视图,以及 WNDB pattern 增删改、级联解除需求关联和整体回滚。
- Python 编译、未使用导入扫描、撤销字段残留扫描和 `git diff --check` 均通过。