Defense: sync admin/student timers from server every 10s; toasts with ready reason; live eye counter

This commit is contained in:
2026-10-02 01:21:32 +03:00
parent fba4e013e5
commit b94538cb7e
13 changed files with 371 additions and 45 deletions

View File

@@ -68,6 +68,28 @@ class DefenseAdminController(
)
}
/** Синхронизация таймеров статусов по списку лабораторных: `labs=id1,id2,...` */
@GetMapping("/watch")
fun watch(
@RequestParam labs: String,
): List<DefenseAdminDto> {
val ids = labs.split(',').mapNotNull { runCatching { UUID.fromString(it.trim()) }.getOrNull() }
return service.listForLabs(ids).map {
DefenseAdminDto(
it.id,
it.studentId,
it.labId,
it.status,
it.code,
it.assignmentMd,
it.startedAt,
it.readyAt,
it.readyReason,
it.secondsLeft,
)
}
}
@PostMapping("/{id}/issue-code")
fun issueCode(
@PathVariable id: UUID,

View File

@@ -30,6 +30,17 @@ class DefenseMyController(
@AuthenticationPrincipal jwt: Jwt,
): DefenseDto? = service.mySession(UUID.fromString(jwt.subject), labId)?.let { hideForStudent(it) }
/** Компактный статус для синхронизации таймера раз в ~10 с. */
@GetMapping("/{labId}/status")
fun status(
@PathVariable labId: UUID,
@AuthenticationPrincipal jwt: Jwt,
): Map<String, Any?> {
val d = service.mySession(UUID.fromString(jwt.subject), labId)
if (d == null) return mapOf("status" to "none", "secondsLeft" to null, "readyReason" to null)
return mapOf("status" to d.status, "secondsLeft" to d.secondsLeft, "readyReason" to d.readyReason)
}
/** Студент не должен видеть ни код, ни текст задания до ввода кода. */
private fun hideForStudent(d: DefenseDto): DefenseDto =
if (d.status == DefenseStatus.waiting.name) d.copy(code = null, assignmentMd = "") else d

View File

@@ -108,6 +108,14 @@ class DefenseService(
return sessions.findByLabIdOrderByStudentIdAsc(labId).map { toDto(it) }
}
/** Наблюдение за сессиями сразу по нескольким лабораторным (раз в ~10 с со страницы группы). */
@Transactional
fun listForLabs(labIds: Collection<UUID>): List<DefenseDto> {
if (labIds.isEmpty()) return emptyList()
syncTimeouts()
return sessions.findByLabIdIn(labIds).map { toDto(it) }
}
@Transactional
fun startByCode(
studentId: UUID,

View File

@@ -7,6 +7,8 @@ import java.util.UUID
interface DefenseSessionRepository : JpaRepository<DefenseSession, UUID> {
fun findByLabIdOrderByStudentIdAsc(labId: UUID): List<DefenseSession>
fun findByLabIdIn(labIds: Collection<UUID>): List<DefenseSession>
fun findByStudentIdAndLabId(
studentId: UUID,
labId: UUID,