feat(platform): 部署配置向导 + 地图管理,地图编辑器接入统一存取与 AI 助手

- 配置向导:登录按 deployment 画像引导平台选型(导航方式/模块/场景),未完成则路由守卫强制进入 /wizard;选型驱动菜单按需裁剪,并联动 SimpleLite 写 plugins/active-scenes.json + 透传 --scenes 选择性加载导航场景插件
- 地图管理页:服务器地图列表/使用/重命名/删除、地图合并、多地图连接管理
- 地图编辑器:项目存取改为存入地图管理统一目录(同名替换确认),支持 ?map=/?new= 进入,新增右侧可停靠 AI 助手面板
- 集成 PTL 拣选模块;新增车队分配面板(运维总览/筛选联动)
- SimpleLiteBuildSync 同步运行时依赖 DLL;重新构建前端静态资源

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zhaowei.huang
2026-06-03 09:37:07 +08:00
co-authored by Cursor
parent e2269e430d
commit 7382e85598
109 changed files with 3481 additions and 398 deletions
+83 -1
View File
@@ -1,6 +1,7 @@
using System.Diagnostics;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text.Json;
using Microsoft.Extensions.Options;
namespace MiGu.Server.Launcher;
@@ -246,13 +247,94 @@ public sealed class SimpleLiteLauncher : IDisposable
};
}
private static string BuildArguments(string displayMode, string extra)
private string BuildArguments(string displayMode, string extra)
{
var args = $"--display-mode={displayMode}";
// 选择性加载:把平台写入的 plugins/active-scenes.json 同步透传为 --scenes(命令行优先级最高,与文件一致,双保险)。
var sceneArg = ReadActiveScenesArg();
if (!string.IsNullOrEmpty(sceneArg)) args += " " + sceneArg;
if (!string.IsNullOrWhiteSpace(extra)) args += " " + extra.Trim();
return args;
}
/// <summary>解析 SimpleLite 工作目录(与拉起时一致):优先显式 <see cref="SimpleLiteOptions.WorkingDirectory"/>
/// 否则取解析到的 exe 所在目录。两者都拿不到返回 null。</summary>
public string? ResolveWorkingDirectory()
{
if (!string.IsNullOrWhiteSpace(_opts.WorkingDirectory))
return Path.GetFullPath(_opts.WorkingDirectory);
var resolved = ResolveExecutable(_opts.ExecutablePath);
return resolved == null ? null : (Path.GetDirectoryName(resolved) ?? Environment.CurrentDirectory);
}
/// <summary>SimpleLite 的 plugins 目录(工作目录/plugins);定位不到工作目录时返回 null。</summary>
public string? ResolvePluginsDir()
{
var wd = ResolveWorkingDirectory();
return wd == null ? null : Path.Combine(wd, "plugins");
}
/// <summary>
/// 把「配置向导选定的导航场景」写入 SimpleLite 的 <c>plugins/active-scenes.json</c> ——
/// 这是「平台 → 内核」选择性加载的主通道。下次 SimpleLite 启动即据此只加载选定导航场景插件;
/// 已在运行的实例需重启或调 <c>POST /projection/scenes/apply</c> 才生效。字段名与 SimpleLite 端
/// <c>ActiveScenesConfig</c> 对齐(activeScenes / alwaysLoad / source / updatedAt)。
/// </summary>
public ActiveScenesWriteResult WriteActiveScenes(IEnumerable<string> activeScenes, IEnumerable<string>? alwaysLoad, string source)
{
var pluginsDir = ResolvePluginsDir();
if (pluginsDir == null)
return new ActiveScenesWriteResult(false, null, "未找到 SimpleLite 工作目录/可执行文件,无法定位 plugins 目录");
try
{
Directory.CreateDirectory(pluginsDir);
var path = Path.Combine(pluginsDir, "active-scenes.json");
var payload = new
{
activeScenes = (activeScenes ?? Enumerable.Empty<string>())
.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList(),
alwaysLoad = (alwaysLoad ?? Enumerable.Empty<string>())
.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList(),
source = string.IsNullOrWhiteSpace(source) ? "deployment-profile" : source,
updatedAt = DateTime.UtcNow
};
File.WriteAllText(path, JsonSerializer.Serialize(payload, new JsonSerializerOptions { WriteIndented = true }));
_log.LogInformation("[SimpleLite] active-scenes.json 写入 {Path}: active=[{Scenes}]",
path, string.Join(",", payload.activeScenes));
return new ActiveScenesWriteResult(true, path, null);
}
catch (Exception ex)
{
_log.LogWarning(ex, "[SimpleLite] 写 active-scenes.json 失败");
return new ActiveScenesWriteResult(false, null, ex.Message);
}
}
/// <summary>从已写入的 active-scenes.json 读取激活场景,拼成 <c>--scenes=a,b</c>(拉起时透传);无内容返回 null。</summary>
private string? ReadActiveScenesArg()
{
try
{
var pluginsDir = ResolvePluginsDir();
if (pluginsDir == null) return null;
var path = Path.Combine(pluginsDir, "active-scenes.json");
if (!File.Exists(path)) return null;
using var doc = JsonDocument.Parse(File.ReadAllText(path));
if (!doc.RootElement.TryGetProperty("activeScenes", out var arr) || arr.ValueKind != JsonValueKind.Array)
return null;
var ids = arr.EnumerateArray()
.Where(e => e.ValueKind == JsonValueKind.String)
.Select(e => e.GetString())
.Where(s => !string.IsNullOrWhiteSpace(s))
.ToList();
return ids.Count == 0 ? null : "--scenes=" + string.Join(",", ids);
}
catch { return null; }
}
/// <summary>写 active-scenes.json 的结果(供向导保存接口回显)。</summary>
public readonly record struct ActiveScenesWriteResult(bool Ok, string? Path, string? Error);
/// <summary>
/// 关闭所有 SimpleLite 进程、同步 obj→bin 最新 DLL,再按 launchMode 重新拉起。
/// </summary>