143 lines
6.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}