Files
Migu2.0/MiGu.Server
zhaowei.huangandCursor bc1d4b8b5c feat(server/ops): 运维动作真实下发内核、审计持久化与幂等去重
OpsController 由「假成功队列」改为经投影反射 API 真实下发 SimpleLite,按 appsettings
的 Ops:Dispatch 映射 opCode→kind:Method,并做白名单 + JWT ops claim 双重校验;
审计改用 OpsAuditStore 原子落盘(取代进程内队列,重启不丢),新增 IdempotencyKey 幂等去重;
ExtractNumericId 取首段数字避免 AGV-12-3 误合并。appsettings 同步加 Ops:Dispatch 说明与 sl-route 鉴权策略。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 16:09:29 +08:00
..

MiGu.Server

迷毂 · 智能调度平台」的后端骨架(ASP.NET Core 8 + YARP)。 对应 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。

# 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 中写好:

"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 仓库):

cd ..\Simple
dotnet build SimpleLite\SimpleLite.csproj -c Debug

若你的 Simple 不在上述绝对路径,可改为相对路径(相对 MiGu.Server 工作目录):

"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(两进程独立)

环境变量覆盖

$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 即可。

安全须知(生产部署必读)

重要:以下默认值仅供本地开发,切勿原样用于生产环境。

  • appsettings.json:Auth.Users 内置 admin/adminops/ops 为弱口令演示账号;
  • appsettings.json:Jwt.SecretInternal.Token 为占位符 REPLACE_ME

生产部署务必通过环境变量appsettings.Production.json / 密钥管理覆盖 (ASP.NET Core 配置优先级:环境变量 > appsettings.{Environment}.json > appsettings.json)。 环境变量示例(__ 双下划线表示配置层级):

$env:Auth__Users__admin__Password = "<强密码>"
$env:Jwt__Secret      = "<不少于 32 字节的随机串>"
$env:Internal__Token  = "<随机串>"

详见代码审核 ISSUE-03Doc/CODE_REVIEW_ISSUES_2026-05-29.md)。

SimpleLite 8222 内部 APIRV-04:默认 simple.json:platform.allowLoopbackBypass=true,本机回环请求免 token多租户 / 共享主机的生产环境建议设为 false,强制所有请求携带 X-Platform-Internal-Token(配合 platform.internalTokenMiGu.Server/data/.internal-token)。详见复审 Doc/CODE_REVIEW_WEEK_2026-05-29.md RV-04。

能力清单(本轮)

  • Kestrel 监听 :8080HTTP);
  • 静态托管:启动时自动探测 WebRoot ——
    • 开发模式:仓库内 frontends/apps/simple-platform-vue/dist/ 存在则直接挂为 WebRootpnpm 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 + EffectivePermissionsLoginRequest 新增 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

不在本轮范围:真实 SimpleLite WebAPI、SystemMission 拉起、真实 JWT/RBAC、SQLite/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

独立启动(仅当你不通过 SimpleLite 自启时)

# 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\<Config>\net8.0\ 沿目录向上找 dist; 找到则使用源码区 dist,找不到则使用 bin 同级的 wwwroot——两种启动方式共用同一份前端产物。

与前端联调

  • 开发模式(前端在 Simple 仓库):
    • 前端:cd ../Simple/frontends && pnpm dev:5173),Vite 代理 /apihttp://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

{
  "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 :8222SimpleLite 未启时会得到 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 — v1.6 总体架构(§4 进程拓扑、§6.3 YARP 配置、§9 配置中心、§10 交互序列、§17 视觉规范)
  • [(见 Simple 仓库)frontends/README.md]((见 Simple 仓库)frontends/README.md) — 前端启动与联调说明(含「迷毂」品牌与紫色主题约定)