2026-06-10 11:16:46 +08:00
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using YD_TrainingCompetition_Api.Competition;
|
|
|
|
|
|
|
|
|
|
namespace YD_TrainingCompetition_Api.Controllers;
|
|
|
|
|
|
|
|
|
|
[ApiController]
|
|
|
|
|
[Route("api/[controller]")]
|
2026-06-10 15:54:30 +08:00
|
|
|
// CompetitionController 是前端比赛页使用的主控制器,
|
|
|
|
|
// 负责暴露“建会话、切场次、下载本场、开始/停止/清零、手工改分”等接口。
|
2026-06-10 11:16:46 +08:00
|
|
|
public sealed class CompetitionController : ControllerBase
|
|
|
|
|
{
|
|
|
|
|
private readonly ICompetitionSessionService _competition;
|
|
|
|
|
|
|
|
|
|
public CompetitionController(ICompetitionSessionService competition)
|
|
|
|
|
{
|
|
|
|
|
_competition = competition;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 15:54:30 +08:00
|
|
|
// 获取当前比赛会话快照,页面初始化和重连后都会读取它。
|
2026-06-10 11:16:46 +08:00
|
|
|
[HttpGet("session")]
|
|
|
|
|
public IActionResult GetSession() => Ok(_competition.GetState());
|
|
|
|
|
|
2026-06-10 15:54:30 +08:00
|
|
|
// 根据配置页提交的秩序册、场次和人员列表创建新的比赛会话。
|
2026-06-10 11:16:46 +08:00
|
|
|
[HttpPost("session")]
|
|
|
|
|
public async Task<IActionResult> CreateSession([FromBody] CreateCompetitionSessionRequest request, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Ok(await _competition.CreateAsync(request, cancellationToken));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest(new { message = ex.Message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost("rounds/{roundIndex:int}/select")]
|
|
|
|
|
public async Task<IActionResult> SelectRound(int roundIndex, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Ok(await _competition.SelectRoundAsync(roundIndex, cancellationToken));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest(new { message = ex.Message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost("rounds/next")]
|
|
|
|
|
public async Task<IActionResult> NextRound(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Ok(await _competition.SelectNextRoundAsync(cancellationToken));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest(new { message = ex.Message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost("rounds/previous")]
|
|
|
|
|
public async Task<IActionResult> PreviousRound(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Ok(await _competition.SelectPreviousRoundAsync(cancellationToken));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest(new { message = ex.Message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost("rounds/current/download")]
|
|
|
|
|
public async Task<IActionResult> DownloadCurrentRound(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Ok(await _competition.DownloadCurrentRoundAsync(cancellationToken));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest(new { message = ex.Message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 15:54:30 +08:00
|
|
|
// 下发开始命令,让已下载本场的终端进入正式测试。
|
2026-06-10 11:16:46 +08:00
|
|
|
[HttpPost("test/start")]
|
|
|
|
|
public async Task<IActionResult> Start(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Ok(await _competition.StartAsync(cancellationToken));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest(new { message = ex.Message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 15:54:30 +08:00
|
|
|
// 下发停止命令,但保留终端当前成绩。
|
2026-06-10 11:16:46 +08:00
|
|
|
[HttpPost("test/stop")]
|
|
|
|
|
public async Task<IActionResult> Stop(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Ok(await _competition.StopAsync(cancellationToken));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest(new { message = ex.Message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 15:54:30 +08:00
|
|
|
// 下发清零命令,并把当前场次的本地成绩也一起清空。
|
2026-06-10 11:16:46 +08:00
|
|
|
[HttpPost("test/reset")]
|
|
|
|
|
public async Task<IActionResult> Reset(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Ok(await _competition.ResetCurrentRoundAsync(cancellationToken));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest(new { message = ex.Message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 15:54:30 +08:00
|
|
|
// 手工指定某名选手成绩,主要用于裁判修正。
|
2026-06-10 11:16:46 +08:00
|
|
|
[HttpPost("participants/{participantId:int}/count")]
|
|
|
|
|
public async Task<IActionResult> SetCount(int participantId, [FromBody] CompetitionParticipantCountRequest request, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Ok(await _competition.SetParticipantCountAsync(participantId, request.Count, cancellationToken));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest(new { message = ex.Message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 15:54:30 +08:00
|
|
|
// 在现有成绩基础上增减,适合前端 +/- 按钮。
|
2026-06-10 11:16:46 +08:00
|
|
|
[HttpPost("participants/{participantId:int}/delta")]
|
|
|
|
|
public async Task<IActionResult> ChangeCount(int participantId, [FromBody] CompetitionParticipantDeltaRequest request, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Ok(await _competition.ChangeParticipantCountAsync(participantId, request.Delta, cancellationToken));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest(new { message = ex.Message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|