2026-08-26 17:46:52 +08:00
# Simple3 三端系统架构设计
2026-05-29 18:16:34 +08:00
2026-08-26 17:46:52 +08:00
> **Migu2.0 仓库说明**:平台后端已拆至本仓库,工程名为 **`MiGu.Server`**(原 `Platform.Server`)。Simple3 与前端源码仍在兄弟仓库 `Simple`。下文中的 `MiGu.Server` 即指本仓库 `MiGu.Server/` 目录。
2026-05-29 18:16:34 +08:00
> 文档版本:v1.7.0
> 更新日期:2026-05-20
2026-08-26 17:46:52 +08:00
> 范围:Simple3(主程序 / 设计端 / 调度引擎宿主 / 进程编排器)+ Platform(业务平台 · 同时承载管理员前端 platform-vue 与运营前端 rcsmonitor-vue · 共用一套 WebAPI · **主入口为 MiGu.Server,登录后拉起 Simple3**)
2026-05-29 18:16:34 +08:00
> v1.7.0 变更(多主题色板 · 工业紫默认):
> - **6 套可切换主题**: `themes.ts` 定义色板(工业紫、深邃蓝、翡翠绿、熔铁赤、石墨钢、琥珀金);默认 **工业紫色** `#6a1b9a`(比 v1.6.2 的 `#641393` 更深更饱和,解决登录后内容区配色过淡)。
> - **运行时注入**: `applyThemeVars()` 向 `:root` 写入 `--mg-*`; `ui` store 的 `themeId` 持久化到 `localStorage`(键 `simple.ui.state`)。
> - **ThemeSwitcher**: `AppShell` 顶栏下拉切换;`main.ts` 启动时 `applyCurrentTheme()`。
> - **壳层变量化**: `AppShell` 侧栏/顶栏/光晕/菜单由硬编码紫改为 `var(--mg-*)`;登录页 `BlankLayout` / `LoginView` 光球与 hero 同步走变量(待办见 §18)。
> - 详见 §17.7、§18「实现进度与待办」。
> v1.6.2 变更(全站统一玻璃风):
> - **品牌主色回归 v1.6 的 `#641393`**:与登录页 hero 一致;保留 FRLD logo 仍在侧栏左上角;侧栏 / 顶导 / 内容卡片采用统一玻璃设计令牌(`--mg-glass-*`、`--mg-radius` 14/20px)。
> - **整站背景深紫渐变**: `body` 固定 `radial-gradient + linear-gradient(135deg,#1a0930 → #2d1456 → #0f0420)`; AppShell 内悬浮三颗低亮度光球(drift 动画 22s)增加层次感。
> - **登录页玻璃 hero 还原**:双栏 hero · 「迷」徽标 · 18 颗 twinkle 星 · 三色 drift 光球 · `mg-glass-lg` 主卡(白色 8% 半透明 + 28px blur + 双层 inset 边框)。
> - **AppShell 玻璃化**:侧栏 `rgba(22,4,31,0.85→0.88)` 深紫渐变 + 18px backdrop blur + 菜单 active 状态紫色渐变背景 + 3px inset 紫光;顶导 `rgba(22,4,31,0.6)` 玻璃;底栏 `rgba(22,4,31,0.25)` 玻璃;用户头像 / 标签 / dropdown 全部玻璃化。
> - **内容区卡片自动玻璃化**: `<el-main class="main mg-content">` 容器内所有 `el-card / el-statistic / el-table / el-descriptions / el-input / el-tag / el-progress / el-collapse / el-radio-button / el-input-number / el-switch / el-pagination` 均沿用紫黑半透明 + 14px 圆角 + hover 上浮(translateY -2px) + shadow 升级;KPI 行 `class="kpi-row"` 内 `el-statistic` 自动玻璃卡。
> - **路由切换微动效**: `<router-view>` 包裹 fade-up( 250ms cubic-bezier .25,.8,.25,1),轻盈不花哨。
> - 详见 §17「视觉规范」。
> v1.6.1 变更(视觉对齐 FRLD · 已在 v1.6.2 中部分回退):
> - **侧边栏左上角加 FRLD(法睿兰达 FAIRYLAND) logo**: `public/FRLD-logo-white.png` 展开态 36px 高、`public/FRLD-logo-white-no_title.png` 折叠态 26px 高 + 缩写「迷毂」。Logo 来源 `E:\ddms\frontend\public`。**v1.6.2 保留 FRLD logo,但取消 #7c3aed 主色与 ddms 风格菜单态,全部回到 v1.6 玻璃风。**
> v1.6 变更(品牌 & 视觉规范):
> - **产品品牌命名定为「迷毂」**:Vue 外壳产品代号、登录窗 / 侧边栏 logo、`<title>`、用户可见文案、`favicon` 全部统一为「迷毂」;后端工程标识 `MiGu.Server` / 前端工程目录 `simple-platform-vue` 不变(避免影响 csproj/pnpm 包名)。
> - **品牌主色紫色系(v1.6 初版 #641393, v1.6.1 调整为 #7c3aed) **:全站统一紫色调;Element Plus 通过 CSS 变量覆盖(`--el-color-primary` 与 `--el-color-primary-rgb` 双重保险);状态色保持系统默认。
> - **登录窗背景**:使用品牌图片资源 `frontends/apps/simple-platform-vue/public/login-bg.jpg`(原始素材 `wall_Beach.jpg`, 1.8 MB) + 紫色径向 + 线性渐变叠加。
> - 详见 §17「视觉规范」。
> v1.5 变更:
> - **持久层多 Provider 支持**:抽象层基于 EF Core,新增对 **MySQL / PostgreSQL / SQL Server** 的原生支持;SQLite 仍为默认/边缘单机首选。
> - **高可用方案按 DB 矩阵化**:除 ROSE HA( SQLite 块级镜像)外,新增 **MySQL InnoDB Cluster / MGR**、**PostgreSQL Patroni / 流复制**、**SQL Server Always On AG** 三种企业级 HA 路径;现场可根据已有 IT 基础设施挑选。
> - 新增 §7.4–§7.7 四类 DB 部署形态详述、选型矩阵、连接串与配置示例、迁移注意事项。
> v1.4 变更(保留):
2026-08-26 17:46:52 +08:00
> - **明确进程编排归属**: Platform 后端进程 `MiGu.Server.exe` 不再被视为"独立的业务平台服务",而是 Simple3 内置 `SystemMission`( `StartPlatformMission`)拉起、被 Simple3 Bootstrapper 守护的**受控子进程**; RCSMonitor 不存在独立后端进程,复用 MiGu.Server。
2026-05-29 18:16:34 +08:00
> - **整体定位章节重写**:明确"两类后端可执行文件 + 一个进程编排者"的关系,避免读者误以为 Platform 是用 Windows Service / systemd 独立部署的服务。
> v1.3 变更(保留):
> 1. **取消 RCSMonitor.Server**: `rcsmonitor-vue` 不再有独立后端进程,直接连 MiGu.Server;由 JWT 中的 `scope=RCSMonitor` + 角色权限码 + `WidgetGrant` 三层限制可见控件与可调用 API。
2026-08-26 17:46:52 +08:00
> 2. **3D 渲染基座定为 webVRender**:复用 Simple3 WebTerminal 已经提供的 webVRender,以 Web Component / iframe 嵌入到 platform-vue 与 rcsmonitor-vue,两端共享同一份 Workspace 协议(PutModel / PutPointCloud / SetCamera 等 DTO)。
> 3. **反向代理选定 YARP**: MiGu.Server 内置 [YARP](https://microsoft.github.io/reverse-proxy/) 做 `/api/sl/*` → Simple3 WebAPI 的反代;带鉴权与审计中间件。
2026-05-29 18:16:34 +08:00
> 4. **运行模式持久化到 `simple.json`**:登录窗"记住模式"勾选后写入 `simple.json.runMode`;下次启动直接进入。
> 5. **Web-Enabled 下严禁同时本地操作**: CycleGUI 主窗只剩"服务状态窗",所有业务面板硬性隐藏;不提供"临时启用本地 UI"开关。
> 6. **共享前端组件库**:使用 pnpm workspace + `frontends/packages/sl-controls`,封装 Workspace3D / 地图 / 任务 / CAD 等可复用 Vue 组件。
---
## 目录
1. [总体目标与拆分原则 ](#1-总体目标与拆分原则 )
2. [顶层系统架构(C4 - 容器视图) ](#2-顶层系统架构c4---容器视图 )
3. [启动登录与运行模式 ](#3-启动登录与运行模式 )
4. [进程拓扑与生命周期 ](#4-进程拓扑与生命周期 )
5. [三端职责矩阵 ](#5-三端职责矩阵 )
6. [共享内核分层 ](#6-共享内核分层 )
7. [数据库与高可用(多 Provider) ](#7-数据库与高可用多-provider )
8. [用户 / 权限模型(含控件级权限) ](#8-用户--权限模型含控件级权限 )
9. [平台配置中心 ](#9-平台配置中心 )
10. [关键交互序列 ](#10-关键交互序列 )
11. [通信协议矩阵 ](#11-通信协议矩阵 )
12. [部署拓扑 ](#12-部署拓扑 )
13. [仓库与解决方案规划 ](#13-仓库与解决方案规划 )
14. [落地路线图 ](#14-落地路线图 )
15. [已确认决策 ](#15-已确认决策 )
16. [现有代码影响面 ](#16-现有代码影响面 )
17. [视觉规范(v1.6 新增) ](#17-视觉规范v16-新增 )
18. [实现进度与待办(对照代码库) ](#18-实现进度与待办对照代码库 )
---
## 1. 总体目标与拆分原则
### 1.1 整体定位
一套为 AGV/AMR 现场打造的「**设计 → 调度 → 运营监控**」三端协同系统。
2026-08-26 17:46:52 +08:00
**三个"端"对应三套用户体验,但只有两类后端可执行文件,且都由 Simple3 统一编排:**
2026-05-29 18:16:34 +08:00
| 端 | 前端载体 | 后端可执行文件 | **启动者** | 角色 | 写权限 |
|----|----------|----------------|------------|------|--------|
2026-08-26 17:46:52 +08:00
| **Simple3 桌面端** | CycleGUI 客户端 | `Simple3.exe` 自身 | 用户双击 / 服务管理器 | 主程序 + 调度引擎宿主 + 全功能 WebAPI 提供者 + **进程编排器(Bootstrapper + SystemMission) ** | **全功能** |
| **Platform 管理端** | `platform-vue` (Vue 3 SPA) | `MiGu.Server.exe` | **Simple3 内置 `SystemMission.StartPlatformMission` 拉起** ( Web-Enabled 时自动 spawn;崩溃由 Simple3 Watchdog 重启) | **Simple3 全能力套壳** :管理员侧 Vue 前端复刻所有设计/调度控件 + 业务平台特有能力(配置中心 / 外部对接 / 库位 / 账号) | **全功能** (等价 Simple3) |
| **RCSMonitor 运营端** | `rcsmonitor-vue` (Vue 3 SPA) | **共用 `MiGu.Server.exe`** (无独立后端进程) | 随 MiGu.Server 一起被 Simple3 拉起 | 运营监控前端:按 `scope=RCSMonitor` + 角色权限 + WidgetGrant 三层裁剪 UI;可对任务/车辆执行白名单运维动作 | **受限** (仅运维白名单) |
2026-05-29 18:16:34 +08:00
**关键澄清(v1.4 重点):**
2026-08-26 17:46:52 +08:00
- **后端可执行文件只有 2 个**: `Simple3.exe` 、`MiGu.Server.exe` 。
- **进程启动只有 1 个真正的入口**:用户/服务管理器只启动 `Simple3.exe` , MiGu.Server 由 Simple3 自己拉起,**不需要也不允许独立安装为 Windows Service**。
- **进程编排归属 Simple3**: `SystemMission` 是 Simple3 自带的 Mission 体系扩展(与业务 Mission 区分开),专门负责拉起/守护/优雅停止 MiGu.Server。Bootstrapper 监听心跳、按指数退避重启、统一记录 OpsAuditLog。
2026-05-29 18:16:34 +08:00
- **Vue 两份产物,同一进程托管**: MiGu.Server 在 `/admin/*` 路径下托管 `platform-vue` ,在 `/monitor/*` 路径下托管 `rcsmonitor-vue` 。
- **鉴权与限权完全在 MiGu.Server 完成**: RCSMonitor 不持有任何独立后端代码(没有 `RCSMonitor.Server.exe` 、没有独立库)。
- **Desktop-Only 模式下,SystemMission 不触发**: MiGu.Server 根本不会启动,Vue 端无法访问。
2026-08-26 17:46:52 +08:00
> 一句话:**用户点 Simple3.exe,由 Simple3 通过 Mission 把 Platform 后端拉起来;Platform 后端再把两套 Vue 端服好。**
2026-05-29 18:16:34 +08:00
### 1.2 核心拆分原则
1. **Crash-Isolation(搞不崩主服务)**
2026-08-26 17:46:52 +08:00
Platform / RCSMonitor 对**地图/任务/车辆等写操作**都通过 Simple3 的 `/api/*` 网关;主服务侧做白名单 + 幂等校验 + 频次限流 + 审计。Platform-Vue 走完整 API 集,RCSMonitor-Vue 仅限运维白名单 `/api/ops/*` 。
2026-05-29 18:16:34 +08:00
2. **共享数据模型,独立运行时**
2026-08-26 17:46:52 +08:00
地图、车辆、任务三端共用 `SimpleCore` 的 DTO;调度引擎只在 Simple3 里跑,Platform 只持有"投影"。
2026-05-29 18:16:34 +08:00
3. **库分离 + Provider 抽象** (详见 §7)
2026-08-26 17:46:52 +08:00
- `simple_main` 由 Simple3 持有;`platform` 由 Platform 持有(含 RCSMonitor 审计/标注);不再有独立的 `rcsmonitor_local` 。
2026-05-29 18:16:34 +08:00
- 持久层基于 **EF Core** , **通过 Provider 切换底层数据库**:默认 **SQLite** (边缘单机,配 ROSE HA);可选 **MySQL / PostgreSQL / SQL Server** ,匹配现场已有 IT 基础设施与企业级 HA 能力(详见 §7.4–§7.7)。
- 高可用方案随 Provider 不同:SQLite 用 ROSE HA; MySQL 用 InnoDB Cluster; PostgreSQL 用 Patroni; SQL Server 用 Always On AG。
4. **统一协议层**
2026-08-26 17:46:52 +08:00
所有进程间调用统一 **WebAPI( REST/JSON) + WebSocket 事件流** ; Vue 端直接对接 Platform 后端的 REST + WS; Platform 后端用 **YARP** 反代 Simple3 的设计/调度 API。
2026-05-29 18:16:34 +08:00
5. **统一身份**
2026-08-26 17:46:52 +08:00
`SimpleShared.Auth` 提供 JWT + RBAC; Platform 是主权限源(在线时),Simple3 在 Desktop-Only 下用本地用户表降级。
2026-05-29 18:16:34 +08:00
6. **运行模式互斥(强约束)**
- **Desktop-Only**: CycleGUI 客户端独占;WebAPI / Platform **不启动** 。
- **Web-Enabled**: WebAPI 启动;Platform 子进程启动;CycleGUI 客户端**所有业务面板硬性隐藏**,仅保留"服务状态窗"; **不允许"临时启用本地 UI"开关**——任何本地操作必须重启进设计模式。
- 模式由**启动登录界面**选择并持久化到 ** `simple.json.runMode` **;下次启动可勾选"记住选择"直接进入。
7. **控件级权限**
Platform / RCSMonitor 登录后向 `SimpleShared.Auth` 拉取 `EffectivePermissions { allowedOps[], visibleWidgets[] }` ;Vue 渲染器按集合裁剪面板、按钮、菜单、右键项。
---
## 2. 顶层系统架构(C4 - 容器视图)
```mermaid
flowchart TB
subgraph Users["使用方"]
U1["现场工程师 (CycleGUI 桌面)"]
U2["管理员 (浏览器)"]
U3["运营 / 班组长 (浏览器)"]
U4["第三方 MES / WMS / RCS"]
end
subgraph Host["主控宿主机 / 边缘服务器"]
2026-08-26 17:46:52 +08:00
subgraph SL["Simple3 主进程 (CycleGUI + ASP.NET Core 8)"]
2026-05-29 18:16:34 +08:00
direction TB
SL_Login["启动登录窗<br/>(模式选择 Desktop / Web)"]
SL_UI["CycleGUI 客户端<br/>(Desktop-Only: 全功能<br/>Web-Enabled: 仅状态窗)"]
SL_Boot["Bootstrapper<br/>(进程守护 + 心跳)"]
SL_Sched["SimpleScheduler 调度引擎"]
SL_Domain["领域服务<br/>(SimpleCore + 命令总线)"]
SL_API["全功能 WebAPI + WS Hub<br/>(Kestrel)<br/>(仅 Web-Enabled 启动)"]
SL_WV["webVRender Web Terminal<br/>(已有 :8223)<br/>(Vue 端嵌入)"]
SL_DB[("SQLite: simple_main.db<br/>WAL")]
end
subgraph PL["Platform 后端 (ASP.NET Core 8 WebAPI)"]
direction TB
PL_Static["静态资源<br/>/admin/* → platform-vue dist<br/>/monitor/* → rcsmonitor-vue dist"]
PL_API["Platform WebAPI<br/>(配置中心 / 外部对接 / 库位 / 账号)"]
2026-08-26 17:46:52 +08:00
PL_YARP["YARP 反代<br/>/api/sl/* → Simple3 WebAPI<br/>+ Auth/审计中间件"]
2026-05-29 18:16:34 +08:00
PL_Auth["SimpleAuth<br/>(JWT 签发 + Scope 校验)"]
PL_Adp["外部系统适配器<br/>(MES / WMS / RCS)"]
PL_DB[("SQLite: platform.db<br/>WAL")]
end
end
subgraph Vues["两份 Vue 编译产物 (同一进程托管)"]
AdminVue["platform-vue<br/>(管理员全功能)"]
MonVue["rcsmonitor-vue<br/>(运营按权限)"]
end
subgraph External["外部"]
Veh["AGV 车端"]
TPS["第三方设备<br/>(电梯 / 充电桩 / IO)"]
end
U1 --> SL_Login
SL_Login --> SL_UI
U2 -- "https://host:8080/admin" --> PL_Static
U3 -- "https://host:8080/monitor" --> PL_Static
U4 -- "REST / OPC-UA" --> PL_API
PL_Static --> AdminVue
PL_Static --> MonVue
AdminVue -- "REST + WSS" --> PL_API
MonVue -- "REST + WSS (scope=RCSMonitor)" --> PL_API
AdminVue -. "iframe / WebComponent" .-> SL_WV
MonVue -. "iframe / WebComponent (read-only)" .-> SL_WV
PL_API --> PL_Auth
PL_API --> PL_YARP
PL_YARP -- "WebAPI + WS<br/>(Web-Enabled 时)" --> SL_API
PL_API --> PL_DB
PL_API --> PL_Adp
SL_Boot == "SystemMission.StartPlatformMission<br/>spawn + heartbeat + watchdog" ==> PL
SL_API <--> SL_Sched
SL_API <--> SL_Domain
SL_Domain --> SL_DB
SL_Sched -- "TCP / WS / OPC-UA" --> Veh
PL_API --> TPS
```
**要点:**
2026-08-26 17:46:52 +08:00
- **只有两个后端可执行文件**: `Simple3.exe` 与 `MiGu.Server.exe` ; RCSMonitor 不再有独立后端。
- **`MiGu.Server.exe` 由 Simple3 的 SystemMission 拉起**:用户只启动 Simple3; Web-Enabled 时 `StartPlatformMission` 自动 spawn MiGu.Server,子进程心跳异常由 Bootstrapper 重启。
2026-05-29 18:16:34 +08:00
- **`PL_Static` 同一进程双 SPA 托管**:浏览器访问路径决定加载哪份 dist,登录时也按路径决定 `scope` 参数。
2026-08-26 17:46:52 +08:00
- **`PL_YARP` 把 `/api/sl/*` 反代到 Simple3**:YARP 在请求转发前后挂中间件做 JWT 校验、权限码二次确认、写操作审计、`Idempotency-Key` 透传、Scope 检查(RCSMonitor 的请求仅允许命中 `/api/ops/*` 这一前缀)。
- **3D 由 Simple3 的 webVRender 提供**: Vue 端通过 iframe 或 Web Component 嵌入 webVRender 的页面,业务交互(选择、点击坐标等)通过 `postMessage` 与 Vue 桥接。
2026-05-29 18:16:34 +08:00
- **Desktop-Only 模式**: `SL_API` 、`PL` 都不启动;Vue 无法访问;CycleGUI 是唯一入口。
- **Web-Enabled 模式**: CycleGUI 退化为状态窗;所有业务操作通过 Vue 完成;硬性不允许同时本地操作。
---
## 3. 启动登录与运行模式
### 3.1 启动流程
```mermaid
flowchart TB
2026-08-26 17:46:52 +08:00
Start([Simple3.exe 启动]) --> CheckPrev{simple.json 中<br/>runMode 已记忆?}
CheckPrev -- 否 --> Login[弹出登录窗<br/>Simple3 Boot Login]
2026-05-29 18:16:34 +08:00
CheckPrev -- 是 --> AutoLogin[自动按记忆模式登录<br/>仍需鉴权身份]
Login --> Form
AutoLogin --> ModeSwitch
subgraph Form[登录窗内容]
F1[用户名 / 密码]
F2[运行模式 RadioButtons]
F2a[Desktop-Only]
F2b[Web-Enabled]
F3[记住选择 CheckBox<br/>勾选后写 simple.json.runMode]
F4[登录按钮]
end
Form --> Auth[本地用户表认证<br/>SimpleShared.Auth]
Auth -- 失败 --> Login
Auth -- 成功 --> Persist{勾选了<br/>记住选择?}
Persist -- 是 --> WriteCfg[写 simple.json<br/>runMode + rememberRunMode=true]
Persist -- 否 --> ModeSwitch
WriteCfg --> ModeSwitch
ModeSwitch{运行模式?}
ModeSwitch -- Desktop-Only --> Path1
ModeSwitch -- Web-Enabled --> Path2
subgraph Path1[Desktop-Only 路径]
D1[启动 CycleGUI 全功能 UI]
D2[启动调度引擎]
D3[加载场景 / 插件]
D4["不启动 WebAPI / Platform"]
end
subgraph Path2[Web-Enabled 路径]
W1[启动 Kestrel + WebAPI]
W2[启动调度引擎]
W3[加载场景 / 插件]
W4[启动 WebTerminal]
W5[启动 MiGu.Server.exe]
W6[CycleGUI 仅渲染服务状态窗<br/>所有业务面板硬性隐藏]
W7[等待 Vue 端登录]
end
```
### 3.2 登录界面(CycleGUI Panel)
字段与控件:
| 元素 | 控件 | 说明 |
|------|------|------|
2026-08-26 17:46:52 +08:00
| 标题 | Label | "Simple3 · 启动登录" |
2026-05-29 18:16:34 +08:00
| 用户名 | TextInput | 默认上次登录用户 |
| 密码 | TextInput (password) | — |
| 运行模式 | RadioButtons | `Desktop-Only` / `Web-Enabled` |
| 记住选择 | CheckBox | 勾选后写入 `simple.json.runMode` 与 `simple.json.rememberRunMode=true` |
| 监听地址(Web-Enabled 显示) | TextInput | 默认 `0.0.0.0` |
| WebAPI 端口(Web-Enabled) | NumberInput | 默认 `7001` |
| Platform 端口(Web-Enabled) | NumberInput | 默认 `8080` (同时承载 admin/monitor 两份 SPA) |
| 高级(Web-Enabled 折叠) | Collapsing | webVRender 端口 / Swagger 是否启用 / CORS 白名单 |
| 登录按钮 | Button | 通过后销毁登录窗,进入选定模式 |
| 退出按钮 | Button | 不进入主程序,退出进程 |
2026-08-26 17:46:52 +08:00
> 实现位置:新增 `Simple3/UI/BootLoginPanel.cs`,由 `Program.Main` 在 `Enssentials.Load` 之后、`Startup.EntryPoint` 之前调用 `GUI.PromptAndWaitPanel(BootLoginPanel.Build())`(阻塞式模态)。
2026-05-29 18:16:34 +08:00
#### simple.json 增量字段
```json
{
"runMode" : "WebEnabled" , // "DesktopOnly" | "WebEnabled"
"rememberRunMode" : true ,
"webApi" : {
"host" : "0.0.0.0" ,
"port" : 7001 ,
"wsPort" : 7002 ,
"enableSwagger" : false
},
"platform" : {
"port" : 8080 ,
"wsPort" : 8081
},
"webVRender" : {
"port" : 8223
}
}
```
### 3.3 模式切换与互斥保证(强约束)
| 场景 | 行为 |
|------|------|
| Desktop-Only 运行中 | Kestrel 不监听任何端口;MiGu.Server 不 spawn;尝试连接 7001/8080 直接 ECONNREFUSED |
2026-08-26 17:46:52 +08:00
| Desktop-Only → Web-Enabled | **必须退出 Simple3 重新登录** ;不支持热切(避免半启动状态) |
2026-05-29 18:16:34 +08:00
| Web-Enabled 运行中 | CycleGUI 主窗只显示状态窗(运行时长 / 在线用户 / 端口 / 主备状态)+「停止」按钮 |
| Web-Enabled 下用户尝试本地控件 | **所有业务面板硬性不渲染** ;Workspace 视口也不显示;用户无法通过任何方式重新启用 |
| Web-Enabled 异常退出 | Watchdog 重新拉起,恢复到 Web-Enabled;不强迫用户重选 |
2026-08-26 17:46:52 +08:00
| 想取消"记住模式" | 启动时按住 `Shift` 或运行 `Simple3.exe --re-login` 强制弹出登录窗 |
2026-05-29 18:16:34 +08:00
| 远程开关模式 | Platform 管理员可以发 `POST /api/sl/runmode/reset` 把 `rememberRunMode` 置为 `false` , **仅在下次重启时生效** |
### 3.4 服务状态窗(Web-Enabled 唯一可见的 CycleGUI 面板)
```text
┌──────────────────────────────────────────┐
2026-08-26 17:46:52 +08:00
│ Simple3 Service Status │
2026-05-29 18:16:34 +08:00
├──────────────────────────────────────────┤
│ 模式: Web-Enabled │
│ 启动时间: 2026-05-18 09:00:00 │
│ 运行时长: 00:23:15 │
│ WebAPI: http://0.0.0.0:7001 [OK] │
│ WebSocket: ws://0.0.0.0:7002 [OK] │
│ webVRender: http://0.0.0.0:8223 [OK] │
│ Platform: :8080 [Running, pid=12345] │
│ 节点角色: Active (ROSE) │
│ 在线 Vue 客户端: 7 (admin=3, monitor=4) │
│ 调度循环: 50Hz | 任务: 14 / 32 │
│ │
│ [Open Logs] [Restart Web] [Shutdown] │
└──────────────────────────────────────────┘
```
> 状态窗**不包含**"启用本地 UI"按钮——切回设计模式必须重启进程并选择 Desktop-Only。
---
## 4. 进程拓扑与生命周期
### 4.1 进程关系图
```mermaid
flowchart LR
2026-08-26 17:46:52 +08:00
A["Simple3.exe<br/>启动 + 登录"] --> Mode{运行模式}
2026-05-29 18:16:34 +08:00
Mode -- Desktop-Only --> D[CycleGUI 全功能 UI]
D --> SchedD[调度引擎]
D --> DB1D[(simple_main.db)]
Mode -- Web-Enabled --> WHost[Kestrel + WebAPI :7001/7002]
WHost --> SchedW[调度引擎]
WHost --> DB1W[(simple_main.db)]
WHost --> WV[WebTerminal :8223<br/>webVRender]
WHost --> StatusPanel[CycleGUI 状态窗]
2026-08-26 17:46:52 +08:00
subgraph SysMis["Simple3 SystemMission 体系"]
2026-05-29 18:16:34 +08:00
E["StartPlatformMission<br/>(SystemMission 子类)"]
SM_Reg["SystemMissionRegistry"]
SM_Hb["HeartbeatChannel<br/>(named pipe)"]
end
WHost --> SysMis
E ==> P["MiGu.Server.exe<br/>(ASP.NET Core WebAPI + YARP)<br/>:8080/8081"]
P --> PStatic["静态托管 2 份 dist:<br/>/admin → platform-vue<br/>/monitor → rcsmonitor-vue"]
P --> PDB[(platform.db)]
P <-. JSON heartbeat .-> SM_Hb
subgraph 守护
W["ProcessWatchdog<br/>(指数退避重启 + 优雅停止)"]
end
SysMis --- W
W -. monitor .- P
```
### 4.2 SystemMission 体系(v1.4 新增章节)
2026-08-26 17:46:52 +08:00
Simple3 现有 `Mission` 是业务任务(运送/搬运),新增 ** `SystemMission` ** 作为同一基类的"系统级 Mission",用于拉起并守护进程级服务。它**不参与调度引擎的 DispatchLoop**,由 Bootstrapper 直接驱动。
2026-05-29 18:16:34 +08:00
**关键类:**
| 类型 | 类型 / 命名空间 | 职责 |
|------|------------------|------|
2026-08-26 17:46:52 +08:00
| `SystemMission` (抽象) | `Simple3.RCS.SystemMissions` | 继承自现有 `Mission` ,新增 `Spawn() / Stop() / Heartbeat()` 等抽象方法 |
| `StartPlatformMission` | `Simple3.RCS.SystemMissions` | 拉起 `MiGu.Server.exe` ;通过命名管道 `\\.\pipe\simple-bootstrap` 接收心跳 |
| `SystemMissionRegistry` | `Simple3.Bootstrap` | 单例,注册所有 SystemMission; Web-Enabled 启动时遍历 `Spawn()` ;进程退出时遍历 `Stop()` |
| `ProcessHost` | `Simple3.Bootstrap` | 封装 `System.Diagnostics.Process` + 重定向标准输出到日志 + 退出码捕获 |
| `HeartbeatChannel` | `Simple3.Bootstrap` | 命名管道服务端;JSON 协议 `{ "ts", "rss", "queueDepth", "status" }` ; 3 个周期未达 → 标记不健康 |
| `ProcessWatchdog` | `Simple3.Bootstrap` | 不健康时 `Stop() + Spawn()` ;指数退避 1s → 2s → 4s → 8s → 30s(上限) |
| `RunMode` (枚举) | `Simple3.Bootstrap` | `DesktopOnly` / `WebEnabled` |
2026-05-29 18:16:34 +08:00
**生命周期:**
```text
2026-08-26 17:46:52 +08:00
Simple3 启动 (Web-Enabled)
2026-05-29 18:16:34 +08:00
├─ Kestrel Up :7001/7002
├─ WebTerminal Up :8223
├─ SchedulerRuntime.Start()
└─ SystemMissionRegistry.SpawnAll()
└─ StartPlatformMission.Spawn()
├─ ProcessHost.Start("MiGu.Server.exe", args)
├─ HeartbeatChannel.Listen(pipeName)
└─ Watchdog.Track(pid)
每 5s:
HeartbeatChannel ← MiGu.Server (JSON tick)
异常:
Watchdog 触发 → ProcessHost.Kill() → 退避等待 → Spawn() 重试
连续 5 次失败 → 状态窗高亮 "Platform Down",停止重试,需人工介入
2026-08-26 17:46:52 +08:00
Simple3 优雅停机:
2026-05-29 18:16:34 +08:00
SystemMissionRegistry.StopAll()
└─ StartPlatformMission.Stop()
├─ 发送 SIGTERM 等价(命名管道 "shutdown" 消息)
├─ 等待 MiGu.Server 完成 IHostedService.StopAsync (≤ 30s)
└─ 超时则 ProcessHost.Kill()
```
**实现要点:**
2026-08-26 17:46:52 +08:00
- **位置**:新建 `Simple3/Bootstrap/` 与 `Simple3/RCS/SystemMissions/` 。
2026-05-29 18:16:34 +08:00
- **不写入主库 Missions 表**: SystemMission 是进程级配置,不参与项目序列化;存储在 `simple_main.db.SystemMissionConfig` (或直接读 `simple.json` )。
2026-08-26 17:46:52 +08:00
- **崩溃隔离**: Platform 异常不影响 Simple3 调度循环;MiGu.Server 也实现 `IHostedService.StopAsync` 做 WAL Checkpoint + WS 排空 → 给 ROSE 释放磁盘。
- **多实例预防**:通过命名互斥体 + `simple.json.allowMultiple=false` (已有)确保 Simple3 唯一;MiGu.Server 启动时校验"父进程是否为 Simple3",否则拒绝启动(防止有人手动双击 `MiGu.Server.exe` )。
2026-05-29 18:16:34 +08:00
- **静态资源**: MiGu.Server 用 `MapWhen + UseStaticFiles + UseSpa` 各挂一份 `index.html` 。
---
## 5. 三端职责矩阵
2026-08-26 17:46:52 +08:00
| 维度 | Simple3 桌面端 | **Platform 管理端(platform-vue) ** | **RCSMonitor 运营端(rcsmonitor-vue) ** |
2026-05-29 18:16:34 +08:00
|------|---------------------|------------------------------------------|------------------------------------------|
2026-08-26 17:46:52 +08:00
| 地图(站点/轨道/区域)编辑 | **读写** ( Desktop-Only) | **全功能读写** ( Vue 地图控件内嵌Simple3地图) | 只读投影,不可编辑 |
2026-05-29 18:16:34 +08:00
| 任务编排(Mission/Recipe) | **读写** | **全功能读写** (含 Demand / Recipe) | 基础动作:暂停/取消/重派/优先级 |
| 车辆配置(参数 / 维护策略) | **读写** | **全功能读写** | 基础动作:暂停/继续/结束任务/上线/离线/手动充电等 |
| CAD 工具 | **可用** | **可用** ( Vue 调用 `/api/sl/cad/*` ) | 不可用 |
| 自定义字段 / 图层 | **可用** | **可用** | 只读 |
| 调度引擎(DispatchLoop) | 进程内拥有 | 远程订阅状态 + 全 API 操作 | 远程订阅状态 + 受限 API |
| 车端通信 | 拥有 | 不直连,只看投影 | 不直连,只看投影 |
2026-08-26 17:46:52 +08:00
| 3D 视口 | 本地 OpenGL( CycleGUI 原生) | **嵌入 Simple3 webVRender** (全功能交互) | **嵌入 Simple3 webVRender** (只读 + 圈选) |
2026-05-29 18:16:34 +08:00
| 系统级配置 | 局部(Desktop-Only) | **全局编辑 + 下发** | 不可编辑 |
| 外部对接(MES/WMS/RCS) | 不涉及 | **唯一入口** | 不涉及 |
| 路径规划/交通/充电策略 | 算法 + 调试 | **可视化编辑 + 下发** | 只读 |
| 权限/角色管理 | 本地用户表(启动登录用) | **统一管理(主源)** | 仅查看自身权限 |
| 库位 / 出入库 | 地图层(库位几何) | **业务主体 + 库存** | 监控视图(按权限) |
| 调度回放 / 日志 | 短期内存 | **长期归档 + 检索** | 故障快照 + 备注 |
| 自定义控件管理 | 本地配置 | **设计 + 发布** | 接收推送 + 按权限渲染 |
> 两个前端共用同一个 MiGu.Server 后端,区别在登录时申请的 `scope`( `Platform` vs `RCSMonitor`)与对应角色绑定的权限码 / WidgetGrant。
### 5.1 RCSMonitor 基础动作白名单(初版)
| 操作 | 权限码 | 影响范围 | 二次确认 |
|------|--------|----------|---------|
| 暂停车辆 | `ops.car.pause` | 单车 | 否 |
| 恢复车辆 | `ops.car.resume` | 单车 | 否 |
| 回原点 | `ops.car.gohome` | 单车 | 是 |
| 重置车辆会话 | `ops.car.resetSession` | 单车 | 是 |
| 手动充电 | `ops.car.manualCharge` | 单车 | 否 |
| 暂停任务 | `ops.task.pause` | 单任务 | 否 |
| 取消任务 | `ops.task.cancel` | 单任务 | 是 |
| 重派任务 | `ops.task.reassign` | 单任务 | 是 |
| 提升优先级 | `ops.task.boostPriority` | 单任务 | 否 |
| 写运营备注 | `monitor.note.write` | 标注层(写 platform.db) | 否 |
> **限权双保险**:① 浏览器侧按 `allowedOps` 隐藏按钮;② MiGu.Server YARP 中间件在转发前再次校验。`scope=RCSMonitor` 的请求路径必须落在 `/api/sl/ops/*` 白名单前缀内,否则 403。
### 5.2 Platform 管理端设计权限示例(全功能套壳)
| 操作 | 权限码 | 控件位置(platform-vue) |
|------|--------|---------------------------|
| 创建/编辑站点 | `map.site.write` | 地图设计页 · 工具栏 |
| 创建/编辑轨道(含曲线) | `map.track.write` | 地图设计页 · 工具栏 |
| 编辑车辆参数 | `vehicle.config.write` | 车辆配置页 |
| 创建/编辑 Mission | `mission.write` | 任务编排页 |
| 加载/保存项目 | `project.write` | 顶栏 |
| 触发 CAD 工具 | `cad.tool.run` | CAD 工具栏 |
| 配置中心编辑 | `config.<section>.write` | 配置中心各分页 |
| 用户/角色管理 | `auth.user.write` / `auth.role.write` | 系统管理页 |
| 切换运行模式(远程) | `system.runmode.reset` | 系统管理页 → 危险操作 |
### 5.3 Platform 平台能力补充(设备统一接入与管理)
Platform 内置通用设备管理模块(`DeviceHub` ),作为所有外围设备的统一接入层与运维入口,覆盖注册、配置、状态监控、告警与审计。
**内置接入设备类型:**
- 电梯、卷帘门、安全门
- 充电桩(自动对接协议)
- 无线 AP、网络交换机
- 视频摄像头、读码器、PLC
**架构原则(一次适配,后续复用):**
- **驱动式插件架构**:设备能力抽象为 `IDeviceDriver` + `IDeviceAdapter` ,每类设备只需完成一次驱动适配。
- **协议标准化**:统一抽象 Modbus-TCP、OPC-UA、HTTP/REST、MQTT、ONVIF、私有 TCP 等协议。
- **配置驱动上线**:新增同类设备无需改代码,只需在平台配置中心新增设备实例配置并绑定已有驱动。
- **可观测性统一**:设备状态统一汇总到 `DeviceRuntimeStatus` (在线/离线、延迟、错误码、最近心跳、告警级别)。
- **故障隔离**:单设备驱动异常不影响其他设备与调度主链路,驱动实例按租户/站点隔离。
### 5.4 Platform 平台能力补充(机器人群组与车队全生命周期管理)
Platform 提供车队级运维能力,支持 AGV 分区域、跨楼层、多车协作调度。
**核心能力:**
- **群组维度管理**:车队按楼层/区域/业务线分组,支持跨组协同任务。
- **硬件健康监控**:主控、驱动器、传感器、电池、定位模块的实时状态与趋势。
- **网络连通诊断**:链路质量、丢包、时延、断连重连次数、AP 切换质量。
- **OTA 升级**:远程固件/软件灰度发布、分批升级、回滚策略与版本基线管理。
- **批量操作**:休眠、唤醒、上线、下线、急停复位、一键任务清空/重派。
- **全生命周期闭环**:入网注册 → 运行监控 → 故障工单 → 维护保养 → 退役归档。
### 5.5 Platform 平台能力补充(业务场景模板化与可扩展机制)
Platform 将典型业务场景抽象为标准模板,支持开箱即用和后续扩展。
**内置模板(首批):**
- SPS 物料配送场景
- 电池 Pack 自动化产线场景
- 环线运行场景
- 点对点柔性搬运场景
**模板内容固化:**
- 调试参数(设备、车辆、节拍、阈值)
- 路径策略(寻路算法、权重、区域限制)
- 交通管制规则(路口互斥、让行策略)
- 任务流(任务编排、优先级、异常分支)
**扩展机制:**
- **配置扩展**:通过模板 DSL / JSON 配置新增场景,无需代码发布。
- **低代码扩展**:通过可视化编排器拼接任务流与策略块。
- **模板版本化**:模板支持版本、灰度、回滚、租户差异化覆盖。
- **模板市场化**:支持插件包导入导出(模板 + 设备驱动 + 参数基线)。
---
## 6. 共享内核分层
```mermaid
flowchart TB
subgraph 顶层["顶层(按端隔离)"]
2026-08-26 17:46:52 +08:00
L1A["Simple3 (CycleGUI + WebAPI Host)"]
2026-05-29 18:16:34 +08:00
L1B["MiGu.Server (ASP.NET Core + YARP)"]
L1B2["platform-vue (SPA, 管理员)"]
L1B3["rcsmonitor-vue (SPA, 运营)"]
end
subgraph 共享["共享业务层 (新增)"]
L2A["SimpleShared.Contracts<br/>(REST DTO + Swagger + WS 事件)"]
L2B["SimpleShared.Auth<br/>(JWT + RBAC + 权限码表)"]
L2C["SimpleShared.Config<br/>(配置模型 + 校验 + 下发协议)"]
L2D["SimpleShared.Persistence<br/>(EF Core + SQLite + 迁移)"]
L2E["SimpleShared.OpsCommand<br/>(运维命令白名单 + 审计 + 幂等)"]
L2F["SimpleShared.WebApiHost<br/>(Kestrel + Auth + Swagger 公共启动器)"]
end
subgraph 引擎["引擎与领域 (现有)"]
L3A["SimpleScheduler<br/>(DispatchLoop / Constraint / Recipe)"]
L3B["SimpleCore<br/>(站点 / 轨道 / Prop / Mission)"]
L3C["HttpClientHelper"]
L3D["Plugins (插件 DLL)"]
end
L1B2 -- "/admin/* + REST + WSS" --> L1B
L1B3 -- "/monitor/* + REST + WSS<br/>(scope=RCSMonitor)" --> L1B
L1A --> L2A
L1B --> L2A
L1A --> L2B
L1B --> L2B
L1A --> L2C
L1B --> L2C
L1A --> L2D
L1B --> L2D
L1A --> L2E
L1B --> L2E
L1A --> L2F
L1B --> L2F
L2C --> L2D
L2B --> L2D
L1A --> L3A
L1A --> L3B
L1A --> L3D
L1B --> L3B
L1B --> L3C
L3A --> L3B
```
### 新工程建议
| 工程 | 类型 | 关键职责 |
|------|------|----------|
| `SimpleShared.Contracts` | netstandard2.1 | REST DTO( POCO + DataAnnotations) + OpenAPI/Swagger + WS 事件结构 + webVRender 桥接消息 |
| `SimpleShared.Auth` | net8.0 | IUserStore / IRoleStore / JwtIssuer / IPermissionEvaluator / 权限码表 / WidgetGrant |
| `SimpleShared.Config` | net8.0 | 强类型配置树 + 版本号 + Diff/校验 + 下发器 |
| `SimpleShared.Persistence` | net8.0 | EF Core 8 多 Provider 抽象(**SQLite / MySQL / PostgreSQL / SQL Server**) + 迁移 + `DbProviderOptions` + 连接串构建器 + `WalSqliteOptions` (仅 SQLite) |
| `SimpleShared.OpsCommand` | net8.0 | 运维白名单命令、审计、幂等键、频次限流 |
| `SimpleShared.WebApiHost` | net8.0 | Kestrel 公共启动器:JWT 中间件 / Swagger / CORS / 健康检查 / 请求审计 |
2026-08-26 17:46:52 +08:00
| `MiGu.Server` | net8.0 ASP.NET Core | 业务 BFF + **YARP 反代 Simple3** + 静态托管两份 Vue dist; **启动时校验父进程为 Simple3,否则拒绝运行**(防误用) |
| `platform-vue` | Vue 3 + Vite + Pinia | 全功能套壳:复刻 Simple3 所有控件 |
2026-05-29 18:16:34 +08:00
| `rcsmonitor-vue` | Vue 3 + Vite + Pinia | 运营监控:按权限渲染,运维动作面板 |
| `packages/sl-controls` ( pnpm workspace) | Vue 3 组件库 | Workspace3D(嵌 webVRender) / 地图列表 / 任务表 / CAD 工具栏等共享组件 |
2026-08-26 17:46:52 +08:00
### 6.1 Platform Vue 控件复刻清单(与 Simple3 CycleGUI 对照)
2026-05-29 18:16:34 +08:00
2026-08-26 17:46:52 +08:00
| Simple3 CycleGUI 面板 | Vue 组件(位于 `sl-controls` ) | 备注 |
2026-05-29 18:16:34 +08:00
|--------------------------|--------------------------------|------|
| 主菜单栏 / 工具栏 | `<AppShell>` 顶栏 + 侧栏 | platform-vue 全功能;rcsmonitor-vue 仅显示运营菜单 |
2026-08-26 17:46:52 +08:00
| Workspace 3D 视口 | `<Workspace3D>` | **iframe / Web Component 嵌入 Simple3 webVRender** ;交互事件用 `postMessage` 桥接 |
2026-05-29 18:16:34 +08:00
| 地图面板(Sites / Tracks) | `<MapEditor>` + `<TrackTable>` | 表格 + 拓扑视图 |
| Car 面板 | `<CarPanel>` | 表格 + 表单 |
| Mission 面板 | `<MissionEditor>` | 任务流图 + 表单 |
| CAD 工具栏 | `<CadToolbar>` | 工具按钮组(按 `cad.tool.run` 显隐) |
| 字段编辑 / 图层 | `<FieldsLayers>` | 表单 + 树 |
| 状态 / 诊断 | `<StatesDashboard>` | ECharts 实时图 |
| 文件对话框 | `<ProjectFileDialog>` | 文件浏览器 + REST 上传/下载 |
| 运维动作面板 | `<OpsActionPanel>` | RCSMonitor 主战场,按 §5.1 白名单生成按钮 |
> 复刻策略:**先 1:1 对齐功能**,UI 风格交给前端工程师二次设计;接口协议统一在 `SimpleShared.Contracts` 内。
### 6.2 webVRender 嵌入方案
CycleGUI 的 `WebTerminal.Use(port, ico)` 已经原地提供:
- WebSocket 路由(驱动 Workspace 同步)
- 嵌入式 webVRender 页面(HTML + JS + WASM)
Vue 端集成方式(推荐 iframe,最简单稳定):
```vue
<!-- packages / sl - controls / src / Workspace3D . vue ( 简化示意 ) -->
< template >
< iframe
ref = "frame"
:src = "vrUrl"
class = "workspace-3d"
@load ="onReady" />
</ template >
< script setup lang = "ts" >
import { computed , ref } from 'vue' ;
const props = defineProps < {
host : string ; // e.g. host:8223
scope : 'Platform' | 'RCSMonitor' ;
token : string ; // 透传 JWT
readOnly ?: boolean ;
} > ();
const emit = defineEmits < {
( e : 'pick' , wp : { x : number ; y : number }) : void ;
( e : 'select' , names : string []) : void ;
} > ();
const frame = ref < HTMLIFrameElement >();
const vrUrl = computed (() =>
`http:// ${ props . host } /?scope= ${ props . scope } &token= ${ encodeURIComponent ( props . token ) } &ro= ${ props . readOnly ? 1 : 0 } ` );
function onReady () {
window . addEventListener ( 'message' , ( ev ) => {
if ( ev . source !== frame . value ? . contentWindow ) return ;
const msg = ev . data ;
if ( msg . type === 'workspace.pick' ) emit ( 'pick' , msg . payload );
else if ( msg . type === 'workspace.select' ) emit ( 'select' , msg . payload );
});
}
</ script >
```
2026-08-26 17:46:52 +08:00
`Simple3/Rendering/SimpleSceneRenderer.cs` 端按 `scope` 和 `ro` 控制:
2026-05-29 18:16:34 +08:00
- `scope=RCSMonitor` 或 `ro=1` → 禁用编辑工具(不响应 `SelectObject` /`GetPosition` 中的拖动)
- 通过 `postMessage` 向 iframe 父页面回传选择/点击事件
如未来需要更深的双向集成(如自定义 Vue 弹层叠在 3D 视口上),可升级到 Web Component( `<sl-workspace3d>` ),但 iframe 已能覆盖 95% 场景。
### 6.3 YARP 反向代理配置(示例)
```csharp
// MiGu.Server / Program.cs
var builder = WebApplication . CreateBuilder ( args );
builder . Services . AddReverseProxy ()
. LoadFromConfig ( builder . Configuration . GetSection ( "ReverseProxy" ))
. AddTransforms ( ctx =>
{
2026-08-26 17:46:52 +08:00
// 透传当前 JWT 的 userId / scope 给 Simple3
2026-05-29 18:16:34 +08:00
ctx . AddRequestTransform ( async tc =>
{
var ident = tc . HttpContext . User ;
tc . ProxyRequest . Headers . Add ( "X-On-Behalf-Of" , ident . FindFirst ( "sub" )?. Value ?? "" );
tc . ProxyRequest . Headers . Add ( "X-Scope" , ident . FindFirst ( "scope" )?. Value ?? "" );
tc . ProxyRequest . Headers . Add ( "X-Svc-Token" , SvcTokenIssuer . Current ());
});
});
builder . Services
. AddAuthentication ( JwtBearerDefaults . AuthenticationScheme )
. AddJwtBearer ( opt => opt . TokenValidationParameters = JwtTokenOptions . Default );
var app = builder . Build ();
app . UseAuthentication ();
app . UseAuthorization ();
// 关键:scope=RCSMonitor 的请求只能命中 /api/sl/ops/* 白名单
app . MapReverseProxy ( pipe => pipe . Use ( async ( ctx , next ) =>
{
var scope = ctx . User . FindFirst ( "scope" )?. Value ;
var path = ctx . Request . Path . Value ?? "" ;
if ( scope == "RCSMonitor" && ! path . StartsWith ( "/api/sl/ops/" ))
{
ctx . Response . StatusCode = StatusCodes . Status403Forbidden ;
await ctx . Response . WriteAsync ( "Scope RCSMonitor cannot access this endpoint" );
return ;
}
await next ();
}));
app . UseStaticFiles ();
app . MapWhen ( c => c . Request . Path . StartsWithSegments ( "/admin" ),
sub => sub . UseStaticFiles (). UseSpa ( s => s . Options . SourcePath = "frontends/platform-vue/dist" ));
app . MapWhen ( c => c . Request . Path . StartsWithSegments ( "/monitor" ),
sub => sub . UseStaticFiles (). UseSpa ( s => s . Options . SourcePath = "frontends/rcsmonitor-vue/dist" ));
app . Run ();
```
`appsettings.json` 中的 YARP 路由:
```json
{
"ReverseProxy" : {
"Routes" : {
"sl-route" : {
"ClusterId" : "sl-cluster" ,
"Match" : { "Path" : "/api/sl/{**catch-all}" },
"Transforms" : [
{ "PathRemovePrefix" : "/api/sl" },
{ "RequestHeader" : "Host" , "Set" : "" }
]
}
},
"Clusters" : {
"sl-cluster" : {
"Destinations" : {
"sl1" : { "Address" : "http://127.0.0.1:7001/" }
}
}
}
}
}
```
---
## 7. 数据库与高可用(多 Provider)
只剩两个逻辑库(`simple_main` / `platform` );持久层基于 **EF Core** , **通过 Provider 切换底层数据库**。**SQLite 是默认与边缘单机首选**,企业级或已有 IT 基础设施现场可平滑切到 **MySQL / PostgreSQL / SQL Server** ;高可用方案随 Provider 不同。
### 7.1 持久层抽象(EF Core Provider 矩阵)
```mermaid
flowchart TB
2026-08-26 17:46:52 +08:00
subgraph App["应用代码 (Simple3 / MiGu.Server)"]
2026-05-29 18:16:34 +08:00
DOM["领域服务 / Repository"]
EFC["EF Core 8 DbContext"]
OPT["DbProviderOptions<br/>{ provider, conn, ha }"]
end
subgraph SharedLayer["SimpleShared.Persistence"]
BLD["ConnectionStringBuilder"]
MIG["Migrations<br/>(SQLite/MySQL/Pg/MSSQL 各一套)"]
DI["AddSimplePersistence(...)<br/>(Provider 注入)"]
end
subgraph Providers["EF Core Provider"]
P1["Microsoft.EntityFrameworkCore.Sqlite"]
P2["Pomelo.EntityFrameworkCore.MySql"]
P3["Npgsql.EntityFrameworkCore.PostgreSQL"]
P4["Microsoft.EntityFrameworkCore.SqlServer"]
end
DOM --> EFC
EFC --> OPT
OPT --> DI
DI --> P1
DI --> P2
DI --> P3
DI --> P4
DI --> MIG
DI --> BLD
P1 -. file .-> S1[(SQLite 文件<br/>+ WAL)]
P2 -. tcp .-> S2[(MySQL 8.x)]
P3 -. tcp .-> S3[(PostgreSQL 14+)]
P4 -. tcp .-> S4[(SQL Server 2019+)]
```
**抽象核心:**
- 所有 `DbContext` (如 `MainDbContext` / `PlatformDbContext` ) **不感知 Provider**,使用通用 LINQ + 注解;少量数据库特性(如 `JSON` 列、`uuid` 主键)由 `IDbProviderSpecifics` 抽象出三套实现。
- **迁移按 Provider 独立维护**: `Migrations/Sqlite` 、`Migrations/MySql` 、`Migrations/Npgsql` 、`Migrations/SqlServer` ; CI 跑全部矩阵保证不偏漂。
- **运行期切换**:仅启动期生效,通过 `simple.json` / `Platform.appsettings.json` 的 `database.provider` 字段决定加载哪一个;不支持热切。
- **测试矩阵**: CI 用 Testcontainers 起对应 DB 跑端到端集成测试。
### 7.2 各库存储内容(与 Provider 无关)
| 逻辑库 | 拥有进程 | 主要表/数据 |
|--------|----------|-------------|
2026-08-26 17:46:52 +08:00
| **simple_main** | Simple3 | Maps, Sites, Tracks, SpecialProps, Missions, Cars, Plugins, DesignAuditLog, OpsAuditLog, LocalUsers, RunModeHistory |
2026-05-29 18:16:34 +08:00
| **platform** | Platform | Users, Roles, RolePermissions, WidgetGrants, SystemConfig, ExternalAdapters, RoutingPolicies, VehicleMaintenancePolicies, ChargePolicies, TaskAllocationPolicies, TrafficRules, Locations, Inventory, OpsLogs(含 RCSMonitor 来源), Annotations, Snapshots, Versions, CustomWidgets |
> 原 v1.2 中的 `rcsmonitor_local` 取消;其表(OpsLogs / Annotations / Snapshots / WidgetLayoutCache)合并到 `platform`,加 `source='RCSMonitor'` 字段区分。
> Provider 不同时,逻辑表名一致;物理实现差异(`uuid` vs `binary(16)`、`json` vs `nvarchar(max)`、自增策略等)由迁移文件适配。
### 7.3 SQLite + ROSE HA(默认 / 边缘单机)
```mermaid
flowchart TB
subgraph A["主节点 NODE-A (Active)"]
2026-08-26 17:46:52 +08:00
SL_A["Simple3.exe"]
2026-05-29 18:16:34 +08:00
PL_A["MiGu.Server.exe"]
D_A[("受 ROSE 保护盘 R:\<br/>simple_main.db<br/>platform.db<br/>(WAL)")]
end
subgraph B["备节点 NODE-B (Standby)"]
2026-08-26 17:46:52 +08:00
SL_B["Simple3.exe (suspended)"]
2026-05-29 18:16:34 +08:00
PL_B["MiGu.Server.exe (suspended)"]
D_B[("ROSE 镜像盘 R:\")]
end
VIP(("浮动 IP / VIP"))
Client["客户端"]
Client --> VIP --> SL_A
A <-- "ROSE 块级镜像 + 心跳" --> B
A -. 故障切换 .-> B
```
**适用场景** :边缘工控机 / 单机现场 / IT 基础设施薄弱 / 初装版本。
**关键约束:**
| 约束 | 落地做法 |
|------|----------|
| **数据文件必须放 ROSE 受保护盘** | 两端 `Configuration` 中数据库路径统一指向 `%SIMPLE_DATA_ROOT%` (默认 `R:\SimpleData\` ) |
| **同一时刻只能一个节点写库** | ROSE 切换前后保证只有 Active 节点起服务;备节点服务进程**默认禁用自启** |
| **关闭跨进程共享缓存** | 连接串 `Cache=Private; Mode=ReadWriteCreate; Pooling=true; Default Timeout=30` |
| **支持优雅停机** | 实现 `IHostedService.StopAsync` ,等 WS 排空 + Checkpoint(WAL) |
| **客户端通过 VIP 访问** | Vue 与车端均连浮动 IP; WS 自动重连 < 5s |
| **Web-Enabled 与 ROSE 强绑定** | 双机热备只在 Web-Enabled 模式下有意义 |
| **切换演练** | `tools/ha/failover-drill.ps1` |
**ROSE 资源建议命名:**
| 资源 | 类型 | 依赖 |
|------|------|------|
| `RES_DISK_R` | Mirror Disk | — |
| `RES_VIP_LAN` | Virtual IP | RES_DISK_R |
2026-08-26 17:46:52 +08:00
| `RES_SVC_SL` | Service: Simple3 (Web-Enabled) | RES_DISK_R, RES_VIP_LAN |
2026-05-29 18:16:34 +08:00
| `RES_SVC_PL` | Service: MiGu.Server | RES_DISK_R, RES_VIP_LAN, RES_SVC_SL |
| `RES_SCRIPT_CHK` | 数据库健康检查脚本 | RES_DISK_R |
**RTO / RPO** : RTO < 60s, RPO ≈ 0(块级镜像)。
### 7.4 MySQL + InnoDB Cluster / MGR
```mermaid
flowchart TB
subgraph App["应用层 (VIP + 重试)"]
2026-08-26 17:46:52 +08:00
SL["Simple3.exe"]
2026-05-29 18:16:34 +08:00
PL["MiGu.Server.exe"]
end
subgraph Router["MySQL Router (或 ProxySQL)"]
R1["VIP :6446 (RW)"]
R2["VIP :6447 (RO)"]
end
subgraph Cluster["InnoDB Cluster (MGR, group_replication)"]
M1[("Primary<br/>node1")]
M2[("Secondary<br/>node2")]
M3[("Secondary<br/>node3")]
M1 <-- "Group Replication" --> M2
M2 <-- "Group Replication" --> M3
M1 <-- "Group Replication" --> M3
end
SL --> R1
PL --> R1
R1 --> M1
R2 --> M2
R2 --> M3
```
**适用场景** :现场已有 MySQL DBA / 中等规模 / Linux 优先 / 多读副本需求。
**关键说明:**
- **EF Core Provider**: `Pomelo.EntityFrameworkCore.MySql` (对 MySQL 8.x / MariaDB 10.x 兼容性最好)。
- **HA 形态**:推荐 **InnoDB Cluster(基于 MySQL Group Replication + MySQL Router) ** 三节点,单点故障自动重选主;老版本 MySQL 可降级用 **MHA / Orchestrator + 半同步复制** 。
- **连接配置**:应用连 MySQL Router 的 RW 端口(`:6446` ),由 Router 自动路由到当前 Primary;只读分析查询可走 RO 端口(`:6447` )。
- **字符集**:统一 `utf8mb4` + `utf8mb4_0900_ai_ci` ; EF Core 配 `CharSet.Utf8Mb4` 。
- **写入隔离级别**: `READ COMMITTED` (避免 RR 下的间隙锁导致死锁);MGR 要求 InnoDB 引擎、主键必填。
- **审计/慢日志**:开 `general_log` + `slow_query_log` ; MiGu.Server 启动期校验。
- **备份**:每天 `mysqldump --single-transaction` 或 XtraBackup 到 NAS;保留 30 天 + Binlog。
- **RTO / RPO**: RTO ≈ 10– 30s( MGR 自动选主);RPO ≈ 0(半同步 / Group Replication 强一致)。
### 7.5 PostgreSQL + Patroni / 流复制
```mermaid
flowchart TB
subgraph App["应用层"]
2026-08-26 17:46:52 +08:00
SL["Simple3.exe"]
2026-05-29 18:16:34 +08:00
PL["MiGu.Server.exe"]
end
subgraph LB["HAProxy / PgBouncer"]
H1["VIP :5432 (RW → leader)"]
H2["VIP :5433 (RO → replicas)"]
end
subgraph Cluster["Patroni 集群"]
P1[("Leader<br/>pg1")]
P2[("Replica<br/>pg2")]
P3[("Replica<br/>pg3")]
DCS[("DCS<br/>etcd / Consul / ZooKeeper")]
P1 -- "流复制" --> P2
P1 -- "流复制" --> P3
P1 <-- "leader lease" --> DCS
P2 <-- "heartbeat" --> DCS
P3 <-- "heartbeat" --> DCS
end
SL --> H1
PL --> H1
H1 --> P1
H2 --> P2
H2 --> P3
```
**适用场景** :复杂查询 / GIS( PostGIS) / JSON 重场景 / 已有 Postgres DBA / 长期数据归档与分析。
**关键说明:**
- **EF Core Provider**: `Npgsql.EntityFrameworkCore.PostgreSQL` ;可直接使用 `uuid` / `jsonb` / `tstzrange` 等原生类型。
- **HA 形态**:推荐 **Patroni + etcd(或 Consul) + 流复制** ; HAProxy / PgBouncer 提供单一 RW VIP。
- **同步策略**: `synchronous_commit = remote_apply` + `synchronous_standby_names = 'ANY 1 (pg2,pg3)'` ,写至少一个备机确认,兼顾性能与 RPO≈0。
- **PgBouncer**:建议事务级(`pool_mode=transaction` ),MiGu.Server 并发连接通过 PgBouncer 复用;注意 EF Core 显式事务跨多个语句时不要踩 transaction 模式陷阱。
- **JSONB 利用**: `SystemConfig` / `CustomWidget.SchemaJson` 等列直接用 `jsonb` + GIN 索引,比 SQLite 的 TEXT/JSON 检索强很多。
- **备份**: `pg_basebackup` + WAL 归档(`archive_command` → NAS / S3 / pgBackRest)。
- **RTO / RPO**: RTO ≈ 10– 30s( Patroni 自动 failover);RPO ≈ 0(同步流复制)。
### 7.6 SQL Server + Always On Availability Groups
```mermaid
flowchart TB
subgraph App["应用层"]
2026-08-26 17:46:52 +08:00
SL["Simple3.exe"]
2026-05-29 18:16:34 +08:00
PL["MiGu.Server.exe"]
end
subgraph Listener["AG Listener (DNS + VIP)"]
L1["VIP :1433 (RW)"]
L2["ReadOnly Routing"]
end
subgraph Cluster["WSFC + Always On AG"]
AG1[("Primary<br/>sql1")]
AG2[("Secondary<br/>sql2 同步提交")]
AG3[("Secondary<br/>sql3 异步提交/可读")]
AG1 -- "同步提交" --> AG2
AG1 -- "异步提交" --> AG3
WSFC[/"Windows Server Failover<br/>Cluster (Quorum)"/]
AG1 --- WSFC
AG2 --- WSFC
AG3 --- WSFC
end
SL --> L1
PL --> L1
L2 --> AG3
```
**适用场景** : Windows 工控/服务器场景 / 已有 SQL Server 许可与 DBA / 与企业 AD/SSO 集成。
**关键说明:**
- **EF Core Provider**: `Microsoft.EntityFrameworkCore.SqlServer` ;支持 SQL Server 2019+(建议 Enterprise 版以启用同步提交副本数 ≥3 + Read-Only Routing)。Standard 版可降级为 2 副本基本可用性组。
- **HA 形态**: **WSFC( Windows Server Failover Cluster) + Always On Availability Groups**;通过 **AG Listener** 暴露统一虚拟名/VIP,客户端连 Listener 名即可。
- **同步策略**:核心库(`platform` / `simple_main` )配 1 个同步提交副本(RPO=0),1–N 个异步副本(可读分析)。
2026-08-26 17:46:52 +08:00
- **认证**:建议混合身份认证;MiGu.Server 用 SQL Login, Simple3 桌面端可走 Windows 集成认证。
2026-05-29 18:16:34 +08:00
- **TempDB**:每节点分摊;MultipleActiveResultSets( MARS)建议关闭,EF Core 已能避免。
- **备份**: `BACKUP DATABASE ... WITH COMPRESSION` + 事务日志备份每 15min;保留策略与公司 IT 一致。
- **RTO / RPO**: RTO ≈ 10– 30s( AG 自动 failover);RPO ≈ 0(同步提交副本)。
### 7.7 选型矩阵
| 维度 | SQLite + ROSE | MySQL InnoDB Cluster | PostgreSQL Patroni | SQL Server Always On |
|------|---------------|----------------------|---------------------|----------------------|
| **典型现场** | 边缘工控机 / 单机 | 中型现场 / Linux 优先 / 已有 MySQL DBA | 复杂分析 / GIS / 已有 PG DBA | Windows 企业环境 / 已有 SQL Server |
| **并发写吞吐** | 低(千级 TPS) | 高(万级) | 高(万级) | 高(万级) |
| **节点数** | 2(主备) | 3+(推荐 3) | 3+ | 2–3(含见证或共享磁盘见证) |
| **复制粒度** | 块级磁盘镜像(ROSE) | Group Replication 行级 | WAL 流复制 | 同步/异步提交 |
| **RTO** | < 60s | 10– 30s | 10– 30s | 10– 30s |
| **RPO** | ≈0(块级实时) | ≈0(强一致) | ≈0(同步) | ≈0(同步提交副本) |
| **运维复杂度** | 低(ROSE 包办) | 中(要会 MGR) | 中–高(要会 Patroni + DCS) | 中(要熟 WSFC) |
| **跨机房 / 多活** | 不支持 | 支持(多组复制 + 路由) | 支持(流复制多机房) | 支持(分布式 AG) |
| **额外依赖** | ROSE HA 软件 | MySQL Router / ProxySQL | etcd/Consul + HAProxy/PgBouncer | Windows Server + WSFC + AD |
| **License 成本** | 仅 ROSE 授权 | 开源(社区版) / 商业支持可选 | 开源 | SQL Server License( Enterprise 较贵) |
| **JSON / 复杂查询** | 弱(TEXT/JSON1) | 中(5.7+ JSON) | **强(jsonb + GIN) ** | 中(NVARCHAR + JSON_VALUE) |
| **GIS** | 弱(SpatiaLite) | 中 | **强(PostGIS) ** | 强(Spatial) |
> **现场决策建议**:默认提供 SQLite 版本(开箱即用 + ROSE 双机)。客户已有数据库基础设施时,按团队最熟悉的栈选 MySQL / PostgreSQL / SQL Server;产品仅维护 4 套迁移与一份连接配置规范。
### 7.8 配置示例(`simple.json` / `appsettings.json`)
#### 7.8.1 SQLite(默认)
```json
{
"database" : {
"provider" : "Sqlite" ,
"simple_main" : {
"dataSource" : "%SIMPLE_DATA_ROOT%/simple_main.db" ,
"extra" : "Cache=Private;Mode=ReadWriteCreate;Pooling=true;Default Timeout=30"
},
"platform" : {
"dataSource" : "%SIMPLE_DATA_ROOT%/platform.db" ,
"extra" : "Cache=Private;Mode=ReadWriteCreate;Pooling=true;Default Timeout=30"
},
"ha" : { "mode" : "ROSE" , "protectedDisk" : "R:\\SimpleData" }
}
}
```
#### 7.8.2 MySQL
```json
{
"database" : {
"provider" : "MySql" ,
"simple_main" : {
"host" : "rcs-mysql.lan" ,
"port" : 6446 ,
"database" : "simple_main" ,
"user" : "simple_app" ,
"password" : "${SECRET_MYSQL_PWD}" ,
"extra" : "SslMode=Required;CharSet=utf8mb4;DefaultCommandTimeout=30"
},
"platform" : {
"host" : "rcs-mysql.lan" ,
"port" : 6446 ,
"database" : "simple_platform" ,
"user" : "simple_app" ,
"password" : "${SECRET_MYSQL_PWD}"
},
"ha" : { "mode" : "InnoDBCluster" , "router" : "rcs-mysql.lan:6446" }
}
}
```
#### 7.8.3 PostgreSQL
```json
{
"database" : {
"provider" : "Npgsql" ,
"simple_main" : {
"host" : "rcs-pg.lan" ,
"port" : 5432 ,
"database" : "simple_main" ,
"user" : "simple_app" ,
"password" : "${SECRET_PG_PWD}" ,
"extra" : "Ssl Mode=Require;Pooling=true;Maximum Pool Size=80;Command Timeout=30"
},
"platform" : {
"host" : "rcs-pg.lan" ,
"port" : 5432 ,
"database" : "simple_platform" ,
"user" : "simple_app" ,
"password" : "${SECRET_PG_PWD}"
},
"ha" : { "mode" : "Patroni" , "vip" : "rcs-pg.lan" , "readReplicaPort" : 5433 }
}
}
```
#### 7.8.4 SQL Server
```json
{
"database" : {
"provider" : "SqlServer" ,
"simple_main" : {
"server" : "tcp:rcs-ag.lan,1433" ,
"database" : "simple_main" ,
"user" : "simple_app" ,
"password" : "${SECRET_MSSQL_PWD}" ,
2026-08-26 17:46:52 +08:00
"extra" : "Encrypt=True;TrustServerCertificate=False;MultiSubnetFailover=True;Application Name=Simple3"
2026-05-29 18:16:34 +08:00
},
"platform" : {
"server" : "tcp:rcs-ag.lan,1433" ,
"database" : "simple_platform" ,
"user" : "simple_app" ,
"password" : "${SECRET_MSSQL_PWD}" ,
"extra" : "Encrypt=True;TrustServerCertificate=False;MultiSubnetFailover=True"
},
"ha" : { "mode" : "AlwaysOnAG" , "listener" : "rcs-ag.lan" }
}
}
```
> **密码注入**: `${SECRET_*}` 由 `IConfiguration` 的环境变量 / Azure Key Vault / DPAPI 加密文件提供;明文绝不入仓库。
### 7.9 跨 Provider 迁移与数据搬迁
- **首次部署切换**:通过 `tools/db/migrate.ps1 --from=Sqlite --to=Npgsql` 一键导出/导入;底层使用 EF Core `IModel` 反序列化每张表后批量写入目标。
- **滚动升级**:不支持热切;停服窗口内执行迁移;保留旧 SQLite 文件作回退包。
- **CI 守护**:每个迁移 PR 必须通过四套 Provider 的迁移 + 端到端测试(Testcontainers)。
- **数据类型差异**: UUID( SQLite TEXT / MySQL CHAR(36) / Pg uuid / MSSQL UNIQUEIDENTIFIER)、JSON( SQLite TEXT JSON1 / MySQL JSON / Pg jsonb / MSSQL NVARCHAR JSON)由 `IDbProviderSpecifics` 抽出三套配置;应用层无感知。
---
## 8. 用户 / 权限模型(含控件级权限)
```mermaid
classDiagram
class User {
+Guid Id
+string Username
+string PasswordHash
+string Email
+bool Enabled
+DateTime LastLogin
}
class Role {
+Guid Id
+string Name
+string Scope
}
class Permission {
+string Code
+string Resource
}
class WidgetGrant {
+string WidgetId
+string Visibility
}
class Tenant {
+Guid Id
+string Name
}
User "1" --> "*" Role : assigned
Role "1" --> "*" Permission : grants
Role "1" --> "*" WidgetGrant : grants
Tenant "1" --> "*" User
User -- LoginSession
LoginSession : +string JwtId
LoginSession : +DateTime IssuedAt
LoginSession : +string ClientApp
LoginSession : +string Scope
```
**落地四件套:**
2026-08-26 17:46:52 +08:00
1. **认证** : `SimpleShared.Auth` 提供 `JwtIssuer` ; Web-Enabled 时 MiGu.Server 签发;Desktop-Only 时 Simple3 本地签发短期 Token( 24h)。
2026-05-29 18:16:34 +08:00
2. **授权** :所有 WebAPI + WS 调用走 `IPermissionEvaluator` ;权限码 `<area>.<action>` 。
3. **范围(Scope) ** : `Lite` / `Platform` / `RCSMonitor` / `*` ;同一用户在不同端可用集合不同。Vue 端登录时由前端代码根据访问路径自动带入 `scope` ( `/admin/*` → `Platform` , `/monitor/*` → `RCSMonitor` )。
4. **控件级权限** :除 `Permission` 外,角色还可关联 `WidgetGrant` ,每个 `WidgetId` 三档可见性:
- `hidden` :根本不渲染
- `readonly` :渲染但禁用交互
- `interactive` :完全可交互(仍受操作权限码二次校验)
### 8.1 RCSMonitor 登录与权限拉取
```mermaid
sequenceDiagram
participant U as 运营用户
participant RV as rcsmonitor-vue
participant P as MiGu.Server
2026-08-26 17:46:52 +08:00
participant L as Simple3 (YARP 后侧)
2026-05-29 18:16:34 +08:00
participant DBp as platform.db
U->>RV: 访问 https://host:8080/monitor
RV->>P: GET /monitor/index.html (静态)
P-->>RV: 200 OK
U->>RV: 输入账号密码
RV->>P: POST /api/auth/login {scope=RCSMonitor}
P->>P: 校验账号 + 计算 EffectivePermissions
P->>DBp: 写 LoginSession
P-->>RV: { token, user, effectivePermissions }
RV->>RV: 按 visibleWidgets / allowedOps 裁剪 UI
RV->>P: GET /api/sl/projection/map (token, scope=RCSMonitor)
P->>P: YARP 中间件: scope 合法? path 在 /ops/* 之外? 投影类不拦截
P->>L: GET /api/projection/map (X-On-Behalf-Of, X-Scope, X-Svc-Token)
L-->>P: 投影
P-->>RV: 投影
RV->>P: WS /ws/events (token)
P->>L: WS /ws/events (svc identity)
L-->>P: 推送
P-->>RV: 转发
```
---
## 9. 平台配置中心
覆盖以下维度:
| 配置维度 | 说明 |
|----------|------|
| 系统级配置 | 运行参数、日志策略、安全策略 |
| 外部系统对接 | MES/WMS/RCS 等标准接口配置 |
| 路径规划策略 | 算法选择、权重、避障规则、区域限速 |
| 车辆维护策略 | 电量阈值、故障上报、自动报修 |
| 充电逻辑 | 充电优先级、空闲充电、任务中断充电 |
| 任务分配机制 | 负载均衡、就近分配、优先级调度 |
| 交通管制规则 | 路口策略、区域互斥、动态让行 |
| 权限与角色管理 | 多用户、功能权限控制、控件授权 |
| 第三方设备统一接入与管理 | 电梯/门禁/充电桩/AP/交换机/摄像头/读码器/PLC 统一注册、配置、监控、告警与审计 |
| 机器人群组与车队全生命周期管理 | 分区域/跨楼层/多车协作;硬件监控、网络诊断、OTA、批量操作、维护闭环 |
| 业务场景模板化与扩展机制 | SPS、Pack 产线、环线、点对点模板;参数/路径/交管/任务流固化;配置/低代码扩展 |
| 库位管理 | 出入库管理、库存管理、库位可视化 |
| 运营维护 | 调度回放、日志管理、版本维护 |
| 自定义控件管理 | 呼叫/展示界面可自由定义(platform-vue + rcsmonitor-vue 双渲染器) |
```mermaid
flowchart LR
subgraph PlatformVue["platform-vue 配置中心"]
UI1[系统级]
UI2[外部对接]
UI3[路径规划]
UI4[车辆维护]
UI5[充电]
UI6[任务分配]
UI7[交通管制]
UI8[权限/角色/控件]
UI9[库位/库存]
UI10[运营/回放]
UI11[自定义控件]
end
subgraph CC["ConfigCenter (MiGu.Server)"]
Sch[Schema 注册表]
Ver[版本/审计]
Val[校验器]
Pub[发布器]
end
UI1 --> Sch
UI2 --> Sch
UI3 --> Sch
UI4 --> Sch
UI5 --> Sch
UI6 --> Sch
UI7 --> Sch
UI8 --> Sch
UI9 --> Sch
UI10 --> Sch
UI11 --> Sch
Sch --> Val --> Ver --> Pub
2026-08-26 17:46:52 +08:00
Pub -- "REST PUT /api/sl/config/{section} (via YARP)" --> SL["Simple3 (主服务)"]
2026-05-29 18:16:34 +08:00
Pub -- "WS broadcast: config.updated" --> RMVue["rcsmonitor-vue"]
Pub -- "WS broadcast: config.updated" --> AdminVue["其他在线 platform-vue"]
```
### 配置强类型骨架(C# 示例)
```csharp
public record SystemConfig ( int DispatchLoopHz , LogPolicy Log , SecurityPolicy Security );
public record ExternalIntegrations ( List < MesEndpoint > Mes , List < WmsEndpoint > Wms , List < RcsEndpoint > Rcs );
public record RoutingPolicy (
string Algorithm ,
Dictionary < string , double > Weights ,
List < AvoidanceRule > Avoidance ,
List < ZoneSpeedLimit > ZoneSpeedLimits );
public record VehicleMaintenancePolicy (
double LowBatteryThreshold ,
double CriticalBatteryThreshold ,
FaultReportPolicy FaultReport ,
AutoRepairPolicy AutoRepair );
public record ChargePolicy ( bool AllowMidTaskCharge , double IdleChargeAfterSec , List < ChargePriorityRule > Priority );
public record TaskAllocationPolicy ( AllocationMode Mode , bool LoadBalance , int MaxQueuePerCar );
public record TrafficRule ( List < IntersectionPolicy > Intersections , List < ZoneMutex > Mutex , List < DynamicYield > Yields );
public record DeviceManagementConfig (
List < DeviceDriverBinding > Drivers ,
List < DeviceInstance > Devices ,
DeviceHealthPolicy HealthPolicy ,
AlarmPolicy AlarmPolicy );
public record FleetLifecycleConfig (
List < FleetGroup > Groups ,
OtaPolicy Ota ,
BatchOpsPolicy BatchOps ,
NetworkDiagPolicy NetworkDiag );
public record ScenarioTemplateConfig (
List < ScenarioTemplate > Templates ,
TemplateDslPolicy DslPolicy ,
LowCodePolicy LowCode ,
TemplateVersionPolicy VersionPolicy );
public record LocationManagement ( List < Location > Locations , List < InventoryRule > InventoryRules );
public record OpsConfig ( PlaybackPolicy Playback , LogRetention LogRetention , VersionPolicy Version );
public record CustomWidget ( string Id , string Name , string SchemaJson , string LayoutJson , List < string > BindToScopes );
public record EffectivePermissions (
string UserId ,
int Version ,
List < string > AllowedOps ,
List < WidgetGrantDto > VisibleWidgets );
```
2026-08-26 17:46:52 +08:00
**发布机制:** Schema → 校验 → 版本 → 灰度 → 全量;MiGu.Server 通过 YARP `PUT /api/sl/config/{section}` 推到 Simple3,主服务原子切换 + ack;失败自动回滚,WS `config.rollback` 广播。
2026-05-29 18:16:34 +08:00
---
## 10. 关键交互序列
### 10.1 启动登录 + Web-Enabled 三端建链
```mermaid
sequenceDiagram
participant U as 用户
2026-08-26 17:46:52 +08:00
participant L as Simple3 (CycleGUI)
2026-05-29 18:16:34 +08:00
participant BL as BootLoginPanel
participant K as Kestrel (WebAPI)
participant P as MiGu.Server (YARP)
participant V as platform-vue
participant RV as rcsmonitor-vue
2026-08-26 17:46:52 +08:00
U->>L: 启动 Simple3.exe
2026-05-29 18:16:34 +08:00
L->>L: 读取 simple.json
alt rememberRunMode = true
L->>L: 直接采用 runMode (仍鉴权)
else
L->>BL: 弹出登录窗
U->>BL: 输入凭据 + 选 Web-Enabled (+记住选择)
BL->>L: 写 simple.json (runMode, rememberRunMode)
end
L->>L: 切换 CycleGUI 到"服务状态窗"
L->>K: 启动 Kestrel :7001/7002 + WebTerminal :8223
L->>L: SchedulerRuntime.Start()
L->>P: spawn MiGu.Server.exe
P->>P: Kestrel :8080/8081 Up
P->>P: YARP 加载路由
P->>K: POST /api/internal/handshake (svcToken)
Note over U,RV: 管理员登录
U->>V: 访问 https://host:8080/admin
V->>P: POST /api/auth/login {scope=Platform}
P-->>V: { token, effectivePermissions }
V->>P: GET /api/sl/projection/map
P->>K: GET /api/projection/map (YARP 转发 + X-On-Behalf-Of)
K-->>P: 投影
P-->>V: 投影
V-->>V: Workspace3D iframe 加载 http://host:8223?scope=Platform&token=...
Note over U,RV: 运营人员登录
U->>RV: 访问 https://host:8080/monitor
RV->>P: POST /api/auth/login {scope=RCSMonitor}
P-->>RV: { token, effectivePermissions (受限) }
RV->>RV: 按 allowedOps/visibleWidgets 渲染
```
### 10.2 Desktop-Only 启动(对比)
```mermaid
sequenceDiagram
participant U as 用户
2026-08-26 17:46:52 +08:00
participant L as Simple3
2026-05-29 18:16:34 +08:00
participant BL as BootLoginPanel
2026-08-26 17:46:52 +08:00
U->>L: 启动 Simple3.exe
2026-05-29 18:16:34 +08:00
L->>L: 读 simple.json (无记忆 或 选项=DesktopOnly)
L->>BL: 必要时弹出登录窗
U->>BL: 输入凭据 + 选 Desktop-Only
BL->>L: 验证通过
L->>L: 启动 CycleGUI 全功能 UI
L->>L: SchedulerRuntime.Start()
Note over L: 不启动 Kestrel<br/>不启动 WebTerminal<br/>不 spawn Platform
L-->>U: 进入设计端主界面
```
2026-08-26 17:46:52 +08:00
### 10.3 platform-vue 编辑地图站点 → Simple3 落库
2026-05-29 18:16:34 +08:00
```mermaid
sequenceDiagram
participant V as platform-vue
participant P as MiGu.Server (YARP)
2026-08-26 17:46:52 +08:00
participant L as Simple3 (WebAPI)
2026-05-29 18:16:34 +08:00
participant DBl as simple_main.db
participant RV as rcsmonitor-vue (订阅)
V->>P: POST /api/sl/map/site {x, y, name}
P->>P: JWT + 鉴权 (map.site.write, scope=Platform)
P->>L: POST /api/map/site (YARP 转发 + X-On-Behalf-Of)
L->>L: 校验 + SimpleLib.SetSite()
L->>DBl: 写入 + DesignAuditLog
L-->>P: 201 Created { siteId }
P-->>V: 201 + siteId
L->>L: WS broadcast { type: "map.site.created", payload }
L-->>P: WS event
2026-08-26 17:46:52 +08:00
P-->>V: WS event (前端 Workspace3D iframe 收到 Simple3 推送的 PutModelObject 同步)
2026-05-29 18:16:34 +08:00
P-->>RV: WS event (RCSMonitor 接收并按权限展示)
```
### 10.4 平台编辑路径规划策略 → 主服务热加载
```mermaid
sequenceDiagram
participant V as platform-vue
participant P as Platform
2026-08-26 17:46:52 +08:00
participant L as Simple3
2026-05-29 18:16:34 +08:00
participant E as DispatchLoop
participant RV as rcsmonitor-vue
V->>P: PUT /api/config/routing (payload, ifMatch=vPrev)
P->>P: 校验 schema + 权限 (config.routing.write)
P->>P: 写 platform.db (version+1, status=staged)
P->>L: PUT /api/sl/config/routing (YARP)
L->>L: ConfigSnapshot.AtomicSwap(vN)
L->>E: OnConfigChanged(routing)
E-->>L: ack
L-->>P: 200 OK { version: vN }
P->>P: status=active
P-->>V: 200 OK + 当前版本
P-->>RV: WS broadcast { type: "config.updated", section: "routing", version: vN }
```
### 10.5 rcsmonitor-vue 发「取消任务」运维命令
```mermaid
sequenceDiagram
participant U as 运营人员
participant RV as rcsmonitor-vue
participant P as MiGu.Server (YARP)
2026-08-26 17:46:52 +08:00
participant L as Simple3 (Ops 网关)
2026-05-29 18:16:34 +08:00
participant E as DispatchLoop
participant DBp as platform.db
participant DBl as simple_main.db
U->>RV: 选中 Demand → 点击「取消」
RV->>RV: 客户端权限校验 (ops.task.cancel ∈ allowedOps?)
RV->>U: 二次确认
U->>RV: 确认
RV->>P: POST /api/sl/ops/task/cancel<br/>{demandId, idempotencyKey, reason}
P->>P: JWT + scope=RCSMonitor + 路径前缀 /api/sl/ops/* 命中白名单
P->>L: POST /api/ops/task/cancel (YARP + X-On-Behalf-Of + svcToken)
L->>L: 服务端鉴权 (ops.task.cancel) + 白名单 + 限流 + 幂等
L->>E: Pool.CancelDemand(id)
E-->>L: result
L->>DBl: 写 OpsAuditLog
L-->>P: 200 OK { status, auditId }
P->>DBp: 写 OpsLogs(source=RCSMonitor, auditId)
P-->>RV: 200 OK
RV-->>U: 提示成功
```
### 10.6 ROSE 主备切换序列
```mermaid
sequenceDiagram
participant CLI as 客户端 (Vue/车端)
participant VIP as 浮动 IP
participant NA as NODE-A (Active)
participant ROSE as ROSE HA Engine
participant NB as NODE-B (Standby)
CLI->>VIP: REST / WS 请求
VIP->>NA: 路由到 Active
Note over NA: 节点故障
ROSE->>NA: 心跳超时 → 触发切换
2026-08-26 17:46:52 +08:00
ROSE->>NA: 强制停止 Simple3 / Platform
2026-05-29 18:16:34 +08:00
ROSE->>NA: 卸载 R: 卷
ROSE->>NB: 挂载 R: 卷
2026-08-26 17:46:52 +08:00
ROSE->>NB: 启动 Simple3 (WAL 重放 + 主库自检)
2026-05-29 18:16:34 +08:00
ROSE->>NB: 启动 MiGu.Server
ROSE->>VIP: 浮动 IP 漂移到 NODE-B
CLI--xVIP: 短暂中断
CLI->>VIP: 重连 (WS auto-reconnect)
VIP->>NB: 路由到 NODE-B (新 Active)
NB-->>CLI: 恢复服务
Note over CLI,NB: RTO 目标 < 60s, RPO ≈ 0
```
---
## 11. 通信协议矩阵
| 通道 | 协议 | 编码 | 端点 | 用途 |
|------|------|------|------|------|
2026-08-26 17:46:52 +08:00
| MiGu.Server (YARP) ↔ Simple3 | REST / WebSocket | JSON | `:7001` / `:7002` | 全功能 API 反代 + 事件订阅 |
2026-05-29 18:16:34 +08:00
| platform-vue ↔ MiGu.Server | REST / WebSocket | JSON | `:8080` / `:8081` (路径前缀 `/admin` ) | UI 操作、事件推送 |
| rcsmonitor-vue ↔ MiGu.Server | REST / WebSocket | JSON | `:8080` / `:8081` (路径前缀 `/monitor` , `scope=RCSMonitor` ) | UI 操作、事件推送(受限) |
2026-08-26 17:46:52 +08:00
| Vue Workspace3D ↔ Simple3 WebTerminal | WSS( webVRender 协议) | webVRender 自有 | `:8223` | 3D 视口同步(嵌入 iframe) |
2026-05-29 18:16:34 +08:00
| 子进程心跳 | Named Pipe | JSON | `\\.\pipe\simple-bootstrap` | 守护 / 重启 |
2026-08-26 17:46:52 +08:00
| Vehicle ↔ Simple3 | TCP / WS / OPC-UA | 业务私有 | `:8222` | 车端协议 |
2026-05-29 18:16:34 +08:00
| ROSE 心跳 | 专用网卡 + 串口 | ROSE 私有 | — | 节点健康监测 |
| ROSE 镜像复制 | 专用网卡 | ROSE 私有 | — | 块级实时数据同步 |
**WebAPI 约定:**
- 资源路径:`/api/{domain}/{resource}` ,例:`/api/ops/task/cancel` 、`/api/projection/map` 、`/api/config/routing` ; Platform 反代前缀 `/api/sl/*`
- 鉴权:`Authorization: Bearer <JWT>` ;服务间互调附 `X-Svc-Token` + `X-On-Behalf-Of: <userId>` + `X-Scope: <scope>`
- 幂等:写操作支持 `Idempotency-Key` 请求头
- 版本化:配置类资源使用 `ETag` / `If-Match` 做乐观锁
2026-08-26 17:46:52 +08:00
- Swagger: Platform / Simple3 都暴露 `/swagger` (生产可关)
2026-05-29 18:16:34 +08:00
- **YARP Scope 白名单**: `scope=RCSMonitor` 时仅允许 `/api/sl/ops/*` 与 `/api/sl/projection/*` 命中;其他路径直接 403
---
## 12. 部署拓扑
```mermaid
flowchart TB
subgraph Cluster["双机 ROSE HA 集群 (Web-Enabled)"]
subgraph NA["NODE-A (Active)"]
2026-08-26 17:46:52 +08:00
SL_A["Simple3.exe<br/>Kestrel :7001/7002<br/>WebTerminal :8223"]
2026-05-29 18:16:34 +08:00
PL_A["MiGu.Server.exe<br/>:8080/8081<br/>(YARP + 双 Vue dist)"]
R_A["ROSE 受保护盘 R:\<br/>两库 (WAL)"]
end
subgraph NB["NODE-B (Standby)"]
2026-08-26 17:46:52 +08:00
SL_B["Simple3.exe (suspended)"]
2026-05-29 18:16:34 +08:00
PL_B["MiGu.Server.exe (suspended)"]
R_B["ROSE 镜像盘 R:\<br/>(实时同步)"]
end
NA <-- "ROSE 块级镜像 + 心跳" --> NB
VIP(("浮动 IP / VIP"))
VIP --> NA
end
subgraph Clients["客户端"]
Op["管理员浏览器<br/>https://vip:8080/admin"]
Run["运营浏览器<br/>https://vip:8080/monitor"]
Veh["AGV 车端"]
end
subgraph Cold["冷备 / 归档"]
NAS["NAS / 对象存储<br/>(夜间快照 + WAL 归档)"]
end
Op -- "HTTPS :8080" --> VIP
Run -- "HTTPS :8080" --> VIP
Veh -- "TCP :8222" --> VIP
NA -. "夜间 sqlite_backup + WAL 归档" .-> NAS
```
**端口规划(单逻辑节点):**
| 端口 | 服务 | 启用条件 |
|------|------|----------|
2026-08-26 17:46:52 +08:00
| 7001 | Simple3 WebAPI | Web-Enabled |
| 7002 | Simple3 WebSocket | Web-Enabled |
2026-05-29 18:16:34 +08:00
| 8080 | Platform WebAPI + platform-vue 静态(/admin) + rcsmonitor-vue 静态(/monitor) | Web-Enabled |
| 8081 | Platform WebSocket | Web-Enabled |
2026-08-26 17:46:52 +08:00
| 8222 | Simple3 车端 / API(现有) | 所有模式 |
| 8223 | Simple3 WebTerminal( webVRender, Vue 嵌入用) | Web-Enabled |
2026-05-29 18:16:34 +08:00
> Desktop-Only 模式下:7001 / 7002 / 8080 / 8081 / 8223 全部**不监听**。
---
## 13. 仓库与解决方案规划
```mermaid
flowchart LR
subgraph repo["Simple-FR 仓库"]
sln["Simple.sln"]
subgraph kernel["内核 (现有)"]
SC["SimpleCore"]
SS["SimpleScheduler"]
HC["HttpClientHelper"]
end
subgraph shared["共享 (新增)"]
SHC["SimpleShared.Contracts"]
SHA["SimpleShared.Auth"]
SHF["SimpleShared.Config"]
SHP["SimpleShared.Persistence"]
SHO["SimpleShared.OpsCommand"]
SHH["SimpleShared.WebApiHost"]
end
subgraph apps["两端后端"]
2026-08-26 17:46:52 +08:00
SL["Simple3 (CycleGUI + WebAPI)"]
2026-05-29 18:16:34 +08:00
PSrv["MiGu.Server (WebAPI + YARP)"]
end
subgraph frontends["前端 (pnpm workspace)"]
ShComp["packages/sl-controls<br/>(共享 Vue 组件库, 含 Workspace3D)"]
PVue["platform-vue (Vue 3 + Vite)"]
RVue["rcsmonitor-vue (Vue 3 + Vite)"]
end
subgraph plugins["插件"]
PG["Plugins / Sample / 第三方"]
end
subgraph ops["运维脚本"]
HA["tools/ha/<br/>(ROSE 资源脚本 + 切换演练)"]
end
end
SL --> SC
SL --> SS
SL --> HC
SL --> SHC
SL --> SHA
SL --> SHF
SL --> SHP
SL --> SHO
SL --> SHH
PSrv --> SC
PSrv --> SHC
PSrv --> SHA
PSrv --> SHF
PSrv --> SHP
PSrv --> SHO
PSrv --> SHH
PG -. 反射加载 .-> SL
ShComp --> PVue
ShComp --> RVue
PVue -- vite build --> PSrv
RVue -- vite build --> PSrv
```
### 13.1 pnpm workspace 配置示例
`frontends/pnpm-workspace.yaml` :
```yaml
packages :
- packages/*
- apps/*
```
`frontends/` 目录结构:
```text
frontends/
├── pnpm-workspace.yaml
├── package.json
├── packages/
│ └── sl-controls/ # 共享组件库(Workspace3D / MapEditor / CarPanel / MissionEditor / CadToolbar / OpsActionPanel / FieldsLayers ...)
│ ├── package.json
│ ├── src/
│ │ ├── Workspace3D.vue # 嵌入 webVRender iframe
│ │ ├── MapEditor.vue
│ │ ├── ...
│ │ └── index.ts
│ └── tsconfig.json
└── apps/
├── platform-vue/ # 管理员前端
│ ├── package.json # depends on "@simple/sl-controls"
│ └── src/
└── rcsmonitor-vue/ # 运营前端
├── package.json # depends on "@simple/sl-controls"
└── src/
```
`packages/sl-controls/package.json` (节选):
```json
{
"name" : "@simple/sl-controls" ,
"version" : "0.1.0" ,
"main" : "src/index.ts" ,
"peerDependencies" : {
"vue" : "^3.4.0"
}
}
```
`apps/platform-vue/package.json` (节选):
```json
{
"dependencies" : {
"@simple/sl-controls" : "workspace:*" ,
"vue" : "^3.4.0" ,
"pinia" : "^2.1.0" ,
"vue-router" : "^4.2.0" ,
"element-plus" : "^2.5.0" ,
"echarts" : "^5.5.0"
}
}
```
构建 + 部署:
```bash
cd frontends
pnpm install
pnpm --filter platform-vue build # 产出 apps/platform-vue/dist
pnpm --filter rcsmonitor-vue build # 产出 apps/rcsmonitor-vue/dist
# MiGu.Server 的发布脚本将这两个 dist 复制到 MiGu.Server/wwwroot/{admin,monitor}
```
---
## 14. 落地路线图
| 里程碑 | 周期估计 | 交付内容 |
|--------|----------|----------|
| **M0 - 内核下沉** | 1 周 | 新建 SimpleShared.* 6 个工程;Configuration → IConfigSnapshotProvider; EF Core + SQLite + 迁移 |
| **M1 - 启动登录 + 运行模式** | 1 周 | `BootLoginPanel` ; `RunMode` 枚举;CycleGUI 状态窗;`simple.json.runMode` 持久化;Web-Enabled 业务面板硬隐藏 |
2026-08-26 17:46:52 +08:00
| **M2 - WebAPI 协议层** | 1.5 周 | Simple3 集成 Kestrel;定义 `/api/projection/*` 、`/api/ops/*` 、`/api/config/*` 、`/api/map/*` 等;WS Hub; JWT |
2026-05-29 18:16:34 +08:00
| **M3 - 共享前端组件库** | 1.5 周 | `frontends/packages/sl-controls` : Workspace3D(嵌入 webVRender) + 地图列表 + 任务表 + CAD 工具栏 + OpsActionPanel 等 |
| **M4 - Platform 后端 + YARP** | 2 周 | MiGu.Server( WebAPI + Swagger + YARP `/api/sl/*` + Scope 中间件 + 静态托管双 Vue dist) + 配置中心 11 个 schema + 用户/角色/控件权限 |
| **M5 - platform-vue (管理员)** | 2.5 周 | 全功能套壳:登录、地图设计、车辆/任务、CAD、字段/图层、配置中心、库位、自定义控件、回放、设备管理、车队生命周期、场景模板 |
| **M6 - rcsmonitor-vue (运营)** | 1.5 周 | 按权限渲染;嵌入 webVRender(read-only);运维白名单动作面板;标注 |
| **M7 - Bootstrapper** | 1 周 | SystemMission 启动 Platform + 心跳 + Watchdog |
| **M8 - 高可用方案落地** | 2 周 | 默认路径 SQLite + ROSE(数据盘 / 启停脚本 / VIP / 切换演练,RTO < 60s 验收);另出 MySQL InnoDB Cluster / PostgreSQL Patroni / SQL Server Always On AG 三份部署指南与 CI 流水线 |
| **M9 - 自定义控件 + 控件级权限** | 2 周 | 控件 schema → platform-vue + rcsmonitor-vue 双渲染器;按 `WidgetGrant` 三档可见性裁剪 |
---
## 15. 已确认决策
| # | 议题 | 决策 |
|---|------|------|
| 1 | 运行模式持久化 | **写入 `simple.json.runMode` / `rememberRunMode`** (不使用 %APPDATA%) |
| 2 | Web-Enabled 下是否允许同时本地操作 | **不允许** ; CycleGUI 仅状态窗;不提供"临时启用本地 UI"开关 |
2026-08-26 17:46:52 +08:00
| 3 | 3D 引擎 | **复用 Simple3 WebTerminal 的 webVRender** , Vue 通过 iframe / Web Component 嵌入 |
2026-05-29 18:16:34 +08:00
| 4 | 反向代理 | **YARP** ( MiGu.Server 用 YARP 反代 `/api/sl/*` ) |
| 5 | RCSMonitor 是否需要独立后端 | **否** ,完全依赖 MiGu.Server;通过 `scope=RCSMonitor` + 角色 + WidgetGrant 限权 |
| 6 | 共享前端组件库 | **pnpm workspace + `frontends/packages/sl-controls`** |
| 7 | 运营白名单 | 采纳 §5.1 列表(10 条,后续可扩) |
| 8 | ROSE 复制粒度 | 块级镜像盘(RPO≈0)首选;视现场版本兜底文件级 |
| 9 | 车端连接策略 | 走 VIP,统一切换;车端实现 WS 自动重连 |
2026-08-26 17:46:52 +08:00
| 10 | **Platform / RCSMonitor 启动归属(v1.4) ** | **由 Simple3 内置 `SystemMission.StartPlatformMission` 拉起并守护** ;不允许独立安装 MiGu.Server.exe 为 Windows Service; MiGu.Server 启动时校验父进程必须是 Simple3 |
2026-05-29 18:16:34 +08:00
| 11 | **持久层多 Provider( v1.5) ** | EF Core 抽象,**默认 SQLite + ROSE HA**;可选 **MySQL InnoDB Cluster / PostgreSQL Patroni / SQL Server Always On AG** ;每 Provider 一套 EF Migrations, CI 全矩阵跑通;运行期由 `simple.json.database.provider` 决定 |
| 12 | **跨 Provider 切换策略** | 不支持热切;切换走停服窗口 + `tools/db/migrate.ps1` ;旧文件作为回退包保留 |
---
## 16. 现有代码影响面
| 现有文件 | 改动方向 |
|----------|----------|
2026-08-26 17:46:52 +08:00
| `Simple3/Program.cs` | 在 `Enssentials.Load` 之后插入 `BootLoginPanel.RunModal()` ;根据返回的 `RunMode` 决定后续路径 |
| `Simple3/Configuration.cs` | 拆为 `LocalBootConfig` (启动期,含 `runMode` 、`rememberRunMode` 、监听地址、各端口)+ `IConfigSnapshotProvider` (运行期);数据库路径走 `%SIMPLE_DATA_ROOT%` |
| `Simple3/Startup.cs` | 按 `RunMode` 分支:Desktop-Only 走原有 CycleGUI; Web-Enabled 启动 Kestrel + WebAPI + WebTerminal + Spawn Platform + 切换 CycleGUI 到状态窗 |
| `Simple3/UI/SimpleUI.cs` | 增加 `RunMode.WebEnabled` 时**所有业务面板硬性不创建**;新增"服务状态窗"面板(唯一允许) |
| `Simple3/UI/BootLoginPanel.cs` (新增) | 启动登录窗实现 + 模式持久化 |
| `Simple3/UI/ServiceStatusPanel.cs` (新增) | Web-Enabled 状态窗实现 |
| `Simple3/RCS/Mission.cs` | 抽出 `SystemMission` 抽象基类(与业务 Mission 同一基类但走独立 Registry, **不进入 DispatchLoop**) |
| `Simple3/RCS/SystemMissions/StartPlatformMission.cs` (新增) | 拉起 `MiGu.Server.exe` 、绑定命名管道、上报心跳与状态到 ServiceStatusPanel |
| `Simple3/Bootstrap/ProcessHost.cs` (新增) | 封装 `System.Diagnostics.Process` + 日志重定向 + 优雅停止 |
| `Simple3/Bootstrap/HeartbeatChannel.cs` (新增) | 命名管道服务端 + JSON 心跳协议 |
| `Simple3/Bootstrap/SystemMissionRegistry.cs` (新增) | 注册/批量 Spawn/Stop SystemMission |
| `Simple3/Bootstrap/ProcessWatchdog.cs` (新增) | 指数退避重启 + 多次失败后熔断并通知状态窗 |
| `Simple3/Bootstrap/RunMode.cs` (新增) | `DesktopOnly` / `WebEnabled` 枚举 + 启动期校验 |
| `Simple3/Rendering/SimpleSceneRenderer.cs` | 按 webVRender 的 `?scope=` / `?ro=` 参数禁用编辑工具 |
2026-05-29 18:16:34 +08:00
| `SimpleScheduler/WebApi.cs` | 调用方从 Nancy 切到 ASP.NET Core Controller;保持 `SchedulerRuntime` 外观稳定 |
2026-08-26 17:46:52 +08:00
| `Simple3/SimpleProject.cs` | 持久化双写:JSON(兼容)+ SQLite(新主存,受 ROSE 保护盘) |
2026-05-29 18:16:34 +08:00
| `Plugins/Sample` | 示例插件读取 `IConfigSnapshotProvider` ;不再读 `Configuration.conf` |
| 新增 `tools/ha/*` | ROSE 资源启停脚本、健康检查脚本、切换演练脚本 |
| 新增 `tools/db/*` | 跨 Provider 迁移脚本(`migrate.ps1` / `dump.ps1` / `restore.ps1` );按目标 DB 校验 schema 与索引 |
| 新增 `SimpleShared.Persistence/Providers/*` | 四个 Provider 适配(`SqliteProviderAdapter` / `MySqlProviderAdapter` / `NpgsqlProviderAdapter` / `SqlServerProviderAdapter` ) + `IDbProviderSpecifics` |
| 新增 `SimpleShared.Persistence/Migrations/{Sqlite,MySql,Npgsql,SqlServer}/*` | 四套 EF Core 迁移文件,CI 矩阵跑全 |
| 新增 `frontends/pnpm-workspace.yaml` | pnpm workspace 根 |
| 新增 `frontends/packages/sl-controls/` | 共享 Vue 组件库(含 Workspace3D 嵌 webVRender) |
| 新增 `frontends/apps/platform-vue/` | Vue 3 + Vite 工程,管理员全功能套壳 |
| 新增 `frontends/apps/rcsmonitor-vue/` | Vue 3 + Vite 工程,运营按权限渲染 |
| 新增 `MiGu.Server` 工程 | ASP.NET Core 8 WebAPI + YARP + 静态托管双 Vue dist |
---
## 17. 视觉规范(v1.6 新增,v1.6.1 对齐 FRLD)
### 17.1 品牌
| 项 | 取值 | 出现位置 |
|----|------|----------|
| 产品名 | **迷毂** | 登录窗标题 / `<title>` / 浏览器 favicon / 顶栏侧栏 logo / 文档抬头 |
| 全称 | **迷毂 · 智能调度平台** | 登录窗 H1、侧边栏中文标题 |
| 英文 | **Mi Gu · Intelligent Dispatch Platform** | 登录窗副标题、侧边栏英文副标 |
| 母公司 | **法睿兰达 FAIRYLAND** (FRLD) | 登录卡 / 侧边栏 顶部 logo |
| 工程标识(保持不动) | `MiGu.Server` / `simple-platform-vue` | csproj 名 / pnpm 包名 / 仓库目录;保持稳定避免破坏构建脚本 |
| Logo 资源 | `public/FRLD-logo-white.png` (11.5 KB) 展开态 + `public/FRLD-logo-white-no_title.png` (3.5 KB) 折叠态 | 侧边栏 / 登录卡;来源 `E:\ddms\frontend\public` (与 FAME 共用) |
| Favicon | `public/favicon.svg` (紫色渐变方块 + 白色「迷」字) | 浏览器标签页 |
| 登录背景 | `public/login-bg.jpg` ( 1.8 MB,原始素材 `C:\Tool\wallpapper\wall_Beach.jpg` ) | 海滩 + 紫色叠加层 |
### 17.2 主色卡(v1.7 默认 · 工业紫色)
**默认主题 `industrial-purple`(工业紫)** — 登录后内容区与 AppShell 的基准色:
```
主色 PRIMARY #5a1890 rgb(90, 24, 144)
悬停 HOVER #7b1fa2 rgb(123, 31, 162)
按下 ACTIVE #38006b
强调 ACCENT #8e6abf rgb(142, 106, 191)
应用渐变起/中/止 #0c021c → #16082e → #04000e(内容卡片 92%+ 不透明,避免发灰)
侧栏渐变 rgba(--mg-bg-aside-rgb) → rgba(--mg-bg-app-deep-rgb)
```
> **历史**: v1.6.2 固定 `#641393`; v1.6.1 曾对齐 ddms 的 `#7c3aed`。v1.7 起以 `themes.ts` 为唯一色板源,`theme.css` 仅提供默认值与玻璃令牌。
>
> 实现位置:
> - `frontends/apps/simple-platform-vue/src/styles/themes.ts` — 6 套预设 + `applyThemeVars()`
> - `frontends/apps/simple-platform-vue/src/styles/theme.css` — `--mg-*` + Element Plus 覆盖
> - `frontends/apps/simple-platform-vue/src/stores/ui.ts` — `themeId` 持久化
### 17.3 状态色
为保持紫色品牌统一,状态色降饱和并往紫色侧偏移:
| 状态 | 取值 | RGB |
|------|------|-----|
| 成功 | `#6f8f4c` | 111, 143, 76 |
| 警告 | `#c98a1d` | 201, 138, 29 |
| 危险 | `#b3324f` | 179, 50, 79 |
| 信息 | `#7a6b87` | 122, 107, 135 |
### 17.4 登录窗视觉构成(§3.2 的物化形态,v1.6.1 对齐 ddms)
1. **背景层 (`z-index:0`)** : `url('/login-bg.jpg') center/cover` , filter `saturate(0.45) brightness(0.42)` , transform `scale(1.05)` 。
2. **紫色叠加层 (`z-index:1`)** :径向渐变(左上 `rgba(168,85,247,0.55)` + 右下 `rgba(26,16,64,0.78)` ) + 主对角线深紫线性渐变。
3. **辉光球 (`z-index:1`)** :两个 blur(85px) 圆斑(左上 440px violet-300、右下 560px purple-500),各自 drift 18s 错相位呼吸。
4. **登录卡 (`z-index:2`)** : 460px 宽,圆角 16px,背景 `linear-gradient(135deg, #2d1b69 0%, #7c3aed 100%)` , `box-shadow: 0 30px 80px rgba(10,10,40,0.55)` + 内边高光。
5. **品牌区** : 52px FRLD logo( drop-shadow violet glow) + H1 `迷 毂 · 智能调度平台` ( letter-spacing 3px) + 副标 `Mi Gu · Intelligent Dispatch Platform` (首字母 bolded) + hint `请登录您的账号 · Web-Enabled` 。
6. **表单** :透明 el-input(白字 + 半透明白边 + focus 紫色辉光)+ scope 双卡(选中态白底紫字)+ checkbox(白勾紫底)+ 高级折叠(端口配置 2x2 grid)+ 白底紫字 46px 大圆角主按钮(hover 上浮 2px)。
### 17.5 AppShell 视觉构成(v1.6.1 对齐 ddms)
1. **侧边栏** :自顶向下渐变 `#2d1b69 → #1a1040` ,宽度 232px(折叠 64px),右侧细边 + `box-shadow: 2px 0 16px rgba(26,16,64,0.25)` 。
2. **侧边栏顶端 Logo 块** : 22/14 内边距,38px FRLD logo( drop-shadow `rgba(168,85,247,0.4)` ),下方中文标题 `迷 毂 · 智能调度平台` ( letter-spacing 2.5px) + 英文副标 `Mi Gu · Intelligent Dispatch Platform` (首字母 bolded)。
3. **折叠态** :仅显示 28px FRLD 小 logo + 「迷毂」缩写。
4. **菜单态** : item margin 2/8 + 44px 高 + 圆角 8px; hover `rgba(168,85,247,0.18)` ; active `linear-gradient(90deg, rgba(168,85,247,0.38), rgba(124,58,237,0.22))` + `inset 3px 0 0 #a855f7` 。
5. **顶栏** :白底 + `box-shadow: 0 1px 6px rgba(45,27,105,0.06)` ;面包屑 `#2d1b69` 、分隔符 `#c4b5fd` 。
6. **用户头像** : `linear-gradient(135deg, #a855f7, #7c3aed)` 圆形。
7. **页脚** : `color: var(--mg-primary-700)` 、`border-top: 1px solid #f0ebf8` 。
### 17.6 复用清单
要在新页面/组件中沿用品牌主色,优先使用 CSS 变量:
| 变量 | 用途 |
|------|------|
| `--mg-primary` | 任意主色字段(按钮、链接、强调) |
| `--mg-primary-{50..900}` | 主色阶梯,用于底色/边框/浅文字 |
| `--mg-bg-app` / `--mg-bg-aside` / `--mg-bg-aside-2` | 全局/侧边栏底色 |
| `--mg-text-light` / `--mg-text-muted` | 暗背景文本 |
| `--mg-divider` | 暗背景分割线 |
| Element Plus 自带 `var(--el-color-primary*)` | 已自动跟随主色 |
新写视图时请避免硬编码 `#641393` / `#7c3aed` (统一走 `var(--mg-primary)` 、`rgba(var(--mg-*-rgb), α )` ,以便主题切换生效)。
### 17.7 多主题色板切换(v1.7 新增)
| 文件 | 职责 |
|------|------|
| `src/styles/themes.ts` | `ThemePreset[]` : id / name / description / preview / `vars` ( `--mg-*` 键值对) |
| `src/styles/theme.css` | 静态默认 + 玻璃令牌 + `.mg-content` 内 Element Plus 暗色适配 |
| `src/stores/ui.ts` | `themeId` 、`setThemeId()` 、`applyCurrentTheme()` 、`localStorage` 键 `simple.ui.state` |
| `src/components/ThemeSwitcher.vue` | 顶栏下拉:色块预览 + 名称/描述 + 当前勾选 |
| `src/layouts/AppShell.vue` | 头部接入 `ThemeSwitcher` ;壳层样式使用 `var(--mg-*)` |
| `src/main.ts` | `createPinia()` 后立即 `useUiStore(pinia).applyCurrentTheme()` |
**6 套预设 ID: **
| id | 名称 | 主色 preview |
|----|------|----------------|
| `industrial-purple` | 工业紫色(**默认**) | `#5a1890` |
| `deep-azure` | 深邃蓝 | `#1565c0` |
| `emerald-forge` | 翡翠绿 | `#00796b` |
| `crimson-iron` | 熔铁赤 | `#b71c1c` |
| `graphite-steel` | 石墨钢 | `#455a64` |
| `amber-forge` | 琥珀金 | `#e65100` |
**切换流程:** 用户选主题 → `setThemeId` → `applyThemeVars` 写 `:root` + `data-theme` 属性 → `persist()` 。刷新页面后 `main.ts` 恢复。
**构建验证:** `pnpm --filter simple-platform-vue exec vue-tsc --noEmit` + `pnpm --filter simple-platform-vue build` ( 2026-05-20 已通过)。
---
## 18. 实现进度与待办(对照代码库)
> 工作目录:`E:\Work\Core\Simple-FR\Simple`。下列状态以仓库内**实际文件**为准,与 §14 里程碑对照;完成项在后续 PR/会话中应同步更新本表。
### 18.1 前端 `simple-platform-vue`(合并 admin + monitor)
| # | 项 | 文档依据 | 状态 | 代码位置 / 备注 |
|---|-----|----------|------|-----------------|
| F1 | pnpm workspace + 单 SPA | §13、frontends/README | ✅ 完成 | `frontends/apps/simple-platform-vue` ;路由 `/admin/*` `/monitor/*` |
| F2 | 登录窗 §3.2 | §3.2、§17.4 | ✅ 完成 | `LoginView.vue` :双栏 hero、海滩背景、Mock 鉴权 |
| F3 | AppShell 玻璃风 | §17.5、v1.6.2 | ✅ 完成 | `AppShell.vue` :侧栏 FRLD logo、菜单、fade-up |
| F4 | Workspace3D / webVRender | §1、§11 | ✅ 完成 | `Workspace3D.vue` → `:8223` iframe |
| F5 | 配置中心 14 页骨架 | §9 | ✅ 完成 | `views/admin/config/*` + `ConfigPageBase` |
| F6 | 运营 monitor 视图 | §5、M6 | ✅ 骨架 | `views/monitor/*` (与 admin 同工程) |
| F7 | **多主题色板** | §17.7( v1.7) | ✅ 完成 | `themes.ts` 、`ThemeSwitcher.vue` 、`ui.ts` 、`main.ts` |
| F8 | AppShell 壳层随主题变色 | §17.7 | ✅ 完成 | `AppShell.vue` 已改 `var(--mg-*)` |
| F9 | 登录页随主题变色 | §17.7 | ✅ 完成 | `BlankLayout` / `LoginView` 光球、叠加层、hero、按钮已改 `var(--mg-*)` ;登录页内切换器仍 ☐(登录后顶栏可切) |
| F10 | `DashboardView` 图表色板 | §17.6 | ☐ 待办 | `MG_PALETTE` 仍为固定紫色数组,应读 `activeTheme` |
| F11 | 独立 `platform-vue` / `rcsmonitor-vue` | §13 | ☐ 未拆 | 当前有意合并;拆分时更新构建脚本 |
| F12 | `packages/sl-controls` | §13、M3 | ☐ 未建 | 组件仍在 app 内 `components/` |
| F13 | dist → `MiGu.Server/wwwroot` | §13 构建说明 | 🔄 需手动 | build 后复制 `dist/*` ; `Program.cs` 已 `MapFallbackToFile` |
### 18.2 后端 `MiGu.Server`(骨架)
| # | 项 | 状态 | 备注 |
|---|-----|------|------|
| B1 | Kestrel + Swagger + CORS | ✅ | `Program.cs` |
| B2 | Auth / Health / Config / Ops / Projection 控制器 | ✅ 骨架 | Mock/内存存储 |
2026-08-26 17:46:52 +08:00
| B3 | YARP `/api/sl/*` → :8222 | ✅ 配置 | 依赖 Simple3 实际监听 |
2026-05-29 18:16:34 +08:00
| B4 | 静态托管 + SPA fallback | ✅ | 单一 `wwwroot/index.html` |
| B5 | JWT 真签发 + 库持久化 | ☐ | 仍为 Mock |
2026-08-26 17:46:52 +08:00
| B6 | 父进程 Simple3 校验 | ☐ | §4、§15 #10 |
2026-05-29 18:16:34 +08:00
2026-08-26 17:46:52 +08:00
### 18.3 Simple3 / 共享内核(ARCHITECTURE 主体)
2026-05-29 18:16:34 +08:00
| # | 项 | 状态 | 备注 |
|---|-----|------|------|
| S1 | `SimpleShared.*` 六工程 | ☐ 未建 | §6、M0 |
| S2 | `BootLoginPanel` + `RunMode` | ☐ | M1 |
| S3 | `SystemMission.StartPlatformMission` | ☐ | M7 |
| S4 | WebAPI Kestrel + WS | ☐ | M2 |
| S5 | 多 DB Provider + HA | ☐ | M0/M8 |
### 18.4 主题功能子任务(v1.7 专项)
| # | 子任务 | 状态 |
|---|--------|------|
| T1 | `themes.ts` 六套预设,工业紫默认 | ✅ |
| T2 | `theme.css` 全站 `--mg-*` + `.mg-content` EP 覆盖 | ✅ |
| T3 | `ui.ts` : `themeId` + `applyCurrentTheme` + localStorage | ✅ |
| T4 | `ThemeSwitcher.vue` 下拉组件 | ✅ |
| T5 | `AppShell` 顶栏接入 | ✅ |
| T6 | `main.ts` 启动恢复主题 | ✅ |
| T7 | `vue-tsc` + `vite build` | ✅ 2026-05-20 |
| T8 | 登录页变量化 + 可选登录页 ThemeSwitcher | 🔄 T8a 变量化 ✅;T8b 登录页 ThemeSwitcher ☐ |
| T9 | `ARCHITECTURE.md` / `frontends/README.md` 同步 | ✅ 本节 |
---
## 关联文档
2026-08-26 17:46:52 +08:00
- [SIMPLELITE_DEVELOPMENT_PLAN.md ](./SIMPLELITE_DEVELOPMENT_PLAN.md ) — Simple3 功能对齐执行计划
2026-05-29 18:16:34 +08:00
- [SIMPLELITE_COMPOSER_PARITY_PLAN.md ](./SIMPLELITE_COMPOSER_PARITY_PLAN.md ) — 与 SimpleComposer 差距分析