Add audit logging for Zeitplan updates, implement conflict validation for overlapping schedules and judge assignments, and enhance frontend with detailed warning visualizations in Zeitplan tab.

This commit is contained in:
2026-04-11 21:00:14 +02:00
parent bc46054412
commit 3515d40fcb
11 changed files with 176 additions and 9 deletions
@@ -6,7 +6,9 @@ import at.mocode.entries.domain.model.RichterEinsatz
import at.mocode.entries.domain.repository.NennungRepository
import at.mocode.entries.domain.service.CompetitionWarningService
import at.mocode.entries.service.errors.LockedException
import at.mocode.entries.service.persistence.AuditLogTable
import at.mocode.entries.service.persistence.TurnierTable
import org.jetbrains.exposed.v1.jdbc.insert
import at.mocode.entries.service.tenant.tenantTransaction
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.selectAll
@@ -162,6 +164,18 @@ class BewerbService(
beginnZeit = req.beginnZeit,
austragungsplatzId = req.austragungsplatzId?.let { Uuid.parse(it) }
)
tenantTransaction {
// Audit-Log schreiben
AuditLogTable.insert {
it[entityType] = "BEWERB"
it[entityId] = id.toJavaUuid()
it[action] = "UPDATE_ZEITPLAN"
it[timestamp] = kotlin.time.Clock.System.now()
it[changesJson] = "{\"old\": {\"datum\": \"${current.geplantesDatum}\", \"zeit\": \"${current.beginnZeit}\", \"platz\": \"${current.austragungsplatzId}\"}, \"new\": {\"datum\": \"${updated.geplantesDatum}\", \"zeit\": \"${updated.beginnZeit}\", \"platz\": \"${updated.austragungsplatzId}\"}}"
}
}
return repo.update(updated)
}
@@ -261,7 +261,9 @@ class BewerbeController(
@PatchMapping("/bewerbe/{id}/zeitplan")
suspend fun updateZeitplan(@PathVariable id: String, @RequestBody body: UpdateZeitplanRequest): BewerbResponse {
val b = service.updateZeitplan(Uuid.parse(id), body)
return domainToDto(b)
val uuid = Uuid.parse(id)
val b = service.updateZeitplan(uuid, body)
val warnungen = service.validateBewerb(uuid)
return domainToDto(b, warnungen)
}
}
@@ -0,0 +1,23 @@
package at.mocode.entries.service.persistence
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.core.java.javaUUID
import org.jetbrains.exposed.v1.datetime.timestamp
import kotlin.time.Instant
object AuditLogTable : Table("audit_log") {
val id = javaUUID("id").autoGenerate()
val entityType = varchar("entity_type", 50)
val entityId = javaUUID("entity_id")
val action = varchar("action", 50)
val userId = javaUUID("user_id").nullable()
val timestamp = timestamp("timestamp")
val changesJson = text("changes_json").nullable()
override val primaryKey = PrimaryKey(id)
init {
index(false, entityId)
index(false, timestamp)
}
}
@@ -39,8 +39,10 @@ class BewerbeZeitplanIntegrationTest {
TurnierTable,
BewerbTable,
AbteilungTable,
BewerbRichterEinsatzTable
BewerbRichterEinsatzTable,
AuditLogTable
)
AuditLogTable.deleteAll()
BewerbRichterEinsatzTable.deleteAll()
BewerbTable.deleteAll()
AbteilungTable.deleteAll()