forked from gongxuegit/tiku-backend.net
feat: add practice session workflow endpoints
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json;
|
||||
using Tiku.Application.Learning;
|
||||
using Tiku.Domain.Common;
|
||||
|
||||
namespace Tiku.Api.Contracts;
|
||||
|
||||
@@ -19,6 +21,89 @@ public sealed class LearningLimitQueryDto
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PracticeSessionQueryDto
|
||||
{
|
||||
public Guid? PracticeSessionId { get; set; }
|
||||
|
||||
public Guid? BlueprintId { get; set; }
|
||||
|
||||
[StringLength(50)]
|
||||
public string? Mode { get; set; }
|
||||
|
||||
[StringLength(50)]
|
||||
public string? Status { get; set; }
|
||||
|
||||
[Range(1, 200)]
|
||||
public int? Limit { get; set; }
|
||||
|
||||
public PracticeSessionFilter ToFilter(Guid? routePracticeSessionId = null)
|
||||
{
|
||||
return new PracticeSessionFilter(
|
||||
routePracticeSessionId ?? PracticeSessionId,
|
||||
BlueprintId,
|
||||
Mode,
|
||||
Status,
|
||||
Limit);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CreatePracticeSessionDto
|
||||
{
|
||||
[StringLength(50)]
|
||||
public string? Mode { get; set; }
|
||||
|
||||
[StringLength(50)]
|
||||
public string? TargetType { get; set; }
|
||||
|
||||
public Guid? TargetId { get; set; }
|
||||
|
||||
public Guid? BlueprintId { get; set; }
|
||||
|
||||
public Guid? CollectionId { get; set; }
|
||||
|
||||
public Guid? EntryId { get; set; }
|
||||
|
||||
public Guid? ContentNodeId { get; set; }
|
||||
|
||||
[Range(1, 500)]
|
||||
public int? QuestionLimit { get; set; }
|
||||
|
||||
[Range(1, 1440)]
|
||||
public int? DurationMinutes { get; set; }
|
||||
|
||||
[Range(typeof(decimal), "0", "99999")]
|
||||
public decimal? TotalScore { get; set; }
|
||||
|
||||
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
|
||||
|
||||
public PracticeSessionCommand ToCommand()
|
||||
{
|
||||
return new PracticeSessionCommand(
|
||||
Mode,
|
||||
TargetType,
|
||||
TargetId,
|
||||
BlueprintId,
|
||||
CollectionId,
|
||||
EntryId,
|
||||
ContentNodeId,
|
||||
QuestionLimit,
|
||||
DurationMinutes,
|
||||
TotalScore,
|
||||
Metadata);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SubmitPracticeSessionDto
|
||||
{
|
||||
[Required]
|
||||
public Guid PracticeSessionId { get; set; }
|
||||
|
||||
public PracticeSessionFilter ToFilter()
|
||||
{
|
||||
return new PracticeSessionFilter(PracticeSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SubmitAnswerDto
|
||||
{
|
||||
[Required]
|
||||
|
||||
@@ -15,6 +15,88 @@ public sealed class LearningController(
|
||||
ICurrentUser currentUser,
|
||||
ICurrentTenant currentTenant) : ControllerBase
|
||||
{
|
||||
[HttpPost("practice-sessions")]
|
||||
[EndpointSummary("创建练习会话")]
|
||||
[ProducesResponseType<PracticeSessionItem>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<ProblemDetails>(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<PracticeSessionItem>> CreatePracticeSession(
|
||||
CreatePracticeSessionDto request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await learningActivityService.CreatePracticeSessionAsync(
|
||||
ResolveActor(),
|
||||
request.ToCommand(),
|
||||
cancellationToken));
|
||||
}
|
||||
|
||||
[HttpGet("practice-sessions/detail")]
|
||||
[EndpointSummary("获取练习会话详情")]
|
||||
[ProducesResponseType<PracticeSessionDetailItem>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<PracticeSessionDetailItem>> GetPracticeSessionDetail(
|
||||
[FromQuery] PracticeSessionQueryDto query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await learningActivityService.GetPracticeSessionDetailAsync(
|
||||
ResolveActor(),
|
||||
query.ToFilter(),
|
||||
cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPost("practice-sessions/submit")]
|
||||
[EndpointSummary("提交练习会话并生成报告")]
|
||||
[ProducesResponseType<PracticeSessionReportItem>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<PracticeSessionReportItem>> SubmitPracticeSession(
|
||||
SubmitPracticeSessionDto request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await learningActivityService.SubmitPracticeSessionAsync(
|
||||
ResolveActor(),
|
||||
request.ToFilter(),
|
||||
cancellationToken));
|
||||
}
|
||||
|
||||
[HttpGet("practice-sessions/report")]
|
||||
[EndpointSummary("获取练习会话报告")]
|
||||
[ProducesResponseType<PracticeSessionReportItem>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<PracticeSessionReportItem>> GetPracticeSessionReport(
|
||||
[FromQuery] PracticeSessionQueryDto query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await learningActivityService.GetPracticeSessionReportAsync(
|
||||
ResolveActor(),
|
||||
query.ToFilter(),
|
||||
cancellationToken));
|
||||
}
|
||||
|
||||
[HttpGet("practice-reports")]
|
||||
[EndpointSummary("查询练习报告列表")]
|
||||
[ProducesResponseType<LearningList<PracticeSessionReportItem>>(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<LearningList<PracticeSessionReportItem>>> GetPracticeReports(
|
||||
[FromQuery] PracticeSessionQueryDto query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await learningActivityService.GetPracticeReportsAsync(
|
||||
ResolveActor(),
|
||||
query.ToFilter(),
|
||||
cancellationToken));
|
||||
}
|
||||
|
||||
[HttpGet("practice-sessions/history")]
|
||||
[EndpointSummary("查询练习历史")]
|
||||
[ProducesResponseType<LearningList<PracticeHistoryItem>>(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<LearningList<PracticeHistoryItem>>> GetPracticeHistory(
|
||||
[FromQuery] PracticeSessionQueryDto query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await learningActivityService.GetPracticeHistoryAsync(
|
||||
ResolveActor(),
|
||||
query.ToFilter(),
|
||||
cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPost("answers")]
|
||||
[EndpointSummary("提交题目答案")]
|
||||
[ProducesResponseType<AnswerRecordItem>(StatusCodes.Status200OK)]
|
||||
|
||||
@@ -93,7 +93,7 @@ public sealed class ExceptionHandlingMiddleware(
|
||||
await WriteProblemAsync(
|
||||
context,
|
||||
learningValidationException.Message,
|
||||
StatusCodes.Status400BadRequest,
|
||||
LearningValidationStatusCode(learningValidationException.Code),
|
||||
learningValidationException.Code);
|
||||
return;
|
||||
}
|
||||
@@ -200,4 +200,13 @@ public sealed class ExceptionHandlingMiddleware(
|
||||
_ => StatusCodes.Status400BadRequest
|
||||
};
|
||||
}
|
||||
|
||||
private static int LearningValidationStatusCode(string code)
|
||||
{
|
||||
return code switch
|
||||
{
|
||||
"no_practice_questions" or "practice_session_empty" => StatusCodes.Status409Conflict,
|
||||
_ => StatusCodes.Status400BadRequest
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user