Files
Migu2.0/ARCHITECTURE.md
T
黄兆尉andCursor 3686abdc78 将调度内核标识从 SimpleLite 全面重命名为 Simple3。
配置段/环境变量、Launcher、健康检查 API、OpenAPI 与前后端文案同步;兼容探测旧 SimpleLite 进程名。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-26 17:46:52 +08:00

1947 lines
91 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Simple3 三端系统架构设计
> **Migu2.0 仓库说明**:平台后端已拆至本仓库,工程名为 **`MiGu.Server`**(原 `Platform.Server`)。Simple3 与前端源码仍在兄弟仓库 `Simple`。下文中的 `MiGu.Server` 即指本仓库 `MiGu.Server/` 目录。
> 文档版本:v1.7.0
> 更新日期:2026-05-20
> 范围:Simple3(主程序 / 设计端 / 调度引擎宿主 / 进程编排器)+ Platform(业务平台 · 同时承载管理员前端 platform-vue 与运营前端 rcsmonitor-vue · 共用一套 WebAPI · **主入口为 MiGu.Server,登录后拉起 Simple3**
> 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-up250ms cubic-bezier .25,.8,.25,1),轻盈不花哨。
> - 详见 §17「视觉规范」。
> v1.6.1 变更(视觉对齐 FRLD · 已在 v1.6.2 中部分回退):
> - **侧边栏左上角加 FRLD(法睿兰达 FAIRYLANDlogo**`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 初版 #641393v1.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 HASQLite 块级镜像)外,新增 **MySQL InnoDB Cluster / MGR**、**PostgreSQL Patroni / 流复制**、**SQL Server Always On AG** 三种企业级 HA 路径;现场可根据已有 IT 基础设施挑选。
> - 新增 §7.4–§7.7 四类 DB 部署形态详述、选型矩阵、连接串与配置示例、迁移注意事项。
> v1.4 变更(保留):
> - **明确进程编排归属**Platform 后端进程 `MiGu.Server.exe` 不再被视为"独立的业务平台服务",而是 Simple3 内置 `SystemMission``StartPlatformMission`)拉起、被 Simple3 Bootstrapper 守护的**受控子进程**RCSMonitor 不存在独立后端进程,复用 MiGu.Server。
> - **整体定位章节重写**:明确"两类后端可执行文件 + 一个进程编排者"的关系,避免读者误以为 Platform 是用 Windows Service / systemd 独立部署的服务。
> v1.3 变更(保留):
> 1. **取消 RCSMonitor.Server**`rcsmonitor-vue` 不再有独立后端进程,直接连 MiGu.Server;由 JWT 中的 `scope=RCSMonitor` + 角色权限码 + `WidgetGrant` 三层限制可见控件与可调用 API。
> 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 的反代;带鉴权与审计中间件。
> 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 现场打造的「**设计 → 调度 → 运营监控**」三端协同系统。
**三个"端"对应三套用户体验,但只有两类后端可执行文件,且都由 Simple3 统一编排:**
| 端 | 前端载体 | 后端可执行文件 | **启动者** | 角色 | 写权限 |
|----|----------|----------------|------------|------|--------|
| **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;可对任务/车辆执行白名单运维动作 | **受限**(仅运维白名单) |
**关键澄清(v1.4 重点):**
- **后端可执行文件只有 2 个**`Simple3.exe``MiGu.Server.exe`
- **进程启动只有 1 个真正的入口**:用户/服务管理器只启动 `Simple3.exe`MiGu.Server 由 Simple3 自己拉起,**不需要也不允许独立安装为 Windows Service**。
- **进程编排归属 Simple3**`SystemMission` 是 Simple3 自带的 Mission 体系扩展(与业务 Mission 区分开),专门负责拉起/守护/优雅停止 MiGu.Server。Bootstrapper 监听心跳、按指数退避重启、统一记录 OpsAuditLog。
- **Vue 两份产物,同一进程托管**MiGu.Server 在 `/admin/*` 路径下托管 `platform-vue`,在 `/monitor/*` 路径下托管 `rcsmonitor-vue`
- **鉴权与限权完全在 MiGu.Server 完成**RCSMonitor 不持有任何独立后端代码(没有 `RCSMonitor.Server.exe`、没有独立库)。
- **Desktop-Only 模式下,SystemMission 不触发**MiGu.Server 根本不会启动,Vue 端无法访问。
> 一句话:**用户点 Simple3.exe,由 Simple3 通过 Mission 把 Platform 后端拉起来;Platform 后端再把两套 Vue 端服好。**
### 1.2 核心拆分原则
1. **Crash-Isolation(搞不崩主服务)**
Platform / RCSMonitor 对**地图/任务/车辆等写操作**都通过 Simple3 的 `/api/*` 网关;主服务侧做白名单 + 幂等校验 + 频次限流 + 审计。Platform-Vue 走完整 API 集,RCSMonitor-Vue 仅限运维白名单 `/api/ops/*`
2. **共享数据模型,独立运行时**
地图、车辆、任务三端共用 `SimpleCore` 的 DTO;调度引擎只在 Simple3 里跑,Platform 只持有"投影"。
3. **库分离 + Provider 抽象**(详见 §7
- `simple_main` 由 Simple3 持有;`platform` 由 Platform 持有(含 RCSMonitor 审计/标注);不再有独立的 `rcsmonitor_local`
- 持久层基于 **EF Core****通过 Provider 切换底层数据库**:默认 **SQLite**(边缘单机,配 ROSE HA);可选 **MySQL / PostgreSQL / SQL Server**,匹配现场已有 IT 基础设施与企业级 HA 能力(详见 §7.4–§7.7)。
- 高可用方案随 Provider 不同:SQLite 用 ROSE HAMySQL 用 InnoDB ClusterPostgreSQL 用 PatroniSQL Server 用 Always On AG。
4. **统一协议层**
所有进程间调用统一 **WebAPIREST/JSON+ WebSocket 事件流**Vue 端直接对接 Platform 后端的 REST + WSPlatform 后端用 **YARP** 反代 Simple3 的设计/调度 API。
5. **统一身份**
`SimpleShared.Auth` 提供 JWT + RBACPlatform 是主权限源(在线时),Simple3 在 Desktop-Only 下用本地用户表降级。
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["主控宿主机 / 边缘服务器"]
subgraph SL["Simple3 主进程 (CycleGUI + ASP.NET Core 8)"]
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/>(配置中心 / 外部对接 / 库位 / 账号)"]
PL_YARP["YARP 反代<br/>/api/sl/* → Simple3 WebAPI<br/>+ Auth/审计中间件"]
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
```
**要点:**
- **只有两个后端可执行文件**`Simple3.exe``MiGu.Server.exe`RCSMonitor 不再有独立后端。
- **`MiGu.Server.exe` 由 Simple3 的 SystemMission 拉起**:用户只启动 Simple3Web-Enabled 时 `StartPlatformMission` 自动 spawn MiGu.Server,子进程心跳异常由 Bootstrapper 重启。
- **`PL_Static` 同一进程双 SPA 托管**:浏览器访问路径决定加载哪份 dist,登录时也按路径决定 `scope` 参数。
- **`PL_YARP``/api/sl/*` 反代到 Simple3**:YARP 在请求转发前后挂中间件做 JWT 校验、权限码二次确认、写操作审计、`Idempotency-Key` 透传、Scope 检查(RCSMonitor 的请求仅允许命中 `/api/ops/*` 这一前缀)。
- **3D 由 Simple3 的 webVRender 提供**Vue 端通过 iframe 或 Web Component 嵌入 webVRender 的页面,业务交互(选择、点击坐标等)通过 `postMessage` 与 Vue 桥接。
- **Desktop-Only 模式**`SL_API``PL` 都不启动;Vue 无法访问;CycleGUI 是唯一入口。
- **Web-Enabled 模式**CycleGUI 退化为状态窗;所有业务操作通过 Vue 完成;硬性不允许同时本地操作。
---
## 3. 启动登录与运行模式
### 3.1 启动流程
```mermaid
flowchart TB
Start([Simple3.exe 启动]) --> CheckPrev{simple.json 中<br/>runMode 已记忆?}
CheckPrev -- 否 --> Login[弹出登录窗<br/>Simple3 Boot Login]
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
字段与控件:
| 元素 | 控件 | 说明 |
|------|------|------|
| 标题 | Label | "Simple3 · 启动登录" |
| 用户名 | 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 | 不进入主程序,退出进程 |
> 实现位置:新增 `Simple3/UI/BootLoginPanel.cs`,由 `Program.Main` 在 `Enssentials.Load` 之后、`Startup.EntryPoint` 之前调用 `GUI.PromptAndWaitPanel(BootLoginPanel.Build())`(阻塞式模态)。
#### 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 |
| Desktop-Only → Web-Enabled | **必须退出 Simple3 重新登录**;不支持热切(避免半启动状态) |
| Web-Enabled 运行中 | CycleGUI 主窗只显示状态窗(运行时长 / 在线用户 / 端口 / 主备状态)+「停止」按钮 |
| Web-Enabled 下用户尝试本地控件 | **所有业务面板硬性不渲染**;Workspace 视口也不显示;用户无法通过任何方式重新启用 |
| Web-Enabled 异常退出 | Watchdog 重新拉起,恢复到 Web-Enabled;不强迫用户重选 |
| 想取消"记住模式" | 启动时按住 `Shift` 或运行 `Simple3.exe --re-login` 强制弹出登录窗 |
| 远程开关模式 | Platform 管理员可以发 `POST /api/sl/runmode/reset``rememberRunMode` 置为 `false`**仅在下次重启时生效** |
### 3.4 服务状态窗(Web-Enabled 唯一可见的 CycleGUI 面板)
```text
┌──────────────────────────────────────────┐
│ Simple3 Service Status │
├──────────────────────────────────────────┤
│ 模式: 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
A["Simple3.exe<br/>启动 + 登录"] --> Mode{运行模式}
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 状态窗]
subgraph SysMis["Simple3 SystemMission 体系"]
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 新增章节)
Simple3 现有 `Mission` 是业务任务(运送/搬运),新增 **`SystemMission`** 作为同一基类的"系统级 Mission",用于拉起并守护进程级服务。它**不参与调度引擎的 DispatchLoop**,由 Bootstrapper 直接驱动。
**关键类:**
| 类型 | 类型 / 命名空间 | 职责 |
|------|------------------|------|
| `SystemMission`(抽象) | `Simple3.RCS.SystemMissions` | 继承自现有 `Mission`,新增 `Spawn() / Stop() / Heartbeat()` 等抽象方法 |
| `StartPlatformMission` | `Simple3.RCS.SystemMissions` | 拉起 `MiGu.Server.exe`;通过命名管道 `\\.\pipe\simple-bootstrap` 接收心跳 |
| `SystemMissionRegistry` | `Simple3.Bootstrap` | 单例,注册所有 SystemMissionWeb-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` |
**生命周期:**
```text
Simple3 启动 (Web-Enabled)
├─ 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",停止重试,需人工介入
Simple3 优雅停机:
SystemMissionRegistry.StopAll()
└─ StartPlatformMission.Stop()
├─ 发送 SIGTERM 等价(命名管道 "shutdown" 消息)
├─ 等待 MiGu.Server 完成 IHostedService.StopAsync (≤ 30s)
└─ 超时则 ProcessHost.Kill()
```
**实现要点:**
- **位置**:新建 `Simple3/Bootstrap/``Simple3/RCS/SystemMissions/`
- **不写入主库 Missions 表**SystemMission 是进程级配置,不参与项目序列化;存储在 `simple_main.db.SystemMissionConfig`(或直接读 `simple.json`)。
- **崩溃隔离**Platform 异常不影响 Simple3 调度循环;MiGu.Server 也实现 `IHostedService.StopAsync` 做 WAL Checkpoint + WS 排空 → 给 ROSE 释放磁盘。
- **多实例预防**:通过命名互斥体 + `simple.json.allowMultiple=false`(已有)确保 Simple3 唯一;MiGu.Server 启动时校验"父进程是否为 Simple3",否则拒绝启动(防止有人手动双击 `MiGu.Server.exe`)。
- **静态资源**MiGu.Server 用 `MapWhen + UseStaticFiles + UseSpa` 各挂一份 `index.html`
---
## 5. 三端职责矩阵
| 维度 | Simple3 桌面端 | **Platform 管理端(platform-vue** | **RCSMonitor 运营端(rcsmonitor-vue** |
|------|---------------------|------------------------------------------|------------------------------------------|
| 地图(站点/轨道/区域)编辑 | **读写**Desktop-Only | **全功能读写**Vue 地图控件内嵌Simple3地图) | 只读投影,不可编辑 |
| 任务编排(Mission/Recipe | **读写** | **全功能读写**(含 Demand / Recipe | 基础动作:暂停/取消/重派/优先级 |
| 车辆配置(参数 / 维护策略) | **读写** | **全功能读写** | 基础动作:暂停/继续/结束任务/上线/离线/手动充电等 |
| CAD 工具 | **可用** | **可用**Vue 调用 `/api/sl/cad/*` | 不可用 |
| 自定义字段 / 图层 | **可用** | **可用** | 只读 |
| 调度引擎(DispatchLoop) | 进程内拥有 | 远程订阅状态 + 全 API 操作 | 远程订阅状态 + 受限 API |
| 车端通信 | 拥有 | 不直连,只看投影 | 不直连,只看投影 |
| 3D 视口 | 本地 OpenGLCycleGUI 原生) | **嵌入 Simple3 webVRender**(全功能交互) | **嵌入 Simple3 webVRender**(只读 + 圈选) |
| 系统级配置 | 局部(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 顶层["顶层(按端隔离)"]
L1A["Simple3 (CycleGUI + WebAPI Host)"]
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 DTOPOCO + 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 / 健康检查 / 请求审计 |
| `MiGu.Server` | net8.0 ASP.NET Core | 业务 BFF + **YARP 反代 Simple3** + 静态托管两份 Vue dist**启动时校验父进程为 Simple3,否则拒绝运行**(防误用) |
| `platform-vue` | Vue 3 + Vite + Pinia | 全功能套壳:复刻 Simple3 所有控件 |
| `rcsmonitor-vue` | Vue 3 + Vite + Pinia | 运营监控:按权限渲染,运维动作面板 |
| `packages/sl-controls`pnpm workspace | Vue 3 组件库 | Workspace3D(嵌 webVRender/ 地图列表 / 任务表 / CAD 工具栏等共享组件 |
### 6.1 Platform Vue 控件复刻清单(与 Simple3 CycleGUI 对照)
| Simple3 CycleGUI 面板 | Vue 组件(位于 `sl-controls` | 备注 |
|--------------------------|--------------------------------|------|
| 主菜单栏 / 工具栏 | `<AppShell>` 顶栏 + 侧栏 | platform-vue 全功能;rcsmonitor-vue 仅显示运营菜单 |
| Workspace 3D 视口 | `<Workspace3D>` | **iframe / Web Component 嵌入 Simple3 webVRender**;交互事件用 `postMessage` 桥接 |
| 地图面板(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>
```
`Simple3/Rendering/SimpleSceneRenderer.cs` 端按 `scope``ro` 控制:
- `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 =>
{
// 透传当前 JWT 的 userId / scope 给 Simple3
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
subgraph App["应用代码 (Simple3 / MiGu.Server)"]
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 无关)
| 逻辑库 | 拥有进程 | 主要表/数据 |
|--------|----------|-------------|
| **simple_main** | Simple3 | Maps, Sites, Tracks, SpecialProps, Missions, Cars, Plugins, DesignAuditLog, OpsAuditLog, LocalUsers, RunModeHistory |
| **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)"]
SL_A["Simple3.exe"]
PL_A["MiGu.Server.exe"]
D_A[("受 ROSE 保护盘 R:\<br/>simple_main.db<br/>platform.db<br/>(WAL)")]
end
subgraph B["备节点 NODE-B (Standby)"]
SL_B["Simple3.exe (suspended)"]
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 与车端均连浮动 IPWS 自动重连 < 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 |
| `RES_SVC_SL` | Service: Simple3 (Web-Enabled) | RES_DISK_R, RES_VIP_LAN |
| `RES_SVC_PL` | Service: MiGu.Server | RES_DISK_R, RES_VIP_LAN, RES_SVC_SL |
| `RES_SCRIPT_CHK` | 数据库健康检查脚本 | RES_DISK_R |
**RTO / RPO**RTO < 60sRPO ≈ 0(块级镜像)。
### 7.4 MySQL + InnoDB Cluster / MGR
```mermaid
flowchart TB
subgraph App["应用层 (VIP + 重试)"]
SL["Simple3.exe"]
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 ≈ 1030sMGR 自动选主);RPO ≈ 0(半同步 / Group Replication 强一致)。
### 7.5 PostgreSQL + Patroni / 流复制
```mermaid
flowchart TB
subgraph App["应用层"]
SL["Simple3.exe"]
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
```
**适用场景**:复杂查询 / GISPostGIS/ 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 ≈ 1030sPatroni 自动 failover);RPO ≈ 0(同步流复制)。
### 7.6 SQL Server + Always On Availability Groups
```mermaid
flowchart TB
subgraph App["应用层"]
SL["Simple3.exe"]
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 形态****WSFCWindows Server Failover Cluster+ Always On Availability Groups**;通过 **AG Listener** 暴露统一虚拟名/VIP,客户端连 Listener 名即可。
- **同步策略**:核心库(`platform` / `simple_main`)配 1 个同步提交副本(RPO=0),1–N 个异步副本(可读分析)。
- **认证**:建议混合身份认证;MiGu.Server 用 SQL LoginSimple3 桌面端可走 Windows 集成认证。
- **TempDB**:每节点分摊;MultipleActiveResultSetsMARS)建议关闭,EF Core 已能避免。
- **备份**`BACKUP DATABASE ... WITH COMPRESSION` + 事务日志备份每 15min;保留策略与公司 IT 一致。
- **RTO / RPO**RTO ≈ 1030sAG 自动 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 | 1030s | 1030s | 1030s |
| **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 LicenseEnterprise 较贵) |
| **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}",
"extra": "Encrypt=True;TrustServerCertificate=False;MultiSubnetFailover=True;Application Name=Simple3"
},
"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)。
- **数据类型差异**UUIDSQLite TEXT / MySQL CHAR(36) / Pg uuid / MSSQL UNIQUEIDENTIFIER)、JSONSQLite 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
```
**落地四件套:**
1. **认证**`SimpleShared.Auth` 提供 `JwtIssuer`Web-Enabled 时 MiGu.Server 签发;Desktop-Only 时 Simple3 本地签发短期 Token24h)。
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
participant L as Simple3 (YARP 后侧)
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
Pub -- "REST PUT /api/sl/config/{section} (via YARP)" --> SL["Simple3 (主服务)"]
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);
```
**发布机制:** Schema → 校验 → 版本 → 灰度 → 全量;MiGu.Server 通过 YARP `PUT /api/sl/config/{section}` 推到 Simple3,主服务原子切换 + ack;失败自动回滚,WS `config.rollback` 广播。
---
## 10. 关键交互序列
### 10.1 启动登录 + Web-Enabled 三端建链
```mermaid
sequenceDiagram
participant U as 用户
participant L as Simple3 (CycleGUI)
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
U->>L: 启动 Simple3.exe
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 用户
participant L as Simple3
participant BL as BootLoginPanel
U->>L: 启动 Simple3.exe
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: 进入设计端主界面
```
### 10.3 platform-vue 编辑地图站点 → Simple3 落库
```mermaid
sequenceDiagram
participant V as platform-vue
participant P as MiGu.Server (YARP)
participant L as Simple3 (WebAPI)
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
P-->>V: WS event (前端 Workspace3D iframe 收到 Simple3 推送的 PutModelObject 同步)
P-->>RV: WS event (RCSMonitor 接收并按权限展示)
```
### 10.4 平台编辑路径规划策略 → 主服务热加载
```mermaid
sequenceDiagram
participant V as platform-vue
participant P as Platform
participant L as Simple3
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)
participant L as Simple3 (Ops 网关)
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: 心跳超时 → 触发切换
ROSE->>NA: 强制停止 Simple3 / Platform
ROSE->>NA: 卸载 R: 卷
ROSE->>NB: 挂载 R: 卷
ROSE->>NB: 启动 Simple3 (WAL 重放 + 主库自检)
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 目标 < 60sRPO ≈ 0
```
---
## 11. 通信协议矩阵
| 通道 | 协议 | 编码 | 端点 | 用途 |
|------|------|------|------|------|
| MiGu.Server (YARP) ↔ Simple3 | REST / WebSocket | JSON | `:7001` / `:7002` | 全功能 API 反代 + 事件订阅 |
| platform-vue ↔ MiGu.Server | REST / WebSocket | JSON | `:8080` / `:8081` (路径前缀 `/admin` | UI 操作、事件推送 |
| rcsmonitor-vue ↔ MiGu.Server | REST / WebSocket | JSON | `:8080` / `:8081` (路径前缀 `/monitor``scope=RCSMonitor`) | UI 操作、事件推送(受限) |
| Vue Workspace3D ↔ Simple3 WebTerminal | WSSwebVRender 协议) | webVRender 自有 | `:8223` | 3D 视口同步(嵌入 iframe |
| 子进程心跳 | Named Pipe | JSON | `\\.\pipe\simple-bootstrap` | 守护 / 重启 |
| Vehicle ↔ Simple3 | TCP / WS / OPC-UA | 业务私有 | `:8222` | 车端协议 |
| 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` 做乐观锁
- SwaggerPlatform / Simple3 都暴露 `/swagger`(生产可关)
- **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)"]
SL_A["Simple3.exe<br/>Kestrel :7001/7002<br/>WebTerminal :8223"]
PL_A["MiGu.Server.exe<br/>:8080/8081<br/>(YARP + 双 Vue dist)"]
R_A["ROSE 受保护盘 R:\<br/>两库 (WAL)"]
end
subgraph NB["NODE-B (Standby)"]
SL_B["Simple3.exe (suspended)"]
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
```
**端口规划(单逻辑节点):**
| 端口 | 服务 | 启用条件 |
|------|------|----------|
| 7001 | Simple3 WebAPI | Web-Enabled |
| 7002 | Simple3 WebSocket | Web-Enabled |
| 8080 | Platform WebAPI + platform-vue 静态(/admin+ rcsmonitor-vue 静态(/monitor | Web-Enabled |
| 8081 | Platform WebSocket | Web-Enabled |
| 8222 | Simple3 车端 / API(现有) | 所有模式 |
| 8223 | Simple3 WebTerminalwebVRenderVue 嵌入用) | Web-Enabled |
> 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["两端后端"]
SL["Simple3 (CycleGUI + WebAPI)"]
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 → IConfigSnapshotProviderEF Core + SQLite + 迁移 |
| **M1 - 启动登录 + 运行模式** | 1 周 | `BootLoginPanel``RunMode` 枚举;CycleGUI 状态窗;`simple.json.runMode` 持久化;Web-Enabled 业务面板硬隐藏 |
| **M2 - WebAPI 协议层** | 1.5 周 | Simple3 集成 Kestrel;定义 `/api/projection/*``/api/ops/*``/api/config/*``/api/map/*` 等;WS HubJWT |
| **M3 - 共享前端组件库** | 1.5 周 | `frontends/packages/sl-controls`Workspace3D(嵌入 webVRender+ 地图列表 + 任务表 + CAD 工具栏 + OpsActionPanel 等 |
| **M4 - Platform 后端 + YARP** | 2 周 | MiGu.ServerWebAPI + 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"开关 |
| 3 | 3D 引擎 | **复用 Simple3 WebTerminal 的 webVRender**Vue 通过 iframe / Web Component 嵌入 |
| 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 自动重连 |
| 10 | **Platform / RCSMonitor 启动归属(v1.4** | **由 Simple3 内置 `SystemMission.StartPlatformMission` 拉起并守护**;不允许独立安装 MiGu.Server.exe 为 Windows ServiceMiGu.Server 启动时校验父进程必须是 Simple3 |
| 11 | **持久层多 Providerv1.5** | EF Core 抽象,**默认 SQLite + ROSE HA**;可选 **MySQL InnoDB Cluster / PostgreSQL Patroni / SQL Server Always On AG**;每 Provider 一套 EF MigrationsCI 全矩阵跑通;运行期由 `simple.json.database.provider` 决定 |
| 12 | **跨 Provider 切换策略** | 不支持热切;切换走停服窗口 + `tools/db/migrate.ps1`;旧文件作为回退包保留 |
---
## 16. 现有代码影响面
| 现有文件 | 改动方向 |
|----------|----------|
| `Simple3/Program.cs` | 在 `Enssentials.Load` 之后插入 `BootLoginPanel.RunModal()`;根据返回的 `RunMode` 决定后续路径 |
| `Simple3/Configuration.cs` | 拆为 `LocalBootConfig`(启动期,含 `runMode``rememberRunMode`、监听地址、各端口)+ `IConfigSnapshotProvider`(运行期);数据库路径走 `%SIMPLE_DATA_ROOT%` |
| `Simple3/Startup.cs` | 按 `RunMode` 分支:Desktop-Only 走原有 CycleGUIWeb-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=` 参数禁用编辑工具 |
| `SimpleScheduler/WebApi.cs` | 调用方从 Nancy 切到 ASP.NET Core Controller;保持 `SchedulerRuntime` 外观稳定 |
| `Simple3/SimpleProject.cs` | 持久化双写:JSON(兼容)+ SQLite(新主存,受 ROSE 保护盘) |
| `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 logodrop-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 logodrop-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 高 + 圆角 8pxhover `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.7v1.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/内存存储 |
| B3 | YARP `/api/sl/*` → :8222 | ✅ 配置 | 依赖 Simple3 实际监听 |
| B4 | 静态托管 + SPA fallback | ✅ | 单一 `wwwroot/index.html` |
| B5 | JWT 真签发 + 库持久化 | ☐ | 仍为 Mock |
| B6 | 父进程 Simple3 校验 | ☐ | §4、§15 #10 |
### 18.3 Simple3 / 共享内核(ARCHITECTURE 主体)
| # | 项 | 状态 | 备注 |
|---|-----|------|------|
| 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` 同步 | ✅ 本节 |
---
## 关联文档
- [SIMPLELITE_DEVELOPMENT_PLAN.md](./SIMPLELITE_DEVELOPMENT_PLAN.md) — Simple3 功能对齐执行计划
- [SIMPLELITE_COMPOSER_PARITY_PLAN.md](./SIMPLELITE_COMPOSER_PARITY_PLAN.md) — 与 SimpleComposer 差距分析