using YD_Event.Application.Event.Dtos;
namespace YD_Event.Application.Scoring;
///
/// 计分规则里裁判席位配置(JudgeRoleSchemaJson)的解析工具。
/// 裁判分配与裁判端评分都要按同一份配置判断裁判类型与执裁资格,因此集中在这里,避免两处规则走偏。
///
public static class JudgeRoleSchemaService
{
///
/// 主裁判
///
public const string RoleChief = "T";
///
/// 表现裁判
///
public const string RolePerformance = "P";
///
/// 创意编排裁判
///
public const string RoleChoreography = "CJ";
///
/// 完成质量裁判
///
public const string RoleCompletionQuality = "CQ";
///
/// 解析计分规则的裁判席位配置。
/// 配置中的每个 slot 代表一个裁判类型(DP、DM、DR、P、T 等),可分配多名裁判;
/// 主裁固定 1 人,其余类型分配时不限人数(成绩按平均值计算),配置里显式给出 min/max 时以配置为准。
///
public static List ParseSections(string? schemaJson)
{
var sections = new List();
if (string.IsNullOrWhiteSpace(schemaJson)) return sections;
JArray array;
// 规则配置由人工维护,格式不合法时按“未配置裁判类型”处理,由调用方给出提示。
try { array = JArray.Parse(schemaJson); } catch (JsonException) { return sections; }
var seen = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var node in array)
{
var section = new JudgeRoleSectionOutput { Title = node["title"]?.ToString() ?? node["name"]?.ToString() ?? "裁判" };
foreach (var slotNode in node["slots"] as JArray ?? new JArray())
{
var code = slotNode["code"]?.ToString();
if (string.IsNullOrWhiteSpace(code)) continue;
var roleType = NormalizeRoleType(code);
// 同一类型在配置里重复出现(如历史 DP1/DP2 席位)时只保留一条,人数改由分配时决定。
if (!seen.Add(roleType)) continue;
var categories = (slotNode["categories"] as JArray)?
.Select(c => c.ToString())
.Where(s => !string.IsNullOrWhiteSpace(s))
.ToList();
if (categories == null || categories.Count == 0)
{
categories = roleType switch
{
"DGA" => new List { "DP+DM (个人绳)", "DA (车轮跳)", "DT (交互绳)", "DT (旅行跳)" },
"DGB" => new List { "DR (个人绳)", "DB (车轮跳)", "DJ (交互绳)", "DL (旅行跳)" },
_ => new List()
};
}
section.Slots.Add(new JudgeRoleSlotOutput
{
Code = roleType,
Name = slotNode["name"]?.ToString() ?? RoleName(roleType),
// 未配置 min 时至少要有 1 人;未配置 max 时只有主裁限 1 人,其余类型不限。
MinCount = Math.Max(ReadCount(slotNode["min"]) ?? 1, 0),
MaxCount = ReadCount(slotNode["max"]) ?? (roleType == RoleChief ? (int?)1 : null),
RoleFlag = RoleFlagOf(roleType),
Categories = categories
});
}
if (section.Slots.Count > 0) sections.Add(section);
}
return sections;
}
///
/// 读取席位人数配置,非数字等非法值按未配置处理
///
private static int? ReadCount(JToken? node)
{
try { return node?.Value(); }
catch (Exception ex) when (ex is FormatException or InvalidCastException or OverflowException) { return null; }
}
///
/// 取配置里的所有裁判类型(按配置顺序去重)
///
public static List ParseSlots(string? schemaJson) =>
ParseSections(schemaJson).SelectMany(x => x.Slots).ToList();
///
/// 把席位编码归一为裁判类型编码,兼容 DP1、CJ2 这类带席位序号的编码
///
public static string NormalizeRoleType(string? code)
{
var trimmed = (code ?? string.Empty).Trim().ToUpperInvariant();
var letters = new string(trimmed.TakeWhile(ch => !char.IsDigit(ch)).ToArray());
return letters.Length > 0 ? letters : trimmed;
}
///
/// 裁判类型编码对应的执裁资格位,未单独列出的类型都按难度裁判处理
///
public static int RoleFlagOf(string roleType) => roleType switch
{
RoleChief => (int)RefereeRole.Chief,
RolePerformance => (int)RefereeRole.Performance,
RoleChoreography => (int)RefereeRole.Choreography,
RoleCompletionQuality => (int)RefereeRole.CompletionQuality,
_ => (int)RefereeRole.Difficulty
};
///
/// 裁判类型的默认名称,规则配置里没写 name 时用于展示
///
public static string RoleName(string roleType) => roleType switch
{
RoleChief => "主裁判",
RolePerformance => "表现裁判",
RoleChoreography => "创意编排裁判",
RoleCompletionQuality => "完成质量裁判",
_ => "难度裁判"
};
///
/// 取裁判类型在当前计分规则里的名称,规则里没配这个类型时回退到默认名称
///
public static string RoleNameOf(List? slots, string? judgeRoleType)
{
var roleType = NormalizeRoleType(judgeRoleType);
var matched = slots?.FirstOrDefault(x => string.Equals(x.Code, roleType, StringComparison.OrdinalIgnoreCase));
return matched?.Name ?? RoleName(roleType);
}
}