Refactor: extract helpers to satisfy detekt (throws, returns, loops)
This commit is contained in:
@@ -29,18 +29,8 @@ class AssetStorage(
|
||||
): StoredAsset {
|
||||
val ext = originalName.substringAfterLast('.', "").lowercase()
|
||||
val isVideo = ext in VIDEO_EXTENSIONS
|
||||
val isImage = ext in IMAGE_EXTENSIONS
|
||||
if (!isVideo && !isImage) {
|
||||
throw BadRequestException(
|
||||
"Недопустимый тип файла: $originalName " +
|
||||
"(изображения: ${IMAGE_EXTENSIONS.joinToString(", ")}; " +
|
||||
"видео: ${VIDEO_EXTENSIONS.joinToString(", ")})",
|
||||
)
|
||||
}
|
||||
val limit = if (isVideo) MAX_VIDEO_BYTES else MAX_IMAGE_BYTES
|
||||
if (bytes.size > limit) {
|
||||
throw BadRequestException("Файл $originalName слишком большой (> ${limit / 1024 / 1024} МБ)")
|
||||
}
|
||||
requireAllowedType(originalName, isVideo, isImage = ext in IMAGE_EXTENSIONS)
|
||||
requireSizeWithinLimit(originalName, bytes.size, isVideo)
|
||||
val publicName = "${UUID.randomUUID()}.$ext"
|
||||
val target = rootDir.resolve(publicName).normalize()
|
||||
if (!target.startsWith(rootDir)) {
|
||||
@@ -50,6 +40,31 @@ class AssetStorage(
|
||||
return StoredAsset(publicName, target, mimeFor(ext))
|
||||
}
|
||||
|
||||
private fun requireAllowedType(
|
||||
originalName: String,
|
||||
isVideo: Boolean,
|
||||
isImage: Boolean,
|
||||
) {
|
||||
if (!isVideo && !isImage) {
|
||||
throw BadRequestException(
|
||||
"Недопустимый тип файла: $originalName " +
|
||||
"(изображения: ${IMAGE_EXTENSIONS.joinToString(", ")}; " +
|
||||
"видео: ${VIDEO_EXTENSIONS.joinToString(", ")})",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun requireSizeWithinLimit(
|
||||
originalName: String,
|
||||
size: Int,
|
||||
isVideo: Boolean,
|
||||
) {
|
||||
val limit = if (isVideo) MAX_VIDEO_BYTES else MAX_IMAGE_BYTES
|
||||
if (size > limit) {
|
||||
throw BadRequestException("Файл $originalName слишком большой (> ${limit / 1024 / 1024} МБ)")
|
||||
}
|
||||
}
|
||||
|
||||
fun read(publicName: String): Pair<Path, String>? {
|
||||
val clean = publicName.substringAfterLast('/')
|
||||
val target = rootDir.resolve(clean).normalize()
|
||||
|
||||
@@ -168,12 +168,12 @@ class ContentService(
|
||||
articleSlug: String,
|
||||
includeHidden: Boolean = false,
|
||||
): ContentArticleView {
|
||||
val ch = chapters.findBySlug(chapterSlug) ?: throw NotFoundException("Глава не найдена")
|
||||
if (!includeHidden && !ch.published) throw NotFoundException("Глава скрыта")
|
||||
val sec = sections.findByChapterIdAndSlug(ch.id, sectionSlug) ?: throw NotFoundException("Раздел не найден")
|
||||
if (!includeHidden && !sec.published) throw NotFoundException("Раздел скрыт")
|
||||
val a = articles.findBySectionIdAndSlug(sec.id, articleSlug) ?: throw NotFoundException("Статья не найдена")
|
||||
if (!includeHidden && !a.published) throw NotFoundException("Статья скрыта")
|
||||
val ch = requireFound(chapters.findBySlug(chapterSlug), "Глава не найдена")
|
||||
requireVisible(ch.published, includeHidden, "Глава скрыта")
|
||||
val sec = requireFound(sections.findByChapterIdAndSlug(ch.id, sectionSlug), "Раздел не найден")
|
||||
requireVisible(sec.published, includeHidden, "Раздел скрыт")
|
||||
val a = requireFound(articles.findBySectionIdAndSlug(sec.id, articleSlug), "Статья не найдена")
|
||||
requireVisible(a.published, includeHidden, "Статья скрыта")
|
||||
return ContentArticleView(
|
||||
id = a.id.toString(),
|
||||
chapterSlug = ch.slug,
|
||||
@@ -258,6 +258,19 @@ class ContentService(
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> requireFound(
|
||||
value: T?,
|
||||
message: String,
|
||||
): T = value ?: throw NotFoundException(message)
|
||||
|
||||
private fun requireVisible(
|
||||
published: Boolean,
|
||||
includeHidden: Boolean,
|
||||
message: String,
|
||||
) {
|
||||
if (!includeHidden && !published) throw NotFoundException(message)
|
||||
}
|
||||
|
||||
private fun uuidOrNull(value: String): UUID? = runCatching { UUID.fromString(value) }.getOrNull()
|
||||
|
||||
@Transactional
|
||||
|
||||
@@ -81,20 +81,7 @@ class LabService(
|
||||
|
||||
val levelByCode = levelEntities.associateBy { it.code }
|
||||
|
||||
bundle.variants.forEach { vmeta ->
|
||||
val level =
|
||||
levelByCode[vmeta.level]
|
||||
?: throw BadRequestException("Неизвестный уровень ${vmeta.level} у варианта ${vmeta.number}")
|
||||
variantsRepo.save(
|
||||
Variant(
|
||||
lab = lab,
|
||||
labLevel = level,
|
||||
number = vmeta.number,
|
||||
title = vmeta.title,
|
||||
statementMd = vmeta.statement,
|
||||
),
|
||||
)
|
||||
}
|
||||
saveVariants(lab, levelByCode, bundle.variants)
|
||||
|
||||
return LabImportResult(
|
||||
labId = lab.id.toString(),
|
||||
@@ -248,10 +235,54 @@ class LabService(
|
||||
return detail(labId)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun create(request: LabCreateRequest): LabDetail {
|
||||
private fun validateCreate(request: LabCreateRequest) {
|
||||
if (request.title.isNullOrBlank()) throw BadRequestException("Название не может быть пустым")
|
||||
if ((request.number ?: 0) <= 0) throw BadRequestException("Номер лабораторной должен быть положительным")
|
||||
}
|
||||
|
||||
private fun saveLevels(
|
||||
lab: Lab,
|
||||
levels: List<LevelSaveRequest>,
|
||||
) {
|
||||
val levelMetas = levels.ifEmpty { listOf(LevelSaveRequest(code = "basic", title = "Основная группа")) }
|
||||
levelMetas.forEachIndexed { index, meta ->
|
||||
if (meta.code.isNullOrBlank()) throw BadRequestException("Код уровня не может быть пустым")
|
||||
labLevels.save(
|
||||
LabLevel(
|
||||
lab = lab,
|
||||
code = meta.code.trim(),
|
||||
title = meta.title.orEmpty().ifBlank { meta.code },
|
||||
description = meta.description.orEmpty(),
|
||||
position = meta.position?.takeIf { it > 0 } ?: index + 1,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveVariants(
|
||||
lab: Lab,
|
||||
levelByCode: Map<String, LabLevel>,
|
||||
variantMetas: List<VariantMeta>,
|
||||
) {
|
||||
variantMetas.forEach { vmeta ->
|
||||
val level =
|
||||
levelByCode[vmeta.level]
|
||||
?: throw BadRequestException("Неизвестный уровень ${vmeta.level} у варианта ${vmeta.number}")
|
||||
variantsRepo.save(
|
||||
Variant(
|
||||
lab = lab,
|
||||
labLevel = level,
|
||||
number = vmeta.number,
|
||||
title = vmeta.title,
|
||||
statementMd = vmeta.statement,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun create(request: LabCreateRequest): LabDetail {
|
||||
validateCreate(request)
|
||||
val semester = findOrCreateSemester(request.semesterTitle.orEmpty(), request.semesterYear ?: 0)
|
||||
if (labs.existsByNumberAndSemesterId(request.number ?: 0, semester.id)) {
|
||||
throw ConflictException("Лабораторная №${request.number} уже существует в этом семестре")
|
||||
@@ -269,22 +300,7 @@ class LabService(
|
||||
status = request.status ?: "draft",
|
||||
),
|
||||
)
|
||||
val levelMetas =
|
||||
request.levels.ifEmpty {
|
||||
listOf(LevelSaveRequest(code = "basic", title = "Основная группа"))
|
||||
}
|
||||
levelMetas.forEachIndexed { index, meta ->
|
||||
if (meta.code.isNullOrBlank()) throw BadRequestException("Код уровня не может быть пустым")
|
||||
labLevels.save(
|
||||
LabLevel(
|
||||
lab = lab,
|
||||
code = meta.code.trim(),
|
||||
title = meta.title.orEmpty().ifBlank { meta.code },
|
||||
description = meta.description.orEmpty(),
|
||||
position = meta.position?.takeIf { it > 0 } ?: index + 1,
|
||||
),
|
||||
)
|
||||
}
|
||||
saveLevels(lab, request.levels)
|
||||
return detail(lab.id)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user