# 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` 调用 `SimpleLiteLauncher.MaybeStart(launchMode)` 按所选模式拉起 `SimpleLite.exe`, > 通过 `--display-mode=web|web+local` 透传给 SimpleLite 的 `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) 启动(登录后按所选模式拉起 SimpleLite) dotnet run # 或:.\bin\Debug\net8.0\MiGu.Server.exe ``` **更新前端**:在 Simple 仓库执行 `build-platform-frontend.bat`,将 `frontends/apps/simple-platform-vue/dist/` 同步到本目录 `wwwroot/`。 ## 配置 SimpleLite 路径(必读) > **配置文件位置(无 Web 界面):** > **`MiGu.Server/appsettings.json`** → 搜索 **`"SimpleLite"`** 节点。 > 登录页选「本地 + Web / 仅 Web」后,后端在此配置的路径拉起 `SimpleLite.exe`。 > 启动后可在浏览器打开 **`http://localhost:8080/api/health/simplelite`** 查看路径是否解析成功。 登录成功后,MiGu.Server 会按所选启动模式拉起 **SimpleLite.exe**。默认已在 `appsettings.json` 写好与 Simple 仓库并列的相对路径;开发机可用 `appsettings.Development.json` 覆盖为绝对路径。 ### 本机开发(与 Simple 仓库并列) 仓库默认已在 `appsettings.Development.json` 中写好: ```json "SimpleLite": { "Enabled": true, "ExecutablePath": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\SimpleLite.exe", "WorkingDirectory": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug", "ProjectionPort": 8222, "ReadinessTimeoutMs": 8000, "FollowParent": false } ``` 请先编译 SimpleLite(在 Simple 仓库): ```pwsh cd ..\Simple dotnet build SimpleLite\SimpleLite.csproj -c Debug ``` 若你的 Simple 不在上述绝对路径,可改为**相对路径**(相对 `MiGu.Server` 工作目录): ```json "ExecutablePath": "..\\..\\Simple\\SimpleLite\\bin\\Debug\\SimpleLite.exe", "WorkingDirectory": "..\\..\\Simple\\SimpleLite\\bin\\Debug" ``` ### 配置项说明 | 键 | 含义 | |----|------| | `Enabled` | `false` 时永不拉起 SimpleLite(只调试平台 UI) | | `ExecutablePath` | `SimpleLite.exe` 绝对或相对路径;留空则自动探测(见 `Launcher/SimpleLiteOptions.cs`) | | `WorkingDirectory` | 子进程工作目录;留空则用 exe 所在目录(读写 `simple.json` / `imgui.ini`) | | `ProjectionPort` | 就绪检测端口,默认 `8222` | | `ReadinessTimeoutMs` | 登录后等待 SimpleLite WebAPI 就绪的最长时间(毫秒) | | `FollowParent` | `true` 时 MiGu.Server 退出会结束 SimpleLite;默认 `false`(两进程独立) | ### 环境变量覆盖 ```pwsh $env:SimpleLite__ExecutablePath = "D:\apps\SimpleLite.exe" $env:SimpleLite__WorkingDirectory = "D:\apps" $env:SimpleLite__Enabled = "true" ``` ### 生产 / 合并发布 将 `SimpleLite.exe` 与依赖 DLL 放到 `MiGu.Server` 同目录,并清空 `ExecutablePath`(走自动探测 `./SimpleLite.exe`),或在 `appsettings.Production.json` 写死部署路径。 浏览器访问 `http://localhost:8080/login`: 1. 用户名 / 密码(`appsettings.json:Auth.Users` 默认 admin/admin、ops/ops); 2. 选 scope(管理员 / 运营); 3. 选 **SimpleLite 启动模式**: - **本地 + Web** → 透传 `--display-mode=web+local`,桌面 ImGui 窗口 + 浏览器同时启动; - **仅 Web** → 透传 `--display-mode=web`,只起 WebTerminal,不弹本地窗口; 4. 点登录。`AuthController.Login` 调用 `SimpleLiteLauncher.MaybeStart(...)` 阻塞等 Projection (`:8222`) 就绪后返回 LoginResponse。 > 如果只想跑 MiGu.Server 单进程调试(不拉 SimpleLite),把 `appsettings.json:SimpleLite.Enabled` 改为 `false` 即可。 ## 数据库启动流程 持久层在独立工程 **[MiGu.DB](../MiGu.DB/README.md)**;Server 通过 `PlatformPersistence` 接入。 ### 启动顺序(`Program.cs`) ```text AddPlatformPersistence(configuration) # 注册 AddMiGuDb + Wms/SimpleFields 等业务服务 │ ▼ builder.Build() │ ▼ MigrateMiGuDbAsync() # Schema 初始化 + 可选 IDataMigrator │ ▼ … 其余中间件 … UseMiddleware # 请求级写入 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(库已存在则不改结构) → IDataMigrator(ApplyDataMigratorsOnStartup=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`)。 > > **SimpleLite 8222 内部 API(RV-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/`(SimpleLite WebAPI;当前 SimpleLite 未启 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"` 字段,登录成功后 `SimpleLiteLauncher` 据此拉起子进程; - SimpleLite 子进程编排:`Launcher/SimpleLiteLauncher.cs` 幂等 / Stdout 转发 / Windows JobObject 父子绑定 / 端口就绪等待,配置见 `appsettings.json:SimpleLite`; - 运维白名单网关占位:`/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/.../dist;assets/ 与 index.html 已 ignore) ``` ## 独立启动(仅当你不通过 SimpleLite 自启时) ```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)。 > SimpleLite 自启 MiGu.Server 时,Program.cs 会从 `bin\\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 与 SimpleLite 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 透传到 SimpleLite `:8222`,SimpleLite 未启时会得到 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