文档定位

基于 Advanced Installer 19.x 构建,最终产品为 EasyPro.exe。本文档同步至 V2.1.1 实际代码:MySQL 前置检查、失败继续策略、动态端口回填、SQL 多文件、版本号两层写入。

1
概述
1.1 项目目标

提供从已安装的智控平台旧版本向新版本无缝升级的能力,不涉及首次安装场景。

核心区别:升级 ≠ 重装

升级包不重新部署完整组件,而是通过 资源_{版本号} 文件夹中的增量升级脚本,按版本顺序依次执行。资源/ 目录(完整安装资源)在升级过程中不被使用。

1.2 产品标识
字段值
产品名称智控平台
UpgradeCode{4A49D8CC-5A22-466E-A867-6E95AD659547}(与安装包相同)
升级包文件名EasyPro.exe
语言中文(zh)
1.3 项目文件
文件用途
v2.1.1_upgrade.aip升级包工程(基于 v2.1.1_install.aip 派生),构建出 EasyPro.exe
v2.1.1_install.aip上游安装包工程,升级包仅继承其构建参数(UAC、提取目录等)
资源_{版本号}/升级核心目录,每个版本包含 upgrade_to_{版本号}.ps1 及增量文件
版本通用资源/版本通用资源 — 每个版本都会变更的大体积文件(yuntrol、build、JAR 等),集中存放避免重复打包
资源/完整安装资源,升级流程中不使用
1.4 升级包构建参数
参数值
打包类型PackageType=1 — 单文件 EXE(Self-Extracting)
提取目录%APPDATA%\浙江源创智控技术有限公司\智控平台 2.0.0\install
UAC 级别2(需要管理员权限)
1.5 资源目录结构约定
版本通用资源/版本通用大文件集中存放(打包于安装包和升级包中)
deploy_common.ps1部署脚本
nginx/html/yuntrol/Nginx 前端资源(数百 MB,每版必变)
optiView/build/OptiView 前端构建产物(数百 MB,每版必变)
service/*.jarJava 业务 JAR 包(~100 MB,每版必变)
资源_2.0.0/升级到 V2.0.0 的资源集
upgrade_to_2.0.0.ps1升级总控脚本
数据更新/SQL 脚本等数据变更
组件更新/需更新的组件文件(不含大文件)
组件新增/新增的组件
资源_2.1.0/升级到 V2.1.0 的资源集
upgrade_to_2.1.0.ps1
数据更新/
组件更新/
组件新增/
资源_2.1.1/升级到 V2.1.1 的资源集(实际内容)
upgrade_to_2.1.1.ps1部署 EasyProTool + 部署 Qdrant + 数据升级
数据更新/0_common / 1_mqtt / 2_app / 3_modbus / 4_bacnet + upgrade_data.ps1
组件新增/
EasyProTool/运维助手(部署EasyProTool.ps1 + 程序文件)
qdrant/向量数据库(部署Qdrant.ps1 + qdrant.exe + 配置)
组件更新/本版本为空(yuntrol/build/JAR 走版本通用资源)
V2.1.1 新增组件

EasyPro 运维助手(EasyProTool,桌面端 Tauri 应用,创建桌面快捷方式)和 Qdrant 向量数据库(注册为 Windows 服务 SctechQdrantService)。两者都通过 组件新增\ 下各自的 部署{组件名}.ps1 完成部署。

1.5.1 设计动机 — 防止包体积膨胀

前端构建产物(yuntrol、build)和业务 JAR 是每个版本迭代都几乎必然会变更的资源,且体积巨大(合计约 1 GB)。若将这些大文件复制到每个 资源_{版本号}/ 目录中:

  • 有 N 个版本 → 包体积增加 N × 1 GB
  • 随着版本积累 → 升级包从几百 MB 膨胀到数 GB
  • 每个版本的增量升级脚本需要维护这些大文件的拷贝逻辑,容易出错

解决方案:将这些大体积文件集中存放在 版本通用资源/ 中,每个升级版本目录 资源_{版本号}/ 只存放版本特有的增量变更(配置文件、SQL 脚本等)。无论有多少个版本,大文件只有一份。

1.5.2 目录路径约定

版本通用资源/ 内的路径结构完全镜像安装后的 [APPDIR] 目录结构:

版本通用资源内路径目标安装路径
nginx/html/yuntrol/[APPDIR]\nginx\html\yuntrol\
optiView/build/[APPDIR]\optiView\build\
service/*.jar[APPDIR]\service\

部署脚本直接基于路径对应关系操作,无需额外维护映射表。

1.5.3 部署操作方式
资源类型操作方式说明
目录型(yuntrol、build)先停止对应服务 → 强制终止残余进程(node.exe、java.exe)→ Remove-DirectorySafely 安全删除目录 → Copy-Item -Recurse 拷贝目录内文件可能增减或重命名,且需先释放文件锁;残余进程(如手动启动的 node.exe)会阻止目录删除
文件型(*.jar)先停止 Java 后端 → 逐个删除同名旧文件 → 拷贝新文件JAR 运行时被 JVM 锁定,必须先停止服务
安装路径获取方式(强制从注册表读取)
  • deploy_common.ps1 不再接受 -InstallDir 参数,安装路径强制从注册表 HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台\Path 读取
  • 读取失败时报错中止
资源目录获取方式(双源策略)
  • 调用者可通过 -SourceDir 参数显式传入资源目录
  • 如果未传入,自动使用本脚本所在目录(版本通用资源/)
注意

资源_{版本号}/ 目录不应再包含大型前端文件(yuntrol、build)和 JAR 包。这些文件统一放在 版本通用资源/ 中,由 deploy_common.ps1 统一部署,避免在每个版本目录中重复打包导致体积膨胀。

2
升级执行流程
升级总控入口

升级包通用脚本\运行增量升级.ps1(升级包工程 v2.1.1_upgrade.aip 自定义操作调用)。该脚本自检管理员权限、从注册表读取安装路径、做 MySQL 服务前置检查后,依次扫描 资源_{版本号}、执行各版本升级脚本、部署版本通用资源、更新注册表版本号,最后汇总成功/失败步骤并打印总耗时。

2.1 核心逻辑

升级包启动后,按照以下步骤执行:

启动升级包 EasyPro.exe
0. 管理员权限检查 + 读取安装路径
从注册表 HKLM:\SOFTWARE\浙江源创智控\智控平台\Path 读取安装路径;非管理员时自动请求提权重启
★ 前置检查:启动 MySQL 服务
升级依赖数据库。服务不存在→报错中止;未运行→启动(最多 5 次重试,间隔 3 秒);启动失败→中止升级
1. 读取当前已安装版本号
注册表 EasyProVersion 键值(如 "2.0.0")
2. 确定提取目录 ExtractDir
优先:注册表「智控平台升级包\Path」;回退:安装路径;两者都失败→报错中止
3. 扫描所有 资源_{版本号} 文件夹
解析版本号并按语义排序,筛选 > 当前已安装版本的资源集
4. 按版本顺序依次执行 upgrade_to_{版本}.ps1
资源_2.0.0 → 资源_2.1.0 → 资源_2.1.1 … 某版本失败时继续执行下一个(尽可能多执行,最后汇总失败步骤)
★ 4.5 部署版本通用资源(增量升级后)
调用 版本通用资源/deploy_common.ps1,依次部署:
① nginx/html/yuntrol 停 Nginx→删旧→拷新;② optiView/build 停 OptiView→杀 node.exe→安全删→拷新;③ service/*.jar 停 Java→删旧 jar→拷新→重启
必须放在增量升级后,确保最新大文件最终覆盖;覆盖 application-local.yml 后用中间件实际端口动态回填(详见 2.7 节)
5. 更新注册表版本号为本次最高版本
EasyProVersion = "2.1.1"。各升级脚本内也写自己版本号,此处由总控用「已执行的最高版本」做最终覆盖,保证一致
6. 结果汇总
打印升级前/后版本、总耗时、成功/失败步骤清单。全部成功 exit 0,存在失败 exit 1
升级完成
2.2 版本号获取方式
当前版本号(注册表 — 权威来源)

升级包启动后读取注册表:

text
路径: HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台
键名: EasyProVersion

PowerShell 获取方式:

powershell
$reg = Get-ItemProperty -Path "HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台"
$currentVersion = $reg.EasyProVersion

该键由安装脚本在首次安装时写入(值为产品版本号),后续每次升级完成后由升级总控脚本同步更新。

可用升级版本号(文件系统)

扫描升级包提取目录下所有匹配 资源_(\d+\.\d+\.\d+) 模式的文件夹,提取版本号并按语义版本排序。

升级目标选择

将排序后的可用版本列表与当前版本比较,仅执行 版本号 > 当前版本 的升级脚本。若无可升级版本,则跳过所有步骤直接退出。

2.3 升级完成后版本号同步

升级完成后,需要在以下位置记录新版本号:

位置键/字段说明
注册表(权威)HKLM\...\智控平台\EasyProVersion由各子脚本和总控脚本分别写入(两层写入,见 2.3.1)
数据库(辅助)sys_platform_info.cur_platform_version由 0_common.sql 在数据升级时更新(不是脚本直接写数据库),供运维助手下发版本查询
2.3.1 版本号两层写入(实际行为)
与早期设计的差异

早期文档约定「子脚本不写版本号,统一由总控写」。实际代码(upgrade_to_2.1.1.ps1)在结尾成功路径内也写了 EasyProVersion=2.1.1。文档按代码现状描述。

写入层时机写入的值作用
子脚本层每个 upgrade_to_{版本}.ps1 全部步骤成功后该版本号(如 2.1.1)单独运行该脚本时也能正确记录版本;并作为总控最终写入的"候选值"
总控层所有子脚本 + 版本通用资源部署完毕后已成功执行的最高版本号($lastVersion)覆盖最终值,确保多版本跨版本升级时记录的是最高版本

两层写入的最终值一致(单版本升级场景下完全相同),总控层用最高版本覆盖是权威写入。子脚本内写入失败只记 WARN 不中止。

2.4 每个升级脚本的执行契约

每个 upgrade_to_{版本号}.ps1 脚本须遵守以下约定:

要求说明
参数签名接受 -InstallDir <string> 参数(安装路径),由总控脚本传入;未传入时自行从注册表读取(便于独立运行)
幂等性脚本应可重复执行而不破坏已有数据(必要时做存在性检查后再操作)
日志输出到 upgrade_to_{版本号}.log
退出码成功返回 0,失败返回非零值(exit 1)
管理员权限脚本内部自行检测并提权
步骤组织用内置 Invoke-Step -StepName <名> -ScriptPath <脚本> 调用各步骤子脚本,每步独立判定成功/失败
版本号写入全部步骤成功后,自行写入注册表 EasyProVersion(见 2.3.1 两层写入)
工作目录脚本所在目录即 资源_{版本号} 文件夹,相对路径指向 数据更新/、组件更新/、组件新增/
2.5 失败处理策略(失败继续,非中止)

总控脚本 运行增量升级.ps1 对单个版本升级失败采用继续执行策略,而非立即中止:

  • 某个 upgrade_to_{版本}.ps1 退出码非 0 或抛异常 → 记入 $failSteps,继续执行下一个版本和后续步骤(版本通用资源部署、注册表更新)
  • 版本通用资源 deploy_common.ps1 部署失败 → 记入 $failSteps,不中止
  • 注册表版本号更新失败 → 记入 $failSteps,不中止
  • 全部结束后:$failSteps 为空则 exit 0;存在失败则 exit 1
  • 注册表版本号只在 $lastVersion(最后一个成功执行的版本)非空时才更新
设计动机

跨多版本升级时,中间某个版本失败不应阻塞后续版本,让尽可能多的升级执行到位,最后由运维根据汇总清单针对性修复。

2.6 MySQL 服务前置检查

运行增量升级.ps1 在扫描升级资源前,强制检查并启动 MySQL 服务(升级依赖数据库执行 SQL):

MySQL 服务状态处理
不存在(SctechCMySQL 未注册)报错并 exit 1 中止
已运行跳过
未运行发起 Start-Service,最多重试 5 次、每次间隔 3 秒等待变为 Running
重试耗尽仍未启动报错并 exit 1 中止

服务名固定为 SctechCMySQL(取自《各服务配置信息速查手册》)。

2.7 动态端口回填(版本通用资源部署特判)

deploy_common.ps1 在覆盖 service\config\ 子目录时,对 application-local.yml 做了动态端口回填特判,避免升级把安装时动态分配的中间件端口冲回默认值:

触发场景:service\config\ 子目录覆盖前,先把旧 application-local.yml 备份到 $env:TEMP\application-local.old.<guid>.yml;新 yml 覆盖完成后调用 Restore-DynamicPorts。

端口真实值来源(权威源优先,失败回退旧 yml)
中间件权威源(运行时生成的本地配置)回退源替换的默认值
MySQL{InstallDir}\mysql\my.ini 的 port= 行旧 yml 的 jdbc:mysql://127.0.0.1:<port>3306(全局替换所有 jdbc url)
Redis{InstallDir}\Redis\redis.windows.conf 的 port 行旧 yml 的 spring.data.redis.port6379(精确替换 port: 行)
InfluxDB{InstallDir}\influxdb2\influxdb2\config.toml 的 http-bind-address旧 yml 的 http://127.0.0.1:<port>8086(全局替换)
服务自身无中间件侧权威源旧 yml 的首行 port:1998(精确替换,值≠1998 才替换)
安全实现要点
  • 端口行替换统一走 Replace-PortLine:用 [regex]::Replace + MatchEvaluator 脚本块,通过 $m.Groups[n].Value 显式取捕获组,绝不在替换串中使用 $1/$2 字面量(PowerShell 会把 '$1'+'1998' 贪婪解析为 $11998,写出非法 yml)
  • 全程失败只告警(Write-Log "WARN"),绝不中断部署(端口回填是增强,不是硬约束)
  • 写回用 UTF-8 无 BOM,与 yml 原编码保持一致
  • 回填前自动备份新 yml 为 application-local.yml.deploy.bak,便于回滚
2.8 与首次安装的差异
方面首次安装升级
资源依据资源/(完整组件包)资源_{版本号}/(增量升级脚本)
前置条件无,从零部署需已安装旧版本(注册表可查 Path + EasyProVersion)
UI 交互完整安装向导(DbConfig / RedisConfig)无数据库配置界面,沿用已有配置
文件覆盖全部新部署仅执行增量脚本中指定的变更
组件注册Windows 服务全新注册增量脚本中只增/更新必要的服务
路径选择用户可选择安装路径从注册表读取已有安装路径
版本判断无通过注册表 EasyProVersion 键确定升级起点
版本写入安装脚本写入 EasyProVersion(基础版本)子脚本 + 总控两层写入(见 2.3.1)
MySQL 前置检查安装时由安装脚本初始化升级前强制检查并启动 MySQL(见 2.6)
2.9 输入参数

升级包不通过 UI 接收输入,所有参数来自已安装环境:

参数来源
安装路径注册表 HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台\Path
当前版本号注册表 HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台\EasyProVersion(不存在或为空视为 0.0.0)
提取目录优先注册表 HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台升级包\Path,失败回退到安装路径

MySQL 及其他服务配置无需作为升级输入参数,因为增量升级脚本可按需从 application-local.yml 获取,版本通用资源部署阶段还会用动态端口回填(2.7)校正中间件端口。

3
资源_{版本号} 目录规范
3.1 目录结构
资源_{版本号}/
upgrade_to_{版本号}.ps1【必须】该版本升级脚本(由总控依次调用)
数据更新/【可选】数据库脚本
upgrade_data.ps1扫描本目录所有 *.sql,按前缀数字顺序执行
0_common.sql数字越小越先执行
1_mqtt.sql
2_app.sql
3_modbus.sql
4_bacnet.sql
…
组件更新/【可选】需更新的组件文件(不含大文件)
service/
mysql/
…
组件新增/【可选】新增的组件
新组件名/
部署{组件名}.ps1
…
关于大文件的约定(核心设计)

组件更新/ 中绝对不应包含 yuntrol、build 目录或 JAR 包。这些每个版本都会变更的大体积文件(合计约 1 GB)统一存放在 版本通用资源/ 中,由总控脚本在增量升级全部执行完毕后通过 deploy_common.ps1 统一部署(放在升级后执行,确保通用资源覆盖旧版文件)。各版本升级脚本应只关注版本特有的增量变更(配置文件修改、数据脚本、服务注册等)。

这样做的好处:

  1. 包体积不随版本数量线性增长 — N 个版本的升级包中,大文件只有一份
  2. 避免重复劳动 — 每个版本不需要重复写大文件的拷贝逻辑
  3. 降低出错概率 — 删除整目录再拷贝的方式比逐文件覆盖更可靠
3.1.1 数据升级 SQL 文件规范

数据更新/ 下所有 .sql 文件由 upgrade_data.ps1 统一调度执行:

  • 命名规则:{数字}_{业务名}.sql(如 0_common.sql、1_mqtt.sql),数字越小越先执行
  • 数据库引用:使用完全限定名(`ibms-system`.`sys_menu`);涉及存储过程的 SQL 文件须在 CREATE PROCEDURE 前显式 USE 指定数据库,避免 ERROR 1046 No database selected
  • 执行隔离:每个 SQL 文件独立执行,单独记录成功/失败;upgrade_data.ps1 全部成功 exit 0,有失败 exit 1
  • 幂等保护:推荐用存储过程 + 游标遍历 sys_project + INSERT ... SELECT ... WHERE NOT EXISTS 模式,保证可重复执行
V2.1.1 当前 SQL 文件清单
序号文件职责
00_common.sql更新 sys_platform_info 平台版本号至 v2.1.1;字段类型修订;为每个项目插入「树形结构默认展开」sys_config
11_mqtt.sqlMQTT 驱动数据升级:为每个项目插入 iot_driver / iot_driver_attribute / iot_driver_point_attribute(含全局 iot_driver_config)
22_app.sqlApp 端菜单:sys_menu 增加 url、is_app 字段;为每个项目插入能源管理 / 智慧告警 / 系统日志业务菜单
33_modbus.sqlModbus 驱动补充 writeFunctionCode / writeCodeSwitchMode 属性
44_bacnet.sqlBACnet IP 驱动数据升级(参考 1_mqtt 模式插入驱动及属性)
新增 SQL 文件

新增 SQL 文件时,序号延续递增(5_xxx.sql...),并在此表登记。

3.2 upgrade_to_{版本号}.ps1 脚本模板
powershell
<#
.SYNOPSIS
  EasyPro V{版本号} 升级脚本
.DESCRIPTION
  按顺序执行当前版本的所有升级步骤:
  - 组件更新(拷贝新文件覆盖旧文件)
  - 组件新增(拷贝新组件到安装目录并安装服务)
  - 数据升级(执行 SQL 脚本)
  版本号写入(升级完成后更新注册表 EasyProVersion 键)
.NOTES
  需要以管理员权限运行。
  由升级总控脚本调用,传入 -InstallDir 参数。
#>

param(
    [string]$InstallDir   # 安装路径,由总控传入
)

$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
$LogFile = Join-Path $ScriptDir "upgrade_to_{版本号}.log"

function Write-Log { ... }
function Invoke-Step { ... }

# 权限检测、日志标题等...

# 步骤 1: 组件更新
# Invoke-Step "更新 xxx" "组件更新\xxx.ps1"

# 步骤 2: 组件新增
# Invoke-Step "部署 yyy" "组件新增\yyy\部署yyy.ps1"

# 步骤 3: 数据升级
# Invoke-Step "数据升级" "数据更新\upgrade_xxx.ps1"

# 步骤 4: 更新注册表版本号
# Set-ItemProperty -Path "HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台" `
#                 -Name "EasyProVersion" -Value "{版本号}"

# 结果汇总...
4
升级总控脚本(入口)

升级包提取到临时目录后,由总控脚本 升级包通用脚本\运行增量升级.ps1 统筹所有升级步骤,由升级包工程 v2.1.1_upgrade.aip 的自定义操作(Custom Action)在文件提取完成后调用。

4.1 总控逻辑(精简骨架)
说明

完整实现见 升级包通用脚本/运行增量升级.ps1。以下为关键骨架(省去日志、汇总、提权等细节),展示真实控制流与失败处理策略。

powershell
$RegPath = "HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台"
$UpgradeRegPath = "HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台升级包"
$successSteps = @();  $failSteps = @()

# 0. 管理员权限自检(非管理员则 RunAs 提权重启)
# 0.1 读取安装路径(注册表 Path 键,失败 exit 1)
$InstallDir = (Get-ItemProperty -Path $RegPath -Name "Path").Path

# 0.2 当前版本(缺失/为空视为 0.0.0,即视为全新环境全量执行)
$currentVersion = try { (Get-ItemProperty -Path $RegPath -Name "EasyProVersion").EasyProVersion } catch { "0.0.0" }

# ★ 前置检查:启动 MySQL 服务(不存在 exit 1;未运行则启动,最多 5 次重试间隔 3 秒)
Ensure-MysqlRunning   # 见 2.6

# 0.3 提取目录:优先升级包注册表,失败回退安装路径
$ExtractDir = try { (Get-ItemProperty -Path $UpgradeRegPath -Name "Path").Path } catch { $InstallDir }

# 步骤 1/5:扫描 资源_{版本号}
$upgradeDirs = Get-ChildItem -Directory (Join-Path $ExtractDir "资源_*") |
    Where-Object { $_.Name -match '^资源_(\d+\.\d+\.\d+)$' } |
    Sort-Object { [Version]$matches[1] }

# 步骤 2/5:筛选版本号 > 当前版本,并依次执行(失败继续,非中止)
$lastVersion = $null
foreach ($dir in $upgradeDirs) {
    $verString = $matches[1]            # 注意:$matches 在 Where 后可能失效,实际代码用显式 rematch
    if ([Version]$verString -le [Version]$currentVersion) { continue }
    $scriptPath = Join-Path $dir.FullName "upgrade_to_${verString}.ps1"
    try {
        & $scriptPath -InstallDir $InstallDir *>&1 | CaptureToLog
        if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
            $failSteps += "升级到 $verString(退出码 $LASTEXITCODE)"
        } else {
            $lastVersion = $verString      # 记录最后一个成功版本
            $successSteps += "升级到 $verString"
        }
    } catch { $failSteps += "升级到 $verString 异常: $_" }
    # ★ 注意:不 throw,继续下一个版本
}

# 步骤 3/5:部署版本通用资源(含动态端口回填特判,见 2.7)
$commonScript = Join-Path $ExtractDir "版本通用资源\deploy_common.ps1"
if (Test-Path $commonScript) {
    & $commonScript -SourceDir (Join-Path $ExtractDir "版本通用资源")
    # 失败仅记入 failSteps,不中止
}

# 步骤 4/5:更新注册表版本号(用最后一个成功版本做最终覆盖)
if ($lastVersion) {
    try { Set-ItemProperty -Path $RegPath -Name "EasyProVersion" -Value $lastVersion }
    catch { $failSteps += "注册表版本号更新失败: $_" }
}

# 步骤 5/5:结果汇总(成功/失败步骤清单 + 总耗时)
if ($failSteps.Count -eq 0) { Write-Host "[完成] 全部完成"; exit 0 }
else { Write-Host "[警告] 部分失败"; exit 1 }
4.2 总控脚本位置与依赖
项说明
脚本路径升级包通用脚本\运行增量升级.ps1(被打包进升级包,由 v2.1.1_upgrade.aip 自定义操作调用)
同级脚本检验版本.ps1(升级首个校验:未安装则退出;已安装版本 ≥ 升级包版本时禁止降级/重复升级并退出,接收 -UpgradeVersion 参数)、启动所有服务.ps1(升级收尾:遍历已知服务列表启动未运行的服务)、卸载所有服务.ps1(升级前可选卸载服务,扫描安装目录所有 uninstall.bat 并按"应用→中间件→数据库"优先级执行)
日志升级包通用脚本\运行增量升级.log(脚本同目录)
提权内部自检,非管理员时 Start-Process -Verb RunAs 提权重启,等子进程退出后用其 ExitCode 退出
4.3 升级包通用脚本目录一览
升级包通用脚本/
运行增量升级.ps1总控入口(扫描资源_*/执行升级/部署通用资源/更新版本号/汇总)
检验版本.ps1升级首个校验(未安装/禁止降级/禁止重复升级则退出)
卸载所有服务.ps1扫描安装目录所有 uninstall.bat,按优先级停止并删除服务
启动所有服务.ps1升级收尾,遍历已知服务列表启动未运行的服务
打包说明

这四个脚本都被打包进升级包工程;安装包工程(v2.1.1_install.aip)同样打包了它们(用于首次安装后执行增量升级,详见安装包文档)。

5
限制与注意事项
1
不可逆向升级
总控脚本仅执行比当前版本高的升级资源集,不会降级。
2
升级路径完整性
升级必须逐版本执行,不能跳过中间版本。如果 资源_2.0.0 和 资源_2.1.0 都存在,从 V2.0.0 起需要两个版本依次执行。
3
无对话框
升级流程无用户交互界面,静默执行。
4
升级包体积
仅包含增量文件,体积远小于安装包(不含完整组件资源)。
5
端口/配置保持
升级后端口/IP 沿用旧版配置;版本通用资源部署阶段还会用动态端口回填(2.7)校正被覆盖 yml 冲回的中间件端口。
6
并发升级
不支持同时运行多个升级实例。
7
资源/ 目录不参与升级
完整安装资源目录在升级流程中不可用,仅 资源_{版本号} 有效。
8
版本号两层写入
EasyProVersion 由子脚本(各自版本号)和总控脚本(最高版本号)分别写入,最终以总控写入为权威;数据库 cur_platform_version 由 0_common.sql 辅助更新(见 2.3.1)。
9
版本通用资源(防包膨胀核心机制)
版本通用资源/ 在每次增量升级全部执行完毕后由总控脚本部署,以「Remove-DirectorySafely 安全删除旧目录 → 拷贝新目录」的方式覆盖 yuntrol、build(删除前会强制终止残余 node.exe 进程防止文件锁),以「删除旧 JAR → 拷贝新 JAR」的方式覆盖 JAR;service\config\ 子目录覆盖后还会做动态端口回填(2.7)。放在升级后执行是为了确保最新大文件最终覆盖到位,防止升级组件更新中的旧文件覆盖新通用资源。所有 资源_{版本号}/ 的 组件更新/ 中绝对不得包含这些大文件,否则违背该设计的初衷。
10
MySQL 前置依赖
升级依赖数据库执行 SQL,总控脚本会强制检查并启动 MySQL(SctechCMySQL),启动失败则中止(见 2.6)。
11
失败不中止
单个版本或版本通用资源部署失败时,总控采用"继续执行 + 末尾汇总"策略,最后 exit 1 提示有失败(见 2.5)。
6
构建与测试
6.1 构建步骤
  1. 打开 v2.1.1_upgrade.aip(基于 v2.1.1_install.aip 派生)
  2. 确认所有 资源_{版本号} 目录及其升级脚本已就绪
  3. 在 Advanced Installer 中执行构建(Build → Build)
  4. 构建产物:EasyPro.exe
6.2 测试要点
测试项预期结果
从未安装的环境执行升级注册表 Path 不存在,提示未检测到安装并 exit 1
从旧版本升级(如 V2.0.0 → V2.1.1)正确扫描版本序列,依次执行各版本升级脚本,注册表 EasyProVersion 更新为 2.1.1
从同版本执行升级(如 V2.1.1 → V2.1.1)无可升级版本,exit 0 完成
从高版本执行升级(回退)筛选结果为空,跳过所有升级步骤
MySQL 服务未启动总控自动启动(最多 5 次重试),启动成功后继续升级
MySQL 服务不存在报错 exit 1 中止(提示 MySQL 未正确安装)
单个版本升级失败记入失败清单,继续执行后续版本,末尾 exit 1 汇总(失败继续策略)
动态端口回填覆盖 yml 后,MySQL/Redis/InfluxDB 端口被回填为中间件实际端口(非默认 3306/6379/8086)
升级过程中断后重启已执行的版本脚本是幂等的(SQL 用 WHERE NOT EXISTS、组件覆盖用删除-拷贝),不会造成破坏
升级后注册表版本号HKLM\...\智控平台\EasyProVersion 已更新为最后一个成功执行的版本号
升级后服务可用所有 Windows 服务正常运行,端口配置正确
数据保留MySQL / InfluxDB / Redis 中的用户数据不被清除
文档版本:2.2

最后更新:2026-07-28 | 同步至 V2.1.1 实际代码:MySQL 前置检查、失败继续策略、动态端口回填、SQL 多文件、版本号两层写入