# -*- coding: utf-8 -*- """版本与安装信息的**唯一真源**(2026-09-17 用户令 1:安装时检查已装版本、提示差异、问是否重装)。 为什么单开一个模块:以前版本号散在三处(`scripts/pack_dist.py` 的 `VERSION`、README 抬头、说明书标题), 改一处忘一处就会出现"包说 0.4.0、安装记录说 0.2.0"这种对不上的事。现在: src/version.py ← 唯一真源(本文件) scripts/pack_dist.py ← 读它写进 dist-manifest.json 与默认包名 install.ps1 / install.sh ← 读它写进 <安装目录>/install-info.json,并与已装的比对 guanlan.py / 各文档 ← 读它(文档里的人工版本号以它为准) `install-info.json` 落在安装根本身(不在 run/ 里):它是**交付物级别的安装记录**, 卸载/重装/拷机都要跟着走,所以和 `configs/`、`dist-manifest.json` 同级。 """ from __future__ import annotations try: from ._root import install_root as _install_root except ImportError: # 直接当脚本跑(python <本文件>)时没有包上下文 from _root import install_root as _install_root import datetime as dt import json import pathlib NAME = '观澜·如东样板 v2' VERSION = '2.84.1' # ★ 版本只改这里 EDITION = 'offline-single-package' PACKAGE_STEM = 'app_guanlang' # 交付包文件名前缀(用户令 2026-09-17) INSTALL_INFO = 'install-info.json' # 相对安装根 SERVICE_NAME = 'guanlan' # Windows 服务名 / systemd 单元名(用户令 1) SERVICE_DISPLAY = '观澜·如东样板 v2 (Guanlan Wind Asset Intelligence)' # ── 版本号规则(用户令 2026-09-17)──────────────────────────────────────────────── # 编号: v<大版本号>.<中版本号>.<小版本号> 例: v2.5.0 # 定义: 大版本号 —— 系统解决方案、架构或核心功能改变 # 中版本号 —— 非核心功能新增、减少、修改 # 小版本号 —— 消缺完善 # 包名: 打包文件名 = app_guanlang_v<版本号>.zip 例: app_guanlang_v2.5.0.zip # 用法: 用户/发布者定"这次算哪一级"→ VERSION 改一行 → 打包器与安装脚本自动跟着变 # (级别判据见 BUMP_RULE;一次发布混了几类就取最高那一级) RULE = 'v<大版本号>.<中版本号>.<小版本号>' LEVEL_MEANING = { 'major': '大版本号 —— 系统解决方案、架构或核心功能改变', 'minor': '中版本号 —— 非核心功能新增、减少、修改', 'patch': '小版本号 —— 消缺完善', } BUMP_RULE = ('改动落在"解决方案/架构/核心功能" → 大 +1(中/小归 0);' '落在"非核心功能的新增/减少/修改" → 中 +1(小归 0);' '只是"消缺完善" → 小 +1;一次发布混了几类,取最高那一级') # 版本记录(人读的那份由 scripts/version_log.py 生成到 docs/版本记录.md;这里只有事实,不重复描述) # level: major/minor/patch 表示这一版**相对上一版**是哪一级变化;legacy 表示该版用的是 # 旧编号体系(0.x,未按本规则),仅作历史对账用。 HISTORY: tuple[dict, ...] = ( dict(version='2.84.1', date='2026-10-05', level='patch', title='清产物/重算后工作台自动重取数(并加「刷新数据」);实测空数据各页显示空态', note='用户要求:**「清除产物」完成后,工作台相应功能(总览/电量算账/部件问题/振动融合分析/发电性能/故障统计)不应再有数据展示**。**实逮的问题**:这些页的数据来自 `/api/fleet`(按窗实时 ✓),但清除产物是在 `/ops` 控制台里点的 ⇒ **Vue 应用不会自动重新取数** ⇒ 页面仍显示清除前的数据。**修**:① `System.vue` 在 `/ops` iframe `load` 后(同源 ✓ 可直接访问其文档)监听 `#b_off`(清除产物)/`#b_rebuild`(执行重算)/`#b_start`/`#b_stop`/`#b_restart` 的点击,动作后 **4 秒**触发 `refresh`;② 重算块上方加**「刷新数据」**按钮(手动兜底);③ `Workbench` 透传、`App` 新增 `reload()` —— **清空页签级缓存 `apis`/`curves` 后重新 `load()` 与 `ensureTabApis(当前页签)`**。**验证(用空数据模拟"清完产物",不做破坏性操作)**:把 `/api/fleet` 返回置为空对象后,六个页签逐一渲染 ⇒ `overview elements=113 · energy elements=5(正文为「电量算账 无数据」)· component 21 · vibration 82 · generation 9 · fault 43`,**全部 `errs=0`(不崩)** ✓ —— 即清除后重取数时,各页会如实呈现"无数据/占位符"状态 ✓。'), dict(version='2.84.0', date='2026-10-05', level='minor', title='修「清除产物 404」:网关加 Path=/ops/api/** 专用路由(并实逮网关一直未真正重启)', note='用户反馈「系统维护」内嵌的 /ops 控制台里点「清除产物」报 **404 操作被拒绝**。**排查链**:该按钮调 **`/ops/api/<动作>`**(该页 API 前缀 `/ops/api/`);**直连 Python 网关 28086** ⇒ `/ops/api/state` **200 · 1,155 B**(正常);**经 Java 网关 28084** ⇒ **404**,响应体是 **Spring 的 `{"timestamp":…,"status":404}`** ⇒ 说明**没有路由匹配**、请求没到 28086。**实逮根因(两个叠加)**:① 路由表里 `ops → 28086` 的谓词是 `Path=/ops/**` + `Path=/ops`,但运行中的进程用的仍是**旧配置** ⇒ **网关根本没有真正重启**(旧 pid 50588 一直存活;此前几轮 `sc stop/start` 的回显都是空的、实际未生效);② 为稳妥起见还在路由表**最前**加了一条专用路由 **`ops-api` `Path=/ops/api/**` → 28086**。**修法**:改 classpath `application.yml` ⇒ **先 `taskkill` 旧 pid、再 `sc stop/start guanlan`、轮询确认端口换新 pid** ⇒ 实测 **旧 pid 50588 → 新 pid 53904** ✓,随后 `/ops/api/state` 经 28084 ⇒ **200 · 1,155 B** ✓(`/ops` 仍 200 ✓)。**判据**:`/ops/api/state` 经网关必须 200(直连本就是 200)✓ —— 达标。**教训(已记入)**:改配置后必须**确认进程换新 pid** 才算重启成功,回显为空不能当作已重启。'), dict(version='2.83.3', date='2026-10-05', level='patch', title='修「系统维护」数据重算按钮消失:内嵌改为直接 iframe src=/ops', note='用户反馈「系统维护」里的「清除产物」「数据重算」按钮又没了。**根因**:这两个按钮位于**内嵌的 `/ops` 运维控制台**内;该块此前用的是 Blob-URL iframe(与「振动 CMS」同一批尝试),在你的浏览器上不显示。而 `/ops` 的 `X-Frame-Options` 早在 **2.81.4** 已放开(网关 `frameOptions().disable()`,实测响应头无 DENY ✓)。**修**:把该块改回**最朴素、与 v2.9.2 门户同款**的写法 —— 直接 `