Files
Migu2.0/PLATFORM_V3_CHANGES.md
T
zhaowei.huangandCursor 42978930ca feat: 迁入 MiGu.Server、平台前端与车辆列表 reflection 回退
从 Simple-FR 拆出 Platform.Server 并重命名为 MiGu.Server;frontends 源码与构建脚本迁入本仓库。地图监控在 projection/cars 失败或为空时回退 reflection 车辆列表;Simple 仓库已移除旧 Platform.Server。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 18:16:34 +08:00

195 lines
13 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.
# 平台 v3 重构 — 阶段 0~6 实际改动
> 范围:把 SimpleLite WebTerminal 由"全功能浮动 Panel"模式收敛到"纯 3D Canvas",所有 UI 改由 Vue 平台前端 (`frontends/apps/simple-platform-vue`) 接管;同时补齐反射 / 编辑 / 监控 / 编排 4 大业务面板与撤销栈、AI 生图、报警 SSE 等关键能力。
>
> 基线:上一份方案见 `ARCHITECTURE.md` v2/v3 章节;后续以本文件为准。
## 编译验证
- `dotnet build SimpleLite/SimpleLite.csproj` —— 0 警告 0 错误
- `dotnet build MiGu.Server/MiGu.Server.csproj` —— 0 警告 0 错误
- `build-platform-frontend.bat` —— vite build 46.5s,无 vue-tsc / ts 错误,robocopy /MIR 已同步 MiGu.Server/wwwroot
-`SimpleComposer` (WinForms) 在 v3 之外,不在本次构建目标里
-`HttpClientHelper` 提升 LangVersion=latest 修通
## 阶段 0:后端 SimpleLite 平台化(已完成)
### 新增文件
| 文件 | 作用 |
|------|------|
| `SimpleLite/Web/MapEditApiController.cs` | 写操作 8 路 APIcreate/delete/batch/copy-to/pick/dashboard/upload/ai-generate;阶段 6 又补:**project/save**、**project/load**、**project/current**、**sample-sites-along-track** |
| `SimpleLite/Web/ReflectionApiController.cs` | 只读 + execute APIlistKinds / listObjects / methods-by-type / setField / getBundle / selection 等 |
| `SimpleLite/Web/ReflectionApiDtos.cs` | 反射 API DTO + 统一信封 `ReflectionEnvelope<T>` |
| `SimpleLite/Web/AiCompletionService.cs` | OpenAI Function Calling 调用 + map-generate 工具桥(真实 LLM,不再桩) |
| `SimpleLite/Web/AiConfigController.cs` | AI 服务配置(endpoint/apiKey/model/system prompt/temperature/maxTokens/timeout)读写 |
| `SimpleLite/Web/AlarmStreamService.cs` | 1s 周期 diff `car.status.enums.AlarmInfo`(语义与 `E:\Work\FRLD\StandardScene\Forklift.cs``Commons.GetCarStatus(this, "AlarmInfo")` 等价),变化时通过 SSE `alarm` 事件推送 raise/update/clear |
| `SimpleLite/Web/ProjectionStreamModule.cs` | 平台 SSE 总线,对外广播 `object-created` / `object-deleted` / `object-patched` / `object-batch-changed` / `alarm` / `pick-result` / `project-saved` / `project-loaded` |
| `SimpleLite/Web/PluginAssemblyScan.cs` | 反射扫描 plugins/*.dll,把 `MethodMember` 标注的方法 + Car/Mission/CarProgram/Site/Track 子类型暴露给反射 API |
### 修改文件
| 文件 | 关键改动 |
|------|----------|
| `SimpleLite/Startup.cs` | EntryPoint 第一步 `EnsureImguiIniFromEmbedded()`;启动 AlarmStreamService、ProjectionStreamModule、AiConfig 加载 |
| `SimpleLite/UI/SimpleUI.cs` | `CreateRemotePanelSetForTerminal` 检测 CanvasOnly 旗标:不创建 Workbench / Toolbar / BottomBar / CAD / Welcome;保留 SelectionAction、SceneInput、SceneDrawable、2DMapView |
| `SimpleLite/Workspace/WorkspaceWebUiContext.cs` | 增加 `IsCanvasOnly(terminal)` 旗标 + `/declareCanvasOnly` 注册器(与 `/declareEmbedUi` 互斥) |
| `SimpleLite/Web/ProjectionWebHost.cs` | `/declareCanvasOnly` 路由注册 |
| `SimpleLite/Web/ProjectionWebApiController.cs` | 现有 projection API 上挂 `selection-detail` SSE hook |
| `SimpleLite/UI/ComposerDockPanel.cs` | CanvasOnly 时 PrepareWelcomePanel 返回空 |
| `SimpleLite/Props/UIHelpers.cs` | 增加 `UI_Image` / `UI_Text` / `UI_Model` PropType。`UI_Model` 用于 CycleGUI 装饰物挂模型(obj/glb/gltf/cyclemodel |
### 关键 API 路径
```
POST /projection/map-edit/objects/{kind} # site|track|bezier|arc|nurbs|image|text|model
DELETE /projection/map-edit/objects/{kind}/{id}
POST /projection/map-edit/objects/batch # create/delete/patch 混合
POST /projection/map-edit/objects/{kind}/{id}/fields/copy-to
POST /projection/map-edit/pick # 拾取会话
GET /projection/map-edit/dashboard/summary # KPI 聚合
POST /projection/map-edit/assets/upload # 图片/模型 base64
POST /projection/map-edit/ai/map-generate # 真实 LLM 生图
# 阶段 6 新增(本会话)
POST /projection/map-edit/project/save # body: { path?: string }
POST /projection/map-edit/project/load # body: { path: string }
GET /projection/map-edit/project/current
POST /projection/map-edit/objects/track/{id}/sample-sites
# body: { spacingMm, includeEndpoints?, layer? }
GET /projection/ai-config # 读 AI 服务配置
POST /projection/ai-config # 写 AI 服务配置
GET /projection/reflection/kinds | objects/{kind} | methods/{kind}/{id} | ...
SSE /projection/stream # 所有 SSE 事件
```
## 阶段 1MapEditorView 四栏壳(已完成)
`frontends/apps/simple-platform-vue/src/views/admin/MapEditorView.vue` 完整重写为:
```
┌─────────────────────────────────────────────────────┐
│ EditTopBar:项目 / 文件 / 编辑 / 视图 / 图层 / 对齐吸附 │
│ + 撤销/重做 + AI 生图 + 保存 │
├──────┬─────────────────────────┬───────────────────┤
│ Edit │ Workspace3D │ EditPropertyPanel │
│ Tool │ (canvas-only iframe) │ 对象/类型默认/图层 │
│ Rail │ │ │
├──────┴─────────────────────────┴───────────────────┤
│ EditStatusBar:选中 / 鼠标坐标 / 工具 / 吸附 / undo │
└─────────────────────────────────────────────────────┘
```
### 新增组件
- `components/map-editor/EditTopBar.vue` —— 6 个 dropdown 菜单(项目/文件/编辑/视图/图层/对齐吸附)+ 撤销重做按钮 + AI 生图入口 + 保存
- `components/map-editor/EditToolRail.vue` —— 6 组 CAD 工具按钮 + 吸附组(网格/对象/端点/中点 + 网格步长)
- `components/map-editor/EditPropertyPanel.vue` —— 3 Tab(对象 / 类型默认 / 图层),自动反射出 `getFields`,每行可改可保存;多选时统一字段写入;不一致字段显示「-」;执行 reflectionApi.execute 调任何 MethodMember
- `components/map-editor/EditStatusBar.vue` —— 状态栏
- `components/map-editor/AiGenerateDialog.vue` —— AI 生图对话框
### 反射 API 接入
- `api/mapEdit.ts` —— MapEditApiController 全 8+4 路 API 胶水(本会话补的 4 个:projectSave / projectLoad / projectCurrent / sampleSitesAlongTrack
- `api/reflection.ts` —— ReflectionApiController 全套胶水(含 MOCK 离线 fallback
## 阶段 2:对齐 / 批量生成 / 复制字段 / 类型默认 / 图层(已完成)
- `composables/useAlignment.ts` —— 11 种对齐模式:left/right/top/bottom/centerH/centerV/center/distributeH/distributeV/toFirst/toLast
- `composables/useBatchGenerate.ts` —— 横向/纵向/矩阵/环形 4 种批量生成器,输出 BatchOp[]
- `composables/useClipboard.ts` —— 对象/字段剪贴板
- `transform.copyFields` 工具:MapEditorView `runCopyFields` 读取多选(第 1 个为源,其余目标),prompt 字段名列表,调 `mapEditApi.copyFieldsTo`;撤销时用前置 patch 回滚
- 类型默认 Tab:站点/路径默认 layer/前缀/颜色/线宽,存 localStorage `mapEditor.defaults`
- 图层 Tab:图层名列表 + 可见 / 可选 / 颜色,本地状态
## 阶段 3:AI 生图 + 状态栏 + 项目保存/加载(已完成)
- AiGenerateDialog 通过 `mapEditApi.aiMapGenerate` 真实调用 LLM,后端走 `AiCompletionService.GenerateMapAsync`:用 OpenAI Function Calling 让 LLM 直接调 create_site / create_track / batch_create 等"工具",落地到 SimpleLib
- 入口:EditTopBar 「AI 生图」按钮 → 弹对话框,参数:prompt / mode (sites/tracks/sites+tracks/full) / bounds / referenceImageBase64 / layer / constraints
- 项目保存/加载:EditTopBar「保存」/「另存为」/「加载项目」→ prompt 输入路径 → 调 `mapEditApi.projectSave/Load` → 服务端走 `SimpleProject.ToFile / FromFile`;最近路径缓存到 localStorage `mapEditor.lastProjectPath`
## 阶段 4MapMonitor 浮动报警卡 + KPI + 实时列表(已完成)
`frontends/apps/simple-platform-vue/src/views/admin/MapMonitorView.vue` 改动:
- 删除原 `AlarmTicker.vue` 底部滚动条
- 新增 `components/map-monitor/FloatingAlarmStack.vue` + `FloatingAlarmCard.vue`,可拖动 / 可吸边 / 可堆叠
- 报警来源走 `useMapEditStream.onAlarm`(订阅 SSE 的 `alarm` 事件,由 AlarmStreamService 1s diff 推送)
- KPI 3 卡:在线车辆 / 运行中任务 / 排队任务
- 实时列表:左侧 Workspace3D + 右侧 VehicleMonitorPanel + SelectionDetailPanel
- 「定位」按钮通过 `reflectionApi.setSelection('car', id)` 让 SimpleLite 3D 高亮该车(视觉上等效 flyTo)
## 阶段 5OrchestrationView 设计与编排(已完成)
`views/admin/OrchestrationView.vue` 5-Tab 壳:
| Tab | 来源 |
|-----|------|
| 车辆 | 复用现有 `CarPanelView` |
| 进程 | 复用现有 `MissionEditorView` |
| 脚本 | `components/orchestration/ReflectionKindTable.vue`kind=script):动态列表 + 反射 method 执行 |
| 任务流 | ReflectionKindTablekind=mission, extraFilter='taskflow'|
| 触发器 | ReflectionKindTablekind=mission, extraFilter='trigger'|
Tab 状态缓存到 sessionStorage。
## 阶段 6:联调 / 撤销栈做实 / 文档(已完成)
### 撤销栈做实
`composables/useHistory.ts` 提供 `run/undo/redo/composite`,每个命令 `apply/revert` 双向。本次把 MapEditorView 里所有占位 revert 改成真实回滚:
| 命令 | 原 revert | 现 revert |
|------|-----------|-----------|
| `runCreateSite` | delete 创建的 id | (未变)|
| `handleTrackPick` 创建路径 | delete 创建的 id | (未变)|
| `deleteSelection` | ⚠️ 仅本地撤销 | **getBundle 抓快照 → batch.create 重建(id 重排)** |
| `onSetField` 字段修改 | ⚠️ 不回滚 | **getBundle 抓原值 → setField 回去** |
| `applyAlignment` | (已用 patch 反向恢复) | (未变)|
| `applyBatchAsCommand` | (已用 delete 反向移除) | (未变)|
| `runAlongPathSampling`(新增)| —— | delete 所有采样产生的 site id |
| `runCopyFields`(新增)| —— | patch 反向把 targets 字段值恢复 |
### 沿路径采样
- 后端:`POST /projection/map-edit/objects/track/{id}/sample-sites { spacingMm, includeEndpoints?, layer? }` —— 当前在 siteA → siteB 之间欧式直线均分(贝塞尔/弧的真实参数化采样留待后续 Track 子类 PR)
- 前端:选中 1+ 条 track,工具栏「沿路径」→ prompt 间距和端点开关 → 调后端,撤销可回退
### 编译相关清理
- `SimpleLite/Recording/`(之前的录制/回放实验目录)误用 `SimpleLite.Rendering` namespace、`Car` 找不到、`Start(string, out)` 默认参数顺序等 3 个错误 → 修
- `SimpleLite/Props/UISite.cs``SimpleLite.Props` namespace 下 `Workspace.AddProp` 被解析成 `SimpleLite.Workspace.AddProp` → 改为全限定 `CycleGUI.Workspace.AddProp`
- `SimpleLite/Workspace/WorkspaceBottomBar.cs``using SimpleLite.Workspace;` → 补
- `HttpClientHelper.csproj` LangVersion 7.3 → latest
## 已知 TODO(不阻塞 v3 上线)
1. 贝塞尔/弧/Nurbs 路径的真实参数化采样(当前直线均分)
2. 撤销栈跨 Tab / 跨 session 共享(当前仅 MapEditor 视图局部)
3. Workspace3D 真正的 postMessage 'workspace.flyTo' 双向桥(当前用 setSelection 代替)
4. 复制字段 UI 支持类型化字段挑选器(当前 prompt 输入字段名)
5. AI 生图 OAuth/键持久化加密(当前明文存 config)
6. Recording 录制/回放模块未完工(PlaybackController 已修通编译,UI 已挂底栏,但实际录制/回放业务逻辑需要后续单独 PR 完成)
## 启动方式
```
# 1. 后端 buildSimpleLite + MiGu.Server
dotnet build SimpleLite\SimpleLite.csproj
dotnet build MiGu.Server\MiGu.Server.csproj
# 2. 前端打包并同步到 wwwroot
.\build-platform-frontend.bat
# 3. 启动平台 + SimpleLite
dotnet run --project MiGu.Server # 监听 0.0.0.0:8080
# SimpleLite 端口 8222/8223 由 SimpleLite 自己启动
# 4. 访问
http://127.0.0.1:8080/admin/map-editor # 地图编辑(v3 四栏壳)
http://127.0.0.1:8080/admin/map-monitor # 地图监控(驾驶舱 + 浮动报警)
http://127.0.0.1:8080/admin/orchestration # 设计与编排(5 Tab
```