144 lines
5.6 KiB
C#
144 lines
5.6 KiB
C#
using System.Security.Claims;
|
||
using Microsoft.AspNetCore.Authorization;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using MiGu.Server.Configs;
|
||
using MiGu.Server.Launcher;
|
||
|
||
namespace MiGu.Server.Controllers;
|
||
|
||
/// <summary>
|
||
/// 配置向导 API。登录后若 <c>deployment.Configured=false</c>(见 <c>LoginResponse.NeedsWizard</c>),
|
||
/// 前端进入向导:
|
||
/// <list type="bullet">
|
||
/// <item><c>GET /api/wizard/options</c>:可选项目录(导航方式 / 模块 / 业务场景模板)。</item>
|
||
/// <item><c>GET /api/wizard/profile</c>:回显当前部署画像(含由导航选型推导的激活场景 id)。</item>
|
||
/// <item><c>PUT /api/wizard/profile</c>:保存并置 <c>Configured=true</c>。</item>
|
||
/// <item><c>POST /api/wizard/reset</c>:把 <c>Configured</c> 置回 false 以重新引导(保留草稿)。</item>
|
||
/// </list>
|
||
/// 说明:保存时即把选型固化为单一事实来源 <c>deployment</c> section,并同步联动 Launcher ——
|
||
/// <see cref="SaveProfile"/> 调 <c>WriteActiveScenes</c> 写 <c>plugins/active-scenes.json</c> / 透传 <c>--scenes</c>,
|
||
/// 驱动 SimpleLite 下次启动选择性加载选定的导航场景插件。
|
||
/// </summary>
|
||
[ApiController]
|
||
[Authorize]
|
||
[Route("api/wizard")]
|
||
public class WizardController : ControllerBase
|
||
{
|
||
private readonly ConfigStore _store;
|
||
private readonly SimpleLiteLauncher _launcher;
|
||
private readonly ILogger<WizardController> _log;
|
||
|
||
public WizardController(ConfigStore store, SimpleLiteLauncher launcher, ILogger<WizardController> log)
|
||
{
|
||
_store = store;
|
||
_launcher = launcher;
|
||
_log = log;
|
||
}
|
||
|
||
[HttpGet("options")]
|
||
public IActionResult Options()
|
||
{
|
||
return Ok(new
|
||
{
|
||
navigationKinds = DeploymentCatalog.NavigationKinds,
|
||
modules = DeploymentCatalog.Modules,
|
||
scenarios = _store.Get("scenario").Payload
|
||
});
|
||
}
|
||
|
||
[HttpGet("profile")]
|
||
public IActionResult GetProfile() => Ok(Project(_store.GetDeployment()));
|
||
|
||
/// <summary>当前部署画像对菜单的裁剪结果(供前端做面板/能力级裁剪与排查)。</summary>
|
||
[HttpGet("effective-pages")]
|
||
public IActionResult EffectivePages()
|
||
{
|
||
var dp = _store.GetDeployment();
|
||
return Ok(new
|
||
{
|
||
configured = dp.Configured,
|
||
tailorablePages = DeploymentCatalog.TailorablePages(),
|
||
enabledPages = DeploymentCatalog.EnabledPages(dp),
|
||
hiddenPages = DeploymentCatalog.HiddenPages(dp)
|
||
});
|
||
}
|
||
|
||
[HttpPut("profile")]
|
||
public IActionResult SaveProfile([FromBody] SaveWizardRequest req)
|
||
{
|
||
if (req == null)
|
||
return BadRequest(new { message = "请求体不能为空" });
|
||
|
||
var user = User.Identity?.Name ?? User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "";
|
||
var profile = new DeploymentProfile(
|
||
Configured: true,
|
||
PlatformType: string.IsNullOrWhiteSpace(req.PlatformType) ? "standard" : req.PlatformType.Trim(),
|
||
Modules: Clean(req.Modules),
|
||
NavigationKinds: Clean(req.NavigationKinds),
|
||
Scenarios: Clean(req.Scenarios),
|
||
UpdatedBy: user);
|
||
|
||
_store.PutDeployment(profile);
|
||
|
||
// 平台 → 内核联动:把导航选型写入 SimpleLite 的 plugins/active-scenes.json(下次启动选择性加载;
|
||
// 已运行实例可由前端再调 POST /api/sl/projection/scenes/apply 触发增量 reload)。
|
||
var sceneIds = profile.ToActiveSceneIds();
|
||
var write = _launcher.WriteActiveScenes(sceneIds, alwaysLoad: null, source: "deployment-profile");
|
||
|
||
_log.LogInformation("部署向导已保存 by={User} nav=[{Nav}] scenes=[{Scenes}] activeScenesWritten={Ok}",
|
||
user, string.Join(",", profile.NavigationKinds), string.Join(",", sceneIds), write.Ok);
|
||
|
||
return Ok(Project(profile, write));
|
||
}
|
||
|
||
[HttpPost("reset")]
|
||
public IActionResult Reset()
|
||
{
|
||
var reset = _store.GetDeployment() with { Configured = false };
|
||
_store.PutDeployment(reset);
|
||
return Ok(Project(reset));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 统一对外投影:camelCase 字段 + 推导出的 activeSceneIds(导航选型 → 内核场景 id)+
|
||
/// hiddenPages(被裁剪的菜单页)+ 可选的 activeScenesWrite(保存时写 active-scenes.json 的结果)。
|
||
/// </summary>
|
||
private static object Project(DeploymentProfile dp, SimpleLiteLauncher.ActiveScenesWriteResult? write = null) => new
|
||
{
|
||
configured = dp.Configured,
|
||
platformType = dp.PlatformType,
|
||
modules = dp.Modules,
|
||
navigationKinds = dp.NavigationKinds,
|
||
scenarios = dp.Scenarios,
|
||
updatedBy = dp.UpdatedBy,
|
||
activeSceneIds = dp.ToActiveSceneIds(),
|
||
hiddenPages = DeploymentCatalog.HiddenPages(dp),
|
||
activeScenesWrite = write == null ? null : new
|
||
{
|
||
ok = write.Value.Ok,
|
||
path = write.Value.Path,
|
||
error = write.Value.Error
|
||
}
|
||
};
|
||
|
||
/// <summary>去空白、去重(大小写不敏感)、保持顺序。</summary>
|
||
private static List<string> Clean(List<string>? items)
|
||
{
|
||
var result = new List<string>();
|
||
if (items == null) return result;
|
||
foreach (var s in items)
|
||
{
|
||
var t = s?.Trim();
|
||
if (!string.IsNullOrEmpty(t) && !result.Contains(t, StringComparer.OrdinalIgnoreCase))
|
||
result.Add(t!);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
public record SaveWizardRequest(
|
||
string? PlatformType,
|
||
List<string>? Modules,
|
||
List<string>? NavigationKinds,
|
||
List<string>? Scenarios);
|
||
}
|