Files
Migu2.0/MiGu.Server/README.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

329 lines
15 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.
# MiGu.Server
> 「**迷毂 · 智能调度平台**」的后端骨架(ASP.NET Core 8 + YARP)。
> 对应 [ARCHITECTURE.md](ARCHITECTURE.md) v1.6 §1.1 中提到的「Platform 管理端」后端可执行文件。
>
> **会话 N+1(启动反转):现在 `MiGu.Server.exe` 是主入口**。
> 用户先启动 MiGu.Server,浏览器登录页选择「启动模式」(本地+Web / 仅Web)
> 后端 `AuthController.Login` 调用 `Simple3Launcher.MaybeStart(launchMode)` 按所选模式拉起 `Simple3.exe`
> 通过 `--display-mode=web|web+local` 透传给 Simple3 的 `Configuration.displayMode`。
>
> 工程名 `MiGu.Server` / 程序集 `MiGu.Server.dll` 保持稳定,仅用户可见前端文案改为「迷毂」。
## 克隆后首次部署速查(必读)
本仓库为 **Migu2.0**,已包含预构建的 `wwwroot/` 前端,克隆后可直接编译运行 MiGu.Server。
```pwsh
# 1) 编译 MiGu.Server
cd MiGu.Server
dotnet build MiGu.Server.csproj -c Debug
# 2) 启动(登录后按所选模式拉起 Simple3)
dotnet run
# 或:.\bin\Debug\net8.0\MiGu.Server.exe
```
**更新前端**:在 Simple 仓库执行 `build-platform-frontend.bat`,将 `frontends/apps/simple-platform-vue/dist/` 同步到本目录 `wwwroot/`
## 配置 Simple3 路径(必读)
> **配置文件位置(无 Web 界面):**
> **`MiGu.Server/appsettings.json`** → 搜索 **`"Simple3"`** 节点。
> 登录页选「本地 + Web / 仅 Web」后,后端在此配置的路径拉起 `Simple3.exe`。
> 启动后可在浏览器打开 **`http://localhost:8080/api/health/simple3`** 查看路径是否解析成功。
登录成功后,MiGu.Server 会按所选启动模式拉起 **Simple3.exe**。默认已在 `appsettings.json` 写好与 Simple 仓库并列的相对路径;开发机可用 `appsettings.Development.json` 覆盖为绝对路径。
### 本机开发(与 Simple 仓库并列)
仓库默认已在 `appsettings.Development.json` 中写好:
```json
"Simple3": {
"Enabled": true,
"ExecutablePath": "E:\\Work\\Core\\Simple-FR\\Simple\\Simple3\\bin\\Debug\\Simple3.exe",
"WorkingDirectory": "E:\\Work\\Core\\Simple-FR\\Simple\\Simple3\\bin\\Debug",
"ProjectionPort": 8222,
"ReadinessTimeoutMs": 8000,
"FollowParent": false
}
```
请先编译 Simple3(在 Simple 仓库):
```pwsh
cd ..\Simple
dotnet build Simple3\Simple3.csproj -c Debug
```
若你的 Simple 不在上述绝对路径,可改为**相对路径**(相对 `MiGu.Server` 工作目录):
```json
"ExecutablePath": "..\\..\\Simple\\Simple3\\bin\\Debug\\Simple3.exe",
"WorkingDirectory": "..\\..\\Simple\\Simple3\\bin\\Debug"
```
### 配置项说明
| 键 | 含义 |
|----|------|
| `Enabled` | `false` 时永不拉起 Simple3(只调试平台 UI |
| `ExecutablePath` | `Simple3.exe` 绝对或相对路径;留空则自动探测(见 `Launcher/Simple3Options.cs` |
| `WorkingDirectory` | 子进程工作目录;留空则用 exe 所在目录(读写 `simple.json` / `imgui.ini` |
| `ProjectionPort` | 就绪检测端口,默认 `8222` |
| `ReadinessTimeoutMs` | 登录后等待 Simple3 WebAPI 就绪的最长时间(毫秒) |
| `FollowParent` | `true` 时 MiGu.Server 退出会结束 Simple3;默认 `false`(两进程独立) |
### 环境变量覆盖
```pwsh
$env:Simple3__ExecutablePath = "D:\apps\Simple3.exe"
$env:Simple3__WorkingDirectory = "D:\apps"
$env:Simple3__Enabled = "true"
```
### 生产 / 合并发布
`Simple3.exe` 与依赖 DLL 放到 `MiGu.Server` 同目录,并清空 `ExecutablePath`(走自动探测 `./Simple3.exe`),或在 `appsettings.Production.json` 写死部署路径。
浏览器访问 `http://localhost:8080/login`
1. 用户名 / 密码(`appsettings.json:Auth.Users` 默认 admin/admin、ops/ops);
2. 选 scope(管理员 / 运营);
3.**Simple3 启动模式**
- **本地 + Web** → 透传 `--display-mode=web+local`,桌面 ImGui 窗口 + 浏览器同时启动;
- **仅 Web** → 透传 `--display-mode=web`,只起 WebTerminal,不弹本地窗口;
4. 点登录。`AuthController.Login` 调用 `Simple3Launcher.MaybeStart(...)` 阻塞等 Projection (`:8222`) 就绪后返回 LoginResponse。
> 如果只想跑 MiGu.Server 单进程调试(不拉 Simple3),把 `appsettings.json:Simple3.Enabled` 改为 `false` 即可。
## 数据库启动流程
持久层在独立工程 **[MiGu.DB](../MiGu.DB/README.md)**Server 通过 `PlatformPersistence` 接入。
### 启动顺序(`Program.cs`
```text
AddPlatformPersistence(configuration) # 注册 AddMiGuDb + Wms/SimpleFields 等业务服务
builder.Build()
MigrateMiGuDbAsync() # Schema 初始化 + 可选 IDataMigrator
… 其余中间件 …
UseMiddleware<HttpActorContextMiddleware> # 请求级写入 IActorContext(审计戳)
```
### 配置(`appsettings*.json`
```json
"Database": {
"Provider": "sqlite",
"SchemaMode": "Migrate",
"ApplyDataMigratorsOnStartup": true
},
"ConnectionStrings": {
"Platform": "Data Source=data/platform.db"
}
```
| 配置项 | 含义 | 取值 |
|--------|------|------|
| `Provider` | 数据库种类 | `sqlite`(默认)/ `mysql` / `npgsql` / `sqlserver` |
| `SchemaMode` | 启动时如何初始化 Schema | 见下表 |
| `ApplyDataMigratorsOnStartup` | Schema 完成后是否跑数据修补 | `true`(默认)/ `false` |
当前仓库约定:
- `appsettings.json``"SchemaMode": "Migrate"`(发版/默认)
- `appsettings.Development.json``"SchemaMode": "EnsureCreated"`(本地开发)
也可用环境变量覆盖,例如:`$env:Database__SchemaMode = "Migrate"`
### SchemaMode 行为
| 值 | 行为 | 适用 |
|----|------|------|
| **`Migrate`** | 旧库无 `__EFMigrationsHistory` 时先写 Initial 基线 → `MigrateAsync` →(可选)DataMigrator | 测试 / 预发 / 生产 |
| **`EnsureCreated`** | 仅 `EnsureCreated`(按**当前模型**建空库),**不**跑 Migrations →(可选)DataMigrator | 开发期改实体、暂不生成迁移 |
流程图:
```text
SchemaMode = EnsureCreated
→ EnsureCreatedAsync(库已存在则不改结构)
→ IDataMigratorApplyDataMigratorsOnStartup=true 时)
SchemaMode = Migrate
→ 程序集无 Migration → EnsureCreated 兜底
→ 否则:基线(如需)→ MigrateAsync
→ IDataMigrator(可选)
```
**开发注意(EnsureCreated):**
- 改实体后结构不会自动升级;请删除 `MiGu.Server/data/platform.db*` 再启动。
- 不会写入迁移历史;以后改回 `Migrate` 时,对已有库会走基线再应用 Migrations。
**发版注意:**
- 现场务必使用 `Migrate`,并随包带上 `MiGu.DB/Migrations/Sqlite/`
- 开发期若长期 `EnsureCreated` 改模型,发版前必须对**当前模型**执行 `dotnet ef migrations add`,再切回 `Migrate` 验证;否则空库只会落到旧 Migration,结构落后于代码。
- 从 EnsureCreated 库切到 `Migrate`:若无 History,启动会把**全部** pending Migration 写入基线(假定库已对齐模型 tip)。结构不对齐时应删库重建或手工处理。
### 相关文档
- 持久层总览与 SchemaMode 摘要:[MiGu.DB/README.md](../MiGu.DB/README.md)
- 如何生成/管理 Migration[MiGu.DB/MIGRATIONS.md](../MiGu.DB/MIGRATIONS.md)
## 安全须知(生产部署必读)
> **重要:以下默认值仅供本地开发,切勿原样用于生产环境。**
>
> - `appsettings.json:Auth.Users` 内置 `admin/admin`、`ops/ops` 为弱口令演示账号;
> - `appsettings.json:Jwt.Secret`、`Internal.Token` 为占位符 `REPLACE_ME`。
>
> 生产部署务必通过**环境变量**或 `appsettings.Production.json` / 密钥管理覆盖
> ASP.NET Core 配置优先级:环境变量 > `appsettings.{Environment}.json` > `appsettings.json`)。
> 环境变量示例(`__` 双下划线表示配置层级):
>
> ```pwsh
> $env:Auth__Users__admin__Password = "<强密码>"
> $env:Jwt__Secret = "<不少于 32 字节的随机串>"
> $env:Internal__Token = "<随机串>"
> ```
>
> 详见代码审核 ISSUE-03`Doc/CODE_REVIEW_ISSUES_2026-05-29.md`)。
>
> **Simple3 8222 内部 APIRV-04**:默认 `simple.json:platform.allowLoopbackBypass=true`,本机回环请求免 token;**多租户 / 共享主机的生产环境建议设为 `false`**,强制所有请求携带 `X-Platform-Internal-Token`(配合 `platform.internalToken` 或 `MiGu.Server/data/.internal-token`)。详见复审 `Doc/CODE_REVIEW_WEEK_2026-05-29.md` RV-04。
## 能力清单(本轮)
- Kestrel 监听 `:8080`HTTP);
- 静态托管:启动时自动探测 WebRoot ——
- 开发模式:仓库内 `frontends/apps/simple-platform-vue/dist/` 存在则直接挂为 WebRoot`pnpm build` 后立即生效,无需 robocopy 到 wwwroot);
- 兜底/部署模式:找不到 dist 时回退到 `wwwroot/`(由 `build-platform-frontend.bat` 的 robocopy /MIR 填充);
- SPA fallback 让 `/admin/*` `/monitor/*` `/login` `/status` 等所有非 API 路径都回退到 `index.html`,由 vue-router 接管;
- YARP 反向代理:
- `/api/sl/{**catch-all}``http://127.0.0.1:8222/`Simple3 WebAPI;当前 Simple3 未启 WebAPI 时返回 502);
- `/vr/{**catch-all}``http://127.0.0.1:8223/`webVRender;可用于同源 iframe 解决 X-Frame-Options 限制);
- 14 维度配置中心占位(对齐 §9):内存 + `data/config-{section}.json` 持久化;
- Mock 鉴权:`/api/auth/login` 返回 Mock JWT + `EffectivePermissions`LoginRequest 新增 `launchMode: "WebOnly" | "DesktopAndWeb"` 字段,登录成功后 `Simple3Launcher` 据此拉起子进程;
- Simple3 子进程编排:`Launcher/Simple3Launcher.cs` 幂等 / Stdout 转发 / Windows JobObject 父子绑定 / 端口就绪等待,配置见 `appsettings.json:Simple3`
- 运维白名单网关占位:`/api/sl/ops/execute``/api/sl/ops/audits`
- 投影 API 占位:`/api/projection/{sites,tracks,cars,missions}`
- 健康检查:`/api/health`
- Swagger:开发环境下 `/swagger`
> 能力补充:平台库由 **MiGu.DB**(EF Core)承载,启动流程见「[数据库启动流程](#数据库启动流程)」。
## 目录结构
```
MiGu.Server/
├── MiGu.Server.csproj
├── Program.cs # Kestrel + YARP + CORS + StaticFiles + SPA fallback
├── appsettings.json # YARP 路由 + CORS 白名单
├── appsettings.Development.json
├── Properties/launchSettings.json
├── Controllers/
│ ├── AuthController.cs # /api/auth/login | /api/auth/logout
│ ├── ConfigController.cs # GET/PUT /api/config[/{section}]
│ ├── ProjectionController.cs # /api/projection/{sites,tracks,cars,missions}
│ ├── OpsController.cs # /api/sl/ops/{execute,audits}
│ └── HealthController.cs # /api/health
├── Configs/ # 与 ARCHITECTURE.md §9 一一对齐的强类型 record
│ ├── SystemConfig.cs
│ ├── ExternalIntegrations.cs
│ ├── RoutingPolicy.cs
│ ├── VehicleMaintenancePolicy.cs
│ ├── ChargePolicy.cs
│ ├── TaskAllocationPolicy.cs
│ ├── TrafficRule.cs
│ ├── DeviceManagementConfig.cs
│ ├── FleetLifecycleConfig.cs
│ ├── ScenarioTemplateConfig.cs
│ ├── LocationManagement.cs
│ ├── OpsConfig.cs
│ ├── CustomWidget.cs
│ ├── EffectivePermissions.cs
│ └── ConfigStore.cs # 内存 + JSON 文件持久化(占位)
├── data/ # 运行时生成的 config-{section}.json
└── wwwroot/ # 部署兜底(dev 模式优先用 frontends/.../distassets/ 与 index.html 已 ignore
```
## 独立启动(仅当你不通过 Simple3 自启时)
```pwsh
# 1) 还原 + 编译 + 运行
cd MiGu.Server
dotnet run
# 等价的 build + run 一键脚本:
.\build-and-run.bat # Debug
.\build-and-run.bat release # Release
# 2) 验证
# 打开 http://localhost:8080/ 欢迎页 / 平台前端
# 打开 http://localhost:8080/swagger API 文档
# 打开 http://localhost:8080/api/health 健康检查
# 打开 http://localhost:8080/api/config/system 系统级配置
```
或在 IDE 中以 `MiGu.Server` 作为启动项目。
> 启动时控制台会打印 `[MiGu.Server] WebRoot -> dist: ...` 或 `WebRoot -> wwwroot ...`,提示当前使用哪一份产物。
> 单独启动且既没有跑过 `pnpm build` 也没有跑过 `build-platform-frontend.bat` 时,会看到 SPA fallback 找不到 `index.html`。
> 仓库根 `build-platform-frontend.bat` 仍可用于「打部署包」场景(把 dist robocopy /MIR 同步到 wwwroot)。
> Simple3 自启 MiGu.Server 时,Program.cs 会从 `bin\<Config>\net8.0\` 沿目录向上找 dist
> 找到则使用源码区 dist,找不到则使用 bin 同级的 wwwroot——两种启动方式共用同一份前端产物。
## 与前端联调
- 开发模式(前端在 Simple 仓库):
- 前端:`cd ../Simple/frontends && pnpm dev``:5173`),Vite 代理 `/api``http://127.0.0.1:8080`
- 后端:`cd MiGu.Server && dotnet run``:8080`);
- 浏览器访问 `http://localhost:5173/login`
- 生产/同源模式(本仓库 `wwwroot/`):
- 默认使用 `MiGu.Server/wwwroot/`(已随仓库提交构建产物);
- 若上级目录存在 `frontends/apps/simple-platform-vue/dist/`Program.cs 会优先挂 dist
- 浏览器访问 `http://localhost:8080/login` 等路径由 vue-router 解析。
## YARP 与 Simple3
YARP 路由配置见 `appsettings.json`
```json
{
"ReverseProxy": {
"Routes": {
"sl-route": { "Match": { "Path": "/api/sl/{**catch-all}" }, "ClusterId": "sl-cluster" },
"vrender-route": { "Match": { "Path": "/vr/{**catch-all}" }, "ClusterId": "vrender-cluster" }
},
"Clusters": {
"sl-cluster": { "Destinations": { "sl1": { "Address": "http://127.0.0.1:8222/" } } },
"vrender-cluster": { "Destinations": { "vr1": { "Address": "http://127.0.0.1:8223/" } } }
}
}
}
```
- `/api/sl/ops/{execute,audits}` 在本工程被 `OpsController` 显式接住(更具体的路由),用于占位测试;
- 其他 `/api/sl/*` 由 YARP 透传到 Simple3 `:8222`Simple3 未启时会得到 502/连接被拒。
## iframe 嵌入 webVRender 的两种方式
1. **直连**(默认):前端 iframe 指向 `http://localhost:8223/?scope=...&token=...&ro=...`,跨源;
2. **同源代理**(可选):iframe 指向 `http://localhost:8080/vr/?scope=...`,由本工程 YARP 反代到 8223,
可规避 X-Frame-Options 等限制。前端可通过 `Workspace3D` 组件的 `host` prop 传 `localhost:8080/vr` 切换。
## 相关文档
- [ARCHITECTURE.md](ARCHITECTURE.md) — v1.6 总体架构(§4 进程拓扑、§6.3 YARP 配置、§9 配置中心、§10 交互序列、§17 视觉规范)
- [(见 Simple 仓库)frontends/README.md]((见 Simple 仓库)frontends/README.md) — 前端启动与联调说明(含「迷毂」品牌与紫色主题约定)
- [MiGu.DB/README.md](../MiGu.DB/README.md) — 持久层框架;[MIGRATIONS.md](../MiGu.DB/MIGRATIONS.md) — 生成 Migration 手册
- 本文「[数据库启动流程](#数据库启动流程)」— SchemaMode / EnsureCreated / Migrate