143 lines
6.0 KiB
C#
143 lines
6.0 KiB
C#
using YD_Event.Application.Event.Dtos;
|
||
|
||
namespace YD_Event.Application.Scoring;
|
||
|
||
/// <summary>
|
||
/// 计分规则里裁判席位配置(JudgeRoleSchemaJson)的解析工具。
|
||
/// 裁判分配与裁判端评分都要按同一份配置判断裁判类型与执裁资格,因此集中在这里,避免两处规则走偏。
|
||
/// </summary>
|
||
public static class JudgeRoleSchemaService
|
||
{
|
||
/// <summary>
|
||
/// 主裁判
|
||
/// </summary>
|
||
public const string RoleChief = "T";
|
||
|
||
/// <summary>
|
||
/// 表现裁判
|
||
/// </summary>
|
||
public const string RolePerformance = "P";
|
||
|
||
/// <summary>
|
||
/// 创意编排裁判
|
||
/// </summary>
|
||
public const string RoleChoreography = "CJ";
|
||
|
||
/// <summary>
|
||
/// 完成质量裁判
|
||
/// </summary>
|
||
public const string RoleCompletionQuality = "CQ";
|
||
|
||
/// <summary>
|
||
/// 解析计分规则的裁判席位配置。
|
||
/// 配置中的每个 slot 代表一个裁判类型(DP、DM、DR、P、T 等),可分配多名裁判;
|
||
/// 主裁固定 1 人,其余类型分配时不限人数(成绩按平均值计算),配置里显式给出 min/max 时以配置为准。
|
||
/// </summary>
|
||
public static List<JudgeRoleSectionOutput> ParseSections(string? schemaJson)
|
||
{
|
||
var sections = new List<JudgeRoleSectionOutput>();
|
||
if (string.IsNullOrWhiteSpace(schemaJson)) return sections;
|
||
JArray array;
|
||
// 规则配置由人工维护,格式不合法时按“未配置裁判类型”处理,由调用方给出提示。
|
||
try { array = JArray.Parse(schemaJson); } catch (JsonException) { return sections; }
|
||
var seen = new HashSet<string>(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<string> { "DP+DM (个人绳)", "DA (车轮跳)", "DT (交互绳)", "DT (旅行跳)" },
|
||
"DGB" => new List<string> { "DR (个人绳)", "DB (车轮跳)", "DJ (交互绳)", "DL (旅行跳)" },
|
||
_ => new List<string>()
|
||
};
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取席位人数配置,非数字等非法值按未配置处理
|
||
/// </summary>
|
||
private static int? ReadCount(JToken? node)
|
||
{
|
||
try { return node?.Value<int?>(); }
|
||
catch (Exception ex) when (ex is FormatException or InvalidCastException or OverflowException) { return null; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取配置里的所有裁判类型(按配置顺序去重)
|
||
/// </summary>
|
||
public static List<JudgeRoleSlotOutput> ParseSlots(string? schemaJson) =>
|
||
ParseSections(schemaJson).SelectMany(x => x.Slots).ToList();
|
||
|
||
/// <summary>
|
||
/// 把席位编码归一为裁判类型编码,兼容 DP1、CJ2 这类带席位序号的编码
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 裁判类型编码对应的执裁资格位,未单独列出的类型都按难度裁判处理
|
||
/// </summary>
|
||
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
|
||
};
|
||
|
||
/// <summary>
|
||
/// 裁判类型的默认名称,规则配置里没写 name 时用于展示
|
||
/// </summary>
|
||
public static string RoleName(string roleType) => roleType switch
|
||
{
|
||
RoleChief => "主裁判",
|
||
RolePerformance => "表现裁判",
|
||
RoleChoreography => "创意编排裁判",
|
||
RoleCompletionQuality => "完成质量裁判",
|
||
_ => "难度裁判"
|
||
};
|
||
|
||
/// <summary>
|
||
/// 取裁判类型在当前计分规则里的名称,规则里没配这个类型时回退到默认名称
|
||
/// </summary>
|
||
public static string RoleNameOf(List<JudgeRoleSlotOutput>? slots, string? judgeRoleType)
|
||
{
|
||
var roleType = NormalizeRoleType(judgeRoleType);
|
||
var matched = slots?.FirstOrDefault(x => string.Equals(x.Code, roleType, StringComparison.OrdinalIgnoreCase));
|
||
return matched?.Name ?? RoleName(roleType);
|
||
}
|
||
}
|