1
概述
1.1 项目目标

将「智控平台」全套软件栈打包为一个 Windows 单文件安装程序(.exe),支持:

  • 首次安装:从零部署完整的智控平台运行环境,并自动执行所有增量升级脚本完成版本演进
  • 覆盖升级:在已安装的旧版本上升级组件(详见《EasyPro升级包设计文档》)
  • 完整卸载:彻底移除所有已安装组件和服务
1.2 产品标识
字段值
产品名称智控平台
制造商浙江源创智控技术有限公司
产品版本2.0.0
ProductCode{0BE1DE1C-2C3C-4D5C-876F-258104AE0EEE}
UpgradeCode{4A49D8CC-5A22-466E-A867-6E95AD659547}
安装包文件名EasyPro V2.1.1.exe
语言中文(zh)
安装类型按需安装(InstallationType=2)
注意

ProductVersion(2.0.0)与安装包版本号(V2.1.1)不耦合。升级包 EasyPro.exe 使用相同 UpgradeCode 实现无缝升级。

1.3 项目文件
文件用途
v2.1.1_install.aip安装包工程 — 构建出完整安装包 EasyPro V2.1.1.exe,包含首次安装 + 覆盖升级 + 完整卸载
v2.1.1_upgrade.aip升级包工程(基于 v2.1.1_install.aip 派生),详见《EasyPro升级包设计文档》
v2.1.1_install-cache/构建缓存(增量构建加速)
资源/完整基础安装资源 — 初始版本的全部软件组件
资源_{版本号}/增量升级资源集 — 每个版本对应一个目录,包含 upgrade_to_{版本号}.ps1 及其增量文件
版本通用资源/版本通用资源 — 每个版本都会变更的大体积文件(yuntrol、build、JAR 等),集中存放避免重复打包
1.4 安装包构建参数
参数值
打包类型PackageType=1 — 单文件 EXE(Self-Extracting)
压缩方式LZMA(Method=2, CompressionLevel=2)
CAB 位置内嵌(CabsLocation=1)
图标资源/nginx/html/def/ycLoge.ico
提取目录%APPDATA%\浙江源创智控技术有限公司\智控平台 2.0.0\install
UAC 级别2(需要管理员权限)
外部 UI启用
2
系统要求
2.1 操作系统
  • Windows 10 / Windows Server 2016 及以上
  • 已排除:Windows 2000 (NT 5.0)、Windows XP (NT 5.1/5.2)、Windows Vista (NT 6.0)、Windows 7 (NT 6.1)
2.2 硬件要求
  • 物理内存 ≥ 7000 MB(通过 AI_DETECTED_PHYSICAL_MEMORY 检测)
2.3 运行库预置条件
预置条件说明
VC++ RedistributableAdvanced Installer 的 Pre-requisites 机制自动检测和安装
其他通过 AI_DetectSoftware 统一检测
3
软件资源清单

安装包打包以下核心组件:

#组件安装目录角色
1JDK[APPDIR]\jdk\Java 运行环境
2OptiCloudService[APPDIR]\service\Java 后台服务(核心业务逻辑)
3Nginx[APPDIR]\nginx\Web 服务器 + 前端静态资源
4MySQL[APPDIR]\mysql\关系数据库
5Redis[APPDIR]\Redis\缓存数据库
6InfluxDB 2[APPDIR]\influxdb2\时序数据库
7EMQX 4.4 / 5.0[APPDIR]\emqx-4.4.11\MQTT 消息服务器
8MinIO[APPDIR]\minio\对象存储服务
9OptiView[APPDIR]\optiView\Node.js 前端应用
10工具[APPDIR]\tools\辅助工具
11环境脚本[APPDIR]\environment\环境初始化脚本
12安装脚本[APPDIR]\script\安装/卸载辅助脚本、端口刷新脚本
13文档[APPDIR]\doc\产品文档
V2.1.1 增量引入的组件

通过 资源_2.1.1\组件新增\ 在增量升级阶段部署,不在基础 资源/ 中:

- EasyPro 运维助手(EasyProTool)→ 部署到 [APPDIR]\EasyProTool\,创建桌面快捷方式

- Qdrant 向量数据库 → 部署到 [APPDIR]\qdrant\,注册为 Windows 服务 SctechQdrantService

3.1 Service 子目录详解

[APPDIR]\service\ 内部结构:

路径内容
OptiCloudService.exeWindows 服务包装程序(作为 Windows Service 启动 Java JAR)
OptiCloudService.jarJava 主业务程序
OptiCloudService.xml服务配置文件
config/配置文件(数据库连接、端口等)
dll/第三方 DLL(Rcp.dll, SuperDog.dll — 加密狗)
init-file/初始化数据文件
lib/Java 依赖库
lib.zip压缩的 Java 依赖库
jar/额外 JAR 包
pay/支付模块
build.rar构建产物存档
install.bat服务注册脚本
uninstall.bat服务注销脚本
3.2 版本通用资源目录 (版本通用资源/)
3.2.1 设计动机

每个版本的迭代中,以下三类资源几乎必定会变更,且体积巨大:

资源典型体积变更频率
nginx/html/yuntrol/(Nginx 前端资源)数百 MB几乎每版必变
optiView/build/(OptiView 前端构建产物)数百 MB几乎每版必变
service/*.jar(Java 主业务 JAR)~100 MB几乎每版必变

如果将这些大文件复制到每个 资源_{版本号}/ 目录中,则每多一个版本,安装包 / 升级包就会膨胀约 1 GB。随着版本积累,包体积将迅速失控。

解决方案

将每个版本都会变更的大体积资源集中存放在 版本通用资源/ 目录中,每个 资源_{版本号}/ 只存放版本特有的增量变更(配置文件、SQL 脚本、少量组件文件等)。这样,无论积累多少个版本,大体积资源只占一份空间。

3.2.2 目录结构约定

版本通用资源/ 内的路径结构完全镜像安装后的 [APPDIR] 目录结构。这样部署脚本无需维护复杂的映射表,直接用路径对应即可:

版本通用资源/项目根目录
deploy_common.ps1【必须】部署脚本,由总控脚本调用
nginx/html/yuntrol/Nginx 前端资源 → 部署到 [APPDIR]\nginx\html\yuntrol\
optiView/build/OptiView 前端构建产物 → 部署到 [APPDIR]\optiView\build\
service/
*.jarJava 主业务 JAR 包 → 部署到 [APPDIR]\service\
…其他大体积通用资源
核心约定

版本通用资源内的路径 = 安装后的目标路径(相对于 [APPDIR])。

3.2.3 部署策略

针对不同类型的资源,采用不同的部署操作:

资源类型操作方式原因
目录型资源(yuntrol/、build/)先停止对应服务(存在才停)→ 再强制终止可能占用文件的进程(node.exe、java.exe)→ 再 Remove-DirectorySafely 删除旧目录 → 再 Copy-Item -Recurse 拷贝新目录防止文件被服务或残余进程占用导致删除失败
文件型资源(*.jar)先停止 Java 后端服务(存在才停)→ 再删除同名的旧 JAR → 再拷贝新 JAR 覆盖JAR 文件运行时被 JVM 锁定,必须先停止服务
注意

部署完成后不重启服务。首次安装场景下服务可能尚未注册,启动操作错误;覆盖升级场景下,由调用方决定何时重启。

3.2.4 部署脚本

部署脚本 deploy_common.ps1 位于 版本通用资源/ 目录下,由总控脚本 升级包通用脚本\运行增量升级.ps1(首次安装和覆盖升级共用)在增量升级执行完毕后调用。

安装路径获取(强制从注册表读取):

  • 从注册表 HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台\Path 读取
  • 读取失败时报错中止,不再接受 -InstallDir 参数传入

资源目录获取顺序(双源策略):

  1. 优先使用调用时传入的 -SourceDir 参数
  2. 如果未传入,自动使用本脚本所在目录(版本通用资源/)
调用约定
powershell
# 方式一:显式传入 -SourceDir(总控脚本调用时使用)
& .\deploy_common.ps1 -SourceDir $commonDir

# 方式二:无参数运行(自动从注册表 + 自身位置获取)
& .\deploy_common.ps1
核心逻辑(完整代码见 版本通用资源/deploy_common.ps1)
powershell
# 0. 强制从注册表读取安装路径
$reg = Get-ItemProperty -Path "HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台" -ErrorAction Stop
$InstallDir = $reg.Path
if (-not $SourceDir) { $SourceDir = Split-Path -Parent $MyInvocation.MyCommand.Definition }

# Step 1: 部署 nginx/html/yuntrol/
Stop-ServiceIfRunning -ServiceName "SctechCFSPUI" -DisplayName "Nginx"  # 停止 Nginx(存在才停)
$sourceYuntrol = Join-Path $SourceDir "nginx\html\yuntrol"
$targetYuntrol = Join-Path $InstallDir "nginx\html\yuntrol"
Remove-DirectorySafely -Path $targetYuntrol                          # 安全删除旧目录(自动清除只读+3次重试+cmd回退)
Copy-Item -Path $sourceYuntrol -Destination $targetYuntrol -Recurse -Force  # 拷贝新目录
# 注意:不重启服务,由调用方决定

# Step 2: 部署 optiView/build/
Stop-ServiceIfRunning -ServiceName "SctechOptiViewService" -DisplayName "OptiView"  # 停止 OptiView(存在才停)
Stop-AllNodeProcesses                                                            # 强制终止所有残余 node.exe
$sourceBuild = Join-Path $SourceDir "optiView\build"
$targetBuild = Join-Path $InstallDir "optiView\build"
Remove-DirectorySafely -Path $targetBuild                          # 安全删除旧目录(自动清除只读+3次重试+cmd回退)
Copy-Item -Path $sourceBuild -Destination $targetBuild -Recurse -Force  # 拷贝新目录

# Step 3: 部署 service/(含子目录与 JAR 文件)
Stop-ServiceIfRunning -ServiceName "SctechOptiCloudService" -DisplayName "OptiCloudService (Java)"

# 3a. 子目录(lib/、config/ 等):删除旧目录 → 拷贝新目录
#     ★ config 特判:覆盖前备份旧 application-local.yml;覆盖后回填中间件动态端口
$subDirs = Get-ChildItem -Path "$SourceDir\service" -Directory
foreach ($subDir in $subDirs) {
    $targetSubDir = Join-Path $InstallDir "service\$($subDir.Name)"

    # config 特判①:覆盖前备份旧 yml(供端口回填读取)
    $oldYmlBackup = $null
    if ($subDir.Name -eq "config") {
        $oldYml = Join-Path $targetSubDir "application-local.yml"
        if (Test-Path $oldYml) {
            $oldYmlBackup = Join-Path $env:TEMP ("application-local.old.{0}.yml" -f [guid]::NewGuid().ToString("N"))
            Copy-Item -Path $oldYml -Destination $oldYmlBackup -Force
        }
    }

    Remove-DirectorySafely -Path $targetSubDir
    Copy-Item -Path $subDir.FullName -Destination $targetSubDir -Recurse -Force

    # config 特判②:覆盖后用中间件实际端口回填新 yml(详见升级包文档 2.7)
    if ($subDir.Name -eq "config") {
        $newYml = Join-Path $targetSubDir "application-local.yml"
        Restore-DynamicPorts -NewYmlPath $newYml -InstallDir $InstallDir -OldYmlPath $oldYmlBackup
    }
}

# 3b. 根目录 JAR 文件:逐个删除旧 JAR → 拷贝新 JAR
$jarFiles = Get-ChildItem -Path "$SourceDir\service" -Filter "*.jar"
foreach ($jar in $jarFiles) {
    $targetJar = Join-Path $InstallDir "service\$($jar.Name)"
    if (Test-Path $targetJar) { Remove-Item -Path $targetJar -Force }
    Copy-Item -Path $jar.FullName -Destination $targetJar -Force
}

# 辅助函数:安全删除目录(三级兜底)
function Remove-DirectorySafely {
    param([string]$Path)
    # 1. 清除只读属性
    Get-ChildItem $Path -Recurse -Force | ForEach-Object { try { $_.IsReadOnly = $false } catch {} }
    # 2. .NET Directory.Delete(recursive=true) × 3 次重试
    $retry = 0; while ($retry -lt 3) {
        try { [System.IO.Directory]::Delete($Path, $true); return }
        catch { $retry++; Start-Sleep -Milliseconds 1000 }
    }
    # 3. 失败时回退 cmd /c rmdir /s /q
    & cmd /c "rmdir /s /q ""$Path"" 2>nul"
    if (Test-Path $Path) {
        # 4. 仍失败 → 终止可疑进程+重置权限后重试
        Stop-AllNodeProcesses; Stop-Process -Name "java" -Force -ErrorAction SilentlyContinue
        & takeown /f "$Path" /r /d y 2>$null; & icacls "$Path" /grant Administrators:F /T /Q 2>$null
        [System.IO.Directory]::Delete($Path, $true)  # 再次尝试
    }
}

# 注意:用 $procId 中转,避免覆盖 PowerShell 内置只读变量 $PID
function Stop-AllNodeProcesses {
    Get-Process -Name "node" -ErrorAction SilentlyContinue | ForEach-Object {
        $procId = $_.Id
        Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue
    }
}
动态端口回填(Restore-DynamicPorts)

覆盖 application-local.yml 后,从中间件本地配置(mysql\my.ini、Redis\redis.windows.conf、influxdb2\influxdb2\config.toml)读取实际端口,回填新 yml 中被冲回默认值的 MySQL(3306)/Redis(6379)/InfluxDB(8086)/服务自身(1998)端口。端口行替换用 [regex]::Replace + MatchEvaluator(避免 $1 捕获组污染),全程失败只告警不中断部署。详见升级包文档 2.7。

3.2.5 执行时机

deploy_common.ps1 在以下两种场景中执行:

  • 首次安装:基础版本安装及所有增量升级脚本执行完毕后,由 运行增量升级.ps1 调用
  • 覆盖升级:所有增量升级脚本执行完毕后,由升级总控脚本调用
为什么放在增量升级之后?

版本通用资源(yuntrol、build、JAR)是每个版本都会变更的大文件,若先部署通用资源再执行增量升级,升级脚本中的组件更新可能会用老版本文件覆盖掉新通用资源。放在最后执行可以确保最新的大文件始终覆盖到位。

无论哪种场景,部署操作都会将安装/升级包携带的最新版本资源写入 [APPDIR],确保用户始终获得最新的前端和业务 JAR。

3.2.6 各 资源_{版本号}/ 的约束

资源_{版本号}/ 目录的 组件更新/ 中不得包含 yuntrol、build、JAR 等大体积文件。这些文件统一由 版本通用资源/ 提供。资源_{版本号}/ 只应包含版本特有的增量变更:

  • 配置文件修改(application-local.yml、OptiCloudService.xml 等)
  • SQL 数据升级脚本
  • 新增的组件或小文件
  • 服务注册/注销脚本
4
安装流程设计
4.1 完整安装流程
启动安装程序
EasyPro V2.1.1.exe,以管理员权限运行
预置条件检测 & 安装
VC++ Redistributable 等运行库检测与自动安装
产品欢迎界面
展示产品信息,进入安装向导
MySQL 数据库配置 对话框 DbConfig
选择数据库类型;是否使用已有库(否→本机安装新库,是→输入 IP/端口/账号密码);连接测试
Redis 数据库配置 对话框 RedisConfig
是否使用已有 Redis(否→本机安装新 Redis,是→输入 IP/端口/密码)
准备安装
显示安装摘要与确认框
执行文件复制(MSI 安装引擎)
将资源文件解压到安装目录。仅 资源/ 按目录映射复制到 [APPDIR];资源_{版本号}/ 保留在提取目录供后续升级使用
运行各组件安装自定义操作(顺序执行)
所有操作通过 viewer.exe 提权执行(/EnforcedRunAsAdmin /RunAsAdmin /HideWindow)
8 个组件安装子步骤(按 seq 顺序)
1
install_redis seq6611
Redis 服务
2
install_mysql seq6612
MySQL + 初始化
3
install_influxdb seq6613
InfluxDB 服务
4
install_emqx seq6614
EMQX MQTT 服务
5
install_nginx seq6615
Nginx Web 服务
6
install_jar seq6616
OptiCloud Java 服务
7
install_view seq6617
OptiView 前端
8
setDynamicport
动态端口(防冲突)
端口配置刷新(PowerShell 脚本)
refresh_mysql_port / redis_port / influxdb_port / nginx_9082_port / service_port:将用户配置的端口/IP 写入各组件配置文件
★ 增量升级脚本执行
基础版本安装完毕后,扫描提取目录所有 资源_{版本号}/,按语义版本排序,依次执行 upgrade_to_{版本号}.ps1 -InstallDir <目录>;失败则中止告警。由 运行增量升级.ps1 总控
★ 部署版本通用资源(增量升级后)
调用 deploy_common.ps1 部署 yuntrol/build/JAR 等通用大文件,确保最新大文件最终覆盖;覆盖 application-local.yml 后用中间件实际端口动态回填,防止端口被冲回默认值。必须放在增量升级之后,避免组件更新覆盖新通用资源
注册表写入
安装路径 → HKLM\Software\浙江源创智控\智控平台\Path;版本号 → EasyProVersion(升级脚本执行后更新为最高版本);卸载信息 → 系统卸载管理器
安装完成
庆祝界面,安装流程结束
4.2 安装包内的资源目录布局

安装包提取目录中包含以下资源目录:

%APPDATA%\浙江源创智控\智控平台 2.0.0\install/提取根目录
资源/完整基础安装资源(按映射规则复制到 [APPDIR])
资源_2.1.1/升级到 V2.1.1 的增量资源
upgrade_to_2.1.1.ps1升级脚本
数据更新/
组件更新/
组件新增/
资源_2.1.2/(如有更多版本)
版本通用资源/版本通用大文件集中存放
deploy_common.ps1部署脚本(含动态端口回填)
nginx/html/yuntrol/
optiView/build/
service/*.jar
升级包通用脚本/升级总控脚本(安装包/升级包共用)
运行增量升级.ps1增量升级总控入口(4.3 节)
检验版本.ps1
卸载所有服务.ps1
启动所有服务.ps1
script/安装辅助脚本(端口刷新等)
tools/辅助工具
viewer.exeAdvanced Installer 提权执行器
文件复制规则
  • 资源/ → 按 MSI 映射规则复制到用户选择的 [APPDIR]
  • 资源_{版本号}/ → 保留在提取目录中,不复制到安装目录,供升级总控脚本直接读取
  • 版本通用资源/ → 保留在提取目录中,不复制到安装目录,由 deploy_common.ps1 在部署时按需将具体子目录拷贝到 [APPDIR]
  • script/、tools/ → 按 MSI 映射规则复制到 [APPDIR]
4.3 执行增量升级(基础安装后)

基础版本安装 + 端口配置完成后,安装包通过 v2.1.1_install.aip 的自定义操作(Custom Action)调用总控脚本 升级包通用脚本\运行增量升级.ps1 统筹执行所有增量升级。

重要更正

早期文档此处描述为 script\run_upgrades.ps1。实际安装包工程打包并调用的是 升级包通用脚本\运行增量升级.ps1(与升级包工程共用同一套通用脚本,见 aip 工程的 File 表)。安装包提取目录中不存在 script\run_upgrades.ps1。

总控脚本:运行增量升级.ps1

该脚本由升级包/安装包共用,完整实现见 升级包通用脚本/运行增量升级.ps1,详细控制流见《EasyPro升级包设计文档》第 4.1 节。安装包场景下的执行要点:

步骤说明
0. 权限 + 路径自检管理员权限(非管理员自动 RunAs 提权);从注册表读安装路径
★ MySQL 前置检查检查并启动 MySQL 服务(SctechCMySQL),启动失败则 exit 1 中止(升级依赖数据库)
1/5 扫描扫描提取目录下所有 资源_{版本号}/,按语义版本排序
2/5 筛选+执行筛选版本号 > 当前版本(首次安装场景当前版本为 0.0.0,即全部执行),依次调用各 upgrade_to_{版本}.ps1 -InstallDir。单个版本失败继续执行下一个(失败继续策略),不中止安装
3/5 部署通用资源调用 版本通用资源\deploy_common.ps1 -SourceDir <版本通用资源目录>,部署 yuntrol/build/JAR;覆盖 service\config\ 后做动态端口回填
4/5 版本号用最后一个成功执行的版本号更新注册表 EasyProVersion
5/5 汇总打印成功/失败步骤清单与总耗时;全成功 exit 0,有失败 exit 1
注意

注册表 EasyProVersion 的最终值由总控脚本用"已执行的最高版本号"写入(权威层);各 upgrade_to_{版本号}.ps1 内部也会写入自己版本号(子脚本层)。两层写入最终值一致,详见升级包文档 2.3.1。

动态端口回填

覆盖 application-local.yml 后,从中间件本地配置(mysql\my.ini、Redis\redis.windows.conf、influxdb2\influxdb2\config.toml)读取实际端口回填新 yml,避免把安装时动态分配的中间件端口冲回默认值(3306/6379/8086)。详见升级包文档 2.7。

4.4 自定义操作实现细节
组件安装脚本

所有组件安装均通过 viewer.exe(Advanced Installer 内置的提权执行器)来运行目标目录中的批处理脚本:

操作脚本说明
install_redis[Redis_Dir]\service-install.bat安装 Redis 为 Windows 服务
install_mysql[mysql_Dir]\install.bat初始化 MySQL 数据目录并安装服务
install_influxdb[influxdb2_Dir]\install.bat安装 InfluxDB 服务
install_emqx[bin_13_Dir]\install.bat安装 EMQX 为 Windows 服务
install_nginx[nginx_Dir]\install.bat安装 Nginx 服务
install_jar[service_Dir]\install.bat注册 OptiCloudService 为 Windows 服务
install_view[optiView_Dir]\installService.cmd安装 OptiView Node.js 服务
setDynamicport[script_Dir]\setDynamicport.bat处理动态端口
增量升级入口升级包通用脚本\运行增量升级.ps1增量升级总控 — 扫描 资源_{版本号} 依次升级,再做版本通用资源部署与动态端口回填(详见 4.3)
端口刷新脚本

端口配置采用 PowerShell 脚本实现,通过 PowerShellScriptLauncher.dll 执行:

操作对应脚本作用
refresh_mysql_portmysql-port-refresh.ps1将用户输入的 MySQL IP/端口写入 Nginx 配置
refresh_redis_portredis-port-refresh.ps1将用户输入的 Redis IP/端口写入配置
refresh_influxdb_portinfluxdb-port-refresh.ps1将用户输入写入 InfluxDB 连接配置
refresh_nginx_9082_portnginx9082-port-refresh.ps1刷新 Nginx 9082 端口配置
refresh_service_portservice-port-refresh.ps1刷新 Java 服务端口配置

端口配置信息通过 AI_DATA_SETTER_X 的 Base64 编码数据传递给 PowerShell 脚本。

5
交互界面设计
5.1 主题风格
  • 主题: Metro Green (绿色主题)
  • 框架: 基于 Advanced Installer Surface 主题
  • 窗口尺寸: 345×400 像素(固定大小)
5.2 对话框序列
顺序对话框说明
1PatchWelcomeDlg产品欢迎页,显示产品名称、制造商 Logo
2DbConfig关系数据库(MySQL)配置页
3RedisConfigRedis 缓存数据库配置页
4VerifyReadyDlg安装确认页
5安装进度默认进度条界面
6ExitDialog安装完成页
5.3 各对话框控件
DbConfig(MySQL 配置)
控件类型说明
ProductNameTitleText产品名称(智控平台)
CompanyTextText公司名称
ProductLogoBitmap产品 Logo
IS_USE_OLD_DBRadioButtonGroup是否使用已有数据库(是/否)
COMBOBOX_1ComboBox数据库类型选择(仅 MySQL)
DB_IP_1 ~ DB_IP_4EditIP 地址四段(4 个输入框)
DB_PORTEdit端口号
DB_USERNAMEEdit用户名(默认 root)
DB_PASSWORDEdit (密码)密码
PushButton_1PushButton连接测试
Text_6Text提示文字(使用新数据库时显示)
Text_7Text提示文字(使用已有数据库时显示)
RedisConfig
控件类型说明
IS_USE_OLD_REDISRadioButtonGroup是否使用已有 Redis(是/否)
REDIS_IP_1 ~ REDIS_IP_4EditIP 地址四段
REDIS_PORTEdit端口号
REDIS_USERNAMEEdit用户名(默认 root)
REDIS_PASSWORDEdit (密码)密码
5.4 维护/卸载模式

当检测到产品已安装时,进入 MaintenanceTypeDlg:

  • 删除(&R) 按钮 — 执行完整卸载
  • 图片风格按钮,无文字修复按钮
6
卸载流程设计
6.1 卸载触发器

卸载通过以下条件触发:

condition
REMOVE = "ALL"  OR  AI_INSTALL_MODE = "Remove"
6.2 卸载序列 (seq 1602-1610)
1
closeNode (seq 1610)
关闭正在运行的 Node 服务
2
uninstall_jar (seq 1602)
注销 OptiCloudService 服务
3
uninstall_redis (seq 1603)
停止并删除 Redis 服务
4
uninstall_mysql (seq 1604)
停止并删除 MySQL 服务
5
uninstall_influxdb (seq 1605)
停止并删除 InfluxDB 服务
6
uninstall_emqx (seq 1606)
停止并删除 EMQX 服务
7
uninstall_nginx (seq 1607)
停止并删除 Nginx 服务
8
del_base_dir
删除安装目录 (delBaseDir.bat)
MSI 文件删除引擎
清理已安装的文件和注册表,卸载完成
6.3 资源清理
  • AI_CLEAN_RESOURCES_UNINSTALL = 1 — 卸载时清理全部资源
  • AI_CLEAN_RESOURCES_USER_PROMPT_BASIC_UI = 0 — 不提示用户确认
  • AI_CLEAN_RESOURCES_USER_PROMPT_FULL_UI = 0 — 不提示用户确认
7
注册表设计
7.1 产品信息注册表
路径键值
HKLM\Software\浙江源创智控技术有限公司\智控平台Path[APPDIR](安装路径)
同上EasyProVersion产品版本号(例如 2.0.0,由安装/升级脚本写入和维护)
HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\智控平台 2.0.0DisplayName智控平台
同上DisplayVersion2.0.0
同上Publisher浙江源创智控技术有限公司
同上InstallLocation[APPDIR]
同上DisplayIcon[ARP_ICON_PATH]
同上UninstallString通过 MSI 卸载
同上NoModify1(不支持修改)
同上NoRepair1(不支持修复)
7.2 Advanced Installer 内部注册表
路径用途
HKLM\Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]\AI_ExePath自解压程序路径
HKLM\Software\Caphyon\Advanced Installer\Prereqs\[ProductCode]\[ProductVersion]\MICROSOFTVISUALCVC++ 预置条件安装标记
8
脚本目录 (script/)
脚本用途
delBaseDir.bat卸载时删除安装目录
setDynamicport.bat设置动态端口
升级包通用脚本\运行增量升级.ps1增量升级总控入口 — 安装/升级时执行所有 资源_{版本号} 升级脚本,再做版本通用资源部署与动态端口回填。由安装包/升级包工程自定义操作调用(详见 4.3)
升级包通用脚本\检验版本.ps1升级前版本校验
升级包通用脚本\卸载所有服务.ps1扫描安装目录所有 uninstall.bat,按"应用→中间件→数据库"优先级停止并删除服务
升级包通用脚本\启动所有服务.ps1升级完成后启动所有平台服务
版本通用资源\deploy_common.ps1版本通用资源部署脚本 — 将 版本通用资源/ 中的 yuntrol、build、JAR 等大体积文件部署到 [APPDIR],采用「Remove-DirectorySafely 安全删除旧目录(含进程终止、权限重置三级兜底),再拷贝新文件」策略防止残留旧版本文件。覆盖 service\config\ 后会做动态端口回填(从中间件本地配置读实际端口回填新 yml,防止动态端口被冲回默认值)。由 运行增量升级.ps1 在适当时机调用。安装路径强制从注册表 HKLM:\SOFTWARE\浙江源创智控技术有限公司\智控平台\Path 读取(不再接受 -InstallDir 参数);-SourceDir 为可选参数,未提供时使用本脚本所在目录
源创软件服务管理.ps1 / 源创软件服务管理.exe服务管理工具(启动/停止/重启)
端口手动替换脚本.ps1手动替换配置文件中的端口
端口自动替换脚本.ps1自动检测并替换端口
mysql-port-refresh.ps1MySQL 端口刷新
redis-port-refresh.ps1Redis 端口刷新
influxdb-port-refresh.ps1InfluxDB 端口刷新
nginx9082-port-refresh.ps1Nginx 9082 端口刷新
service-port-refresh.ps1服务端口刷新
EMS Lite.html嵌入式 EMS Lite 页面
yc_new.png / 2.源创智控标志B(2025年12月版).png / yuncontrol.ico品牌图标资源
9
关键技术要点
9.1 提权执行机制

所有服务和应用的安装/卸载操作均通过 viewer.exe 以管理员权限执行。viewer.exe 是 Advanced Installer 内置的工具,用于在 MSI 安装上下文中以提升的权限运行子进程。

关键参数
text
/EnforcedRunAsAdmin     # 强制以管理员身份运行
/RunAsAdmin             # 以管理员权限运行
/HideWindow             # 隐藏子进程窗口
/dir <目录>             # 设置工作目录
[#filekey]              # 引用 MSI 中的文件(通过 file key)
9.2 端口配置机制

安装过程中用户输入的 IP/端口信息,通过 AI_DATA_SETTER_X(Type=51,设置 CustomActionData)传递给后续的 PowerShell 操作(PowerShellScriptLauncher.dll)。配置数据经过 Base64 编码,解码后包含:

  1. DigitalSignature — 数字签名策略
  2. Flags — 执行标志
  3. LaunchDir — 执行目录
  4. Params — PowerShell 脚本参数
  5. Property — 目标属性名
9.3 组件依赖关系
OptiCloudService (JAR) · Nginx
核心业务逻辑 + 反向代理/前端
MySQL
持久化
Redis
缓存
InfluxDB 2
时序数据
EMQX
MQTT 中间件
MinIO
对象存储
OptiView
Node.js 前端
JDK (Java)
所有 Java 组件依赖
9.4 构建产物
  • 安装包: EasyPro V2.1.1.exe(单文件自解压包,约 2~3 GB)
  • 提取到临时目录:%APPDATA%\浙江源创智控技术有限公司\智控平台 2.0.0\install
10
注意事项与已知约束
  1. 版本号不一致:安装包文件名为 V2.1.1,但 MSI ProductVersion 为 2.0.0。注册表 HKLM\SOFTWARE\浙江源创智控技术有限公司\智控平台\EasyProVersion 记录的是产品版本号。安装过程中,基础版本安装先写入 ProductVersion,随后增量升级脚本执行完毕后由 运行增量升级.ps1 统一更新为已执行的最高版本号(各子脚本也会写入自己版本号,两层写入最终一致,见升级包文档 2.3.1)。
  2. 安装包含升级:安装包同时打包 资源/(基础版本)和所有 资源_{版本号}/(增量升级)。安装流程为:基础版本安装 → 依次执行增量升级脚本。这意味着每次安装都会达到最新的已打包版本,无需安装后再单独运行升级包。
  3. 安装包体积:由于包含 JDK、MySQL、EMQX 等完整软件栈以及多个版本的增量升级资源,安装包体积较大(2GB+),建议通过 LZMA 高压缩级别优化。
  4. 提权要求:整个安装过程需要管理员权限,普通用户无法安装。
  5. 并发安装:不支持同时运行多个安装实例。卸载时不显示进度 UI(ExtUI=true 仅用于安装)。
  6. 端口冲突:setDynamicport.bat 和端口刷新脚本用于自动检测和避免端口冲突。
  7. 注册表版本号两层写入:增量升级脚本 upgrade_to_{版本号}.ps1 在自己全部步骤成功后会写入对应版本号;总控脚本 运行增量升级.ps1 在所有版本执行完毕后再用"已执行的最高版本号"做最终覆盖(权威写入)。两层写入最终值一致,详见升级包文档 2.3.1。
  8. 版本通用资源管理(防止包膨胀):版本通用资源/ 中的文件(yuntrol、build、JAR)在每次安装/升级时由 deploy_common.ps1 以「删除旧目录 → 拷贝新目录」的方式覆盖。这些资源每个版本都会变更且体积巨大(合计约 1 GB),如果复制到每个 资源_{版本号}/ 中将导致安装包和升级包随版本积累迅速膨胀。因此,所有 资源_{版本号}/ 不得包含这些大文件,统一由 版本通用资源/ 提供。目录结构约定:版本通用资源内的路径 = 安装后 [APPDIR] 中的目标路径。覆盖 service\config\ 后还会做动态端口回填(从中间件本地配置读实际端口回填新 yml)。
  9. 增量升级入口脚本:安装包执行增量升级的总控脚本是 升级包通用脚本\运行增量升级.ps1(与升级包共用),不是 script\run_upgrades.ps1(该文件实际不存在)。该脚本会先做 MySQL 服务前置检查、扫描版本、依次执行升级、部署通用资源、动态端口回填、更新版本号,并采用"失败继续 + 末尾汇总"策略。
  10. V2.1.1 新增组件:通过 资源_2.1.1\组件新增\ 引入 EasyPro 运维助手(EasyProTool,桌面端应用,创建桌面快捷方式)和 Qdrant 向量数据库(注册为 Windows 服务 SctechQdrantService),由 upgrade_to_2.1.1.ps1 在增量升级阶段部署。
A
附录 A:Advanced Installer 配置速查
配置项路径 / 说明
产品详情Product Details → Product Name / Version / Upgrade Code
文件管理Resources → Files and Folders → Add 资源/ 目录
注册表Resources → Registry
自定义操作Custom Actions → Sequences → Install / Uninstall
对话框User Interface → Dialogs
预置条件Prerequisites
启动条件Launch Conditions
构建Builds → DefaultBuild → Build

文档版本:1.1 | 最后更新:2026-07-28 | 修正增量升级入口脚本(run_upgrades→运行增量升级)、补充动态端口回填、V2.1.1 新增组件、版本号两层写入