* 新增 ControllerClientCompatOptions.ConfigRoot 及解析方法 * 兼容层默认从运行目录 Config 加载模型、轨迹和配置 * 移除隐式父工作区根目录推断,旧路径仅在显式配置时生效 * Host 项目编译时将 Config 目录复制到输出目录 * 请求响应日志中间件忽略 /api/status/snapshot 高频轮询 * 补充 ConfigRoot 和日志过滤相关单元测试
121 lines
4.2 KiB
C#
121 lines
4.2 KiB
C#
namespace Flyshot.Core.Config;
|
|
|
|
/// <summary>
|
|
/// 标识需要生成哪一种平台风格的兼容路径。
|
|
/// </summary>
|
|
public enum CompatibilityPathStyle
|
|
{
|
|
/// <summary>
|
|
/// 使用 Linux/Unix 风格路径。
|
|
/// </summary>
|
|
Posix,
|
|
|
|
/// <summary>
|
|
/// 使用 Windows 风格路径。
|
|
/// </summary>
|
|
Windows
|
|
}
|
|
|
|
/// <summary>
|
|
/// 提供旧配置与新服务端之间的路径兼容策略。
|
|
/// </summary>
|
|
public static class PathCompatibility
|
|
{
|
|
/// <summary>
|
|
/// 按当前服务配置目录约定解析配置文件路径。
|
|
/// </summary>
|
|
/// <param name="configPath">调用方传入的原始配置路径。</param>
|
|
/// <param name="repoRoot">当前兼容搜索的仓库根目录。</param>
|
|
/// <returns>命中的绝对配置路径。</returns>
|
|
public static string ResolveConfigPath(string configPath, string repoRoot)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(configPath))
|
|
{
|
|
throw new ArgumentException("配置路径不能为空。", nameof(configPath));
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(repoRoot))
|
|
{
|
|
throw new ArgumentException("仓库根目录不能为空。", nameof(repoRoot));
|
|
}
|
|
|
|
var rawPath = configPath.Trim();
|
|
if (Path.IsPathRooted(rawPath))
|
|
{
|
|
return File.Exists(rawPath)
|
|
? Path.GetFullPath(rawPath)
|
|
: throw new FileNotFoundException($"未找到配置文件: {rawPath}", rawPath);
|
|
}
|
|
|
|
var normalizedRepoRoot = Path.GetFullPath(repoRoot);
|
|
var checkedPaths = new List<string>();
|
|
|
|
// 相对路径只允许落在当前服务根目录的 Config 下,避免隐式回退到父工作区旧文件。
|
|
foreach (var candidate in BuildConfigCandidates(normalizedRepoRoot, rawPath))
|
|
{
|
|
var fullCandidate = Path.GetFullPath(candidate);
|
|
if (checkedPaths.Contains(fullCandidate, StringComparer.OrdinalIgnoreCase))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
checkedPaths.Add(fullCandidate);
|
|
if (File.Exists(fullCandidate))
|
|
{
|
|
return fullCandidate;
|
|
}
|
|
}
|
|
|
|
throw new FileNotFoundException(
|
|
$"未找到配置文件 '{configPath}'。已检查: {string.Join(", ", checkedPaths)}",
|
|
configPath);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 构造当前平台约定的用户数据根目录。
|
|
/// </summary>
|
|
/// <param name="homeDirectory">用户主目录。</param>
|
|
/// <param name="pathStyle">目标平台风格。</param>
|
|
/// <returns>兼容旧系统的用户数据目录。</returns>
|
|
public static string BuildUserDataRoot(string homeDirectory, CompatibilityPathStyle pathStyle)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(homeDirectory))
|
|
{
|
|
throw new ArgumentException("用户目录不能为空。", nameof(homeDirectory));
|
|
}
|
|
|
|
return pathStyle switch
|
|
{
|
|
CompatibilityPathStyle.Posix => JoinPosix(homeDirectory, ".Rvbust", "Data"),
|
|
CompatibilityPathStyle.Windows => JoinWindows(homeDirectory, ".Rvbust", "Data"),
|
|
_ => throw new ArgumentOutOfRangeException(nameof(pathStyle), pathStyle, "不支持的路径风格。")
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 枚举当前服务配置目录下允许的配置候选路径。
|
|
/// </summary>
|
|
private static IEnumerable<string> BuildConfigCandidates(string repoRoot, string rawPath)
|
|
{
|
|
yield return Path.Combine(repoRoot, "Config", rawPath);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 使用 Posix 风格拼接路径,便于在 Linux 下验证固定输出。
|
|
/// </summary>
|
|
private static string JoinPosix(string root, params string[] segments)
|
|
{
|
|
var trimmedRoot = root.TrimEnd('/');
|
|
return string.Join("/", new[] { trimmedRoot }.Concat(segments));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 使用 Windows 风格拼接路径,避免在 Linux 上测试时被当前平台分隔符污染。
|
|
/// </summary>
|
|
private static string JoinWindows(string root, params string[] segments)
|
|
{
|
|
var normalizedRoot = root.TrimEnd('\\', '/').Replace('/', '\\');
|
|
return string.Join("\\", new[] { normalizedRoot }.Concat(segments));
|
|
}
|
|
}
|