Refactor: bind admin forms via @ModelAttribute DTOs; detekt fully default
This commit is contained in:
@@ -9,21 +9,16 @@ config:
|
|||||||
warningsAsErrors: false
|
warningsAsErrors: false
|
||||||
|
|
||||||
complexity:
|
complexity:
|
||||||
# Все правила сложности — на значениях по умолчанию: крупные агрегаты раздроблены
|
# Все правила сложности — строго на значениях detekt по умолчанию. Крупные агрегаты
|
||||||
# (web ApiClient — по доменам, AdminController — AdminSupport + 5 контроллеров,
|
# раздроблены: ApiClient — по доменам, AdminController — AdminSupport + 5 контроллеров,
|
||||||
# импорт — ContentImporter, авторинг/порядок и оценки — отдельные сервисы,
|
# импорт — ContentImporter, авторинг/порядок и оценки — отдельные сервисы, submissions —
|
||||||
# submissions — отдельный контроллер). Единственное ослабление — LongParameterList
|
# отдельный контроллер; MVC-формы связаны через @ModelAttribute-DTO.
|
||||||
# для MVC-форм (см. ниже): у обработчиков формы @RequestParam естественно много.
|
|
||||||
LongMethod:
|
LongMethod:
|
||||||
threshold: 60
|
threshold: 60
|
||||||
LongParameterList:
|
LongParameterList:
|
||||||
# MVC-обработчики форм принимают набор @RequestParam — параметров объективно много.
|
functionThreshold: 6
|
||||||
functionThreshold: 8
|
constructorThreshold: 7
|
||||||
constructorThreshold: 8
|
|
||||||
ignoreDefaultParameters: true
|
ignoreDefaultParameters: true
|
||||||
ignoreAnnotated:
|
|
||||||
- 'PostMapping'
|
|
||||||
- 'GetMapping'
|
|
||||||
TooManyFunctions:
|
TooManyFunctions:
|
||||||
thresholdInFiles: 20
|
thresholdInFiles: 20
|
||||||
thresholdInClasses: 20
|
thresholdInClasses: 20
|
||||||
|
|||||||
@@ -12,6 +12,15 @@ import ru.ysu.lms.common.BadRequestException
|
|||||||
import ru.ysu.lms.common.NotFoundException
|
import ru.ysu.lms.common.NotFoundException
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
data class ArticlePlacement(
|
||||||
|
val title: String,
|
||||||
|
val bodyMd: String,
|
||||||
|
val chapterId: UUID?,
|
||||||
|
val newChapterTitle: String?,
|
||||||
|
val sectionId: UUID?,
|
||||||
|
val newSectionTitle: String?,
|
||||||
|
)
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class ContentAuthoringService(
|
class ContentAuthoringService(
|
||||||
private val chapters: ContentChapterRepository,
|
private val chapters: ContentChapterRepository,
|
||||||
@@ -141,53 +150,46 @@ class ContentAuthoringService(
|
|||||||
|
|
||||||
/** Создаёт статью; если раздела нет — создаёт главу/раздел по названиям. */
|
/** Создаёт статью; если раздела нет — создаёт главу/раздел по названиям. */
|
||||||
@Transactional
|
@Transactional
|
||||||
fun createArticlePlaced(
|
fun createArticlePlaced(placement: ArticlePlacement): ContentArticleView {
|
||||||
title: String,
|
|
||||||
bodyMd: String,
|
|
||||||
chapterId: UUID?,
|
|
||||||
newChapterTitle: String?,
|
|
||||||
sectionId: UUID?,
|
|
||||||
newSectionTitle: String?,
|
|
||||||
): ContentArticleView {
|
|
||||||
val targetSection: ContentSection =
|
val targetSection: ContentSection =
|
||||||
when {
|
when {
|
||||||
sectionId != null && !newSectionTitle.isNullOrBlank() -> {
|
placement.sectionId != null && !placement.newSectionTitle.isNullOrBlank() -> {
|
||||||
val anchor =
|
val anchor =
|
||||||
sections
|
sections
|
||||||
.findById(sectionId)
|
.findById(placement.sectionId)
|
||||||
.orElseThrow { NotFoundException("Раздел не найден") }
|
.orElseThrow { NotFoundException("Раздел не найден") }
|
||||||
val node = createSection(anchor.chapterId, newSectionTitle, null)
|
val node = createSection(anchor.chapterId, placement.newSectionTitle, null)
|
||||||
sections
|
sections
|
||||||
.findById(UUID.fromString(node.id))
|
.findById(UUID.fromString(node.id))
|
||||||
.orElseThrow { NotFoundException("Раздел не найден") }
|
.orElseThrow { NotFoundException("Раздел не найден") }
|
||||||
}
|
}
|
||||||
sectionId != null ->
|
placement.sectionId != null ->
|
||||||
sections
|
sections
|
||||||
.findById(sectionId)
|
.findById(placement.sectionId)
|
||||||
.orElseThrow { NotFoundException("Раздел не найден") }
|
.orElseThrow { NotFoundException("Раздел не найден") }
|
||||||
!newSectionTitle.isNullOrBlank() -> {
|
!placement.newSectionTitle.isNullOrBlank() -> {
|
||||||
val chapter: ContentChapter =
|
val chapter: ContentChapter =
|
||||||
when {
|
when {
|
||||||
chapterId != null ->
|
placement.chapterId != null ->
|
||||||
chapters
|
chapters
|
||||||
.findById(chapterId)
|
.findById(placement.chapterId)
|
||||||
.orElseThrow { NotFoundException("Глава не найдена") }
|
.orElseThrow { NotFoundException("Глава не найдена") }
|
||||||
!newChapterTitle.isNullOrBlank() -> {
|
!placement.newChapterTitle.isNullOrBlank() -> {
|
||||||
val node = createChapter(newChapterTitle, null)
|
val node = createChapter(placement.newChapterTitle, null)
|
||||||
chapters
|
chapters
|
||||||
.findById(UUID.fromString(node.id))
|
.findById(UUID.fromString(node.id))
|
||||||
.orElseThrow { NotFoundException("Глава не найдена") }
|
.orElseThrow { NotFoundException("Глава не найдена") }
|
||||||
}
|
}
|
||||||
else -> throw BadRequestException("Укажите главу")
|
else -> throw BadRequestException("Укажите главу")
|
||||||
}
|
}
|
||||||
val node = createSection(chapter.id, newSectionTitle, null)
|
val node = createSection(chapter.id, placement.newSectionTitle, null)
|
||||||
sections
|
sections
|
||||||
.findById(UUID.fromString(node.id))
|
.findById(UUID.fromString(node.id))
|
||||||
.orElseThrow { NotFoundException("Раздел не найден") }
|
.orElseThrow { NotFoundException("Раздел не найден") }
|
||||||
}
|
}
|
||||||
else -> throw BadRequestException("Укажите раздел")
|
else -> throw BadRequestException("Укажите раздел")
|
||||||
}
|
}
|
||||||
return createArticle(targetSection.id, title, bodyMd)
|
return createArticle(targetSection.id, placement.title, placement.bodyMd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping
|
|||||||
import org.springframework.web.bind.annotation.RequestParam
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
import org.springframework.web.multipart.MultipartFile
|
import org.springframework.web.multipart.MultipartFile
|
||||||
|
import ru.ysu.lms.academic.content.ArticlePlacement
|
||||||
import ru.ysu.lms.academic.content.ContentAuthoringService
|
import ru.ysu.lms.academic.content.ContentAuthoringService
|
||||||
import ru.ysu.lms.academic.content.ContentImportResult
|
import ru.ysu.lms.academic.content.ContentImportResult
|
||||||
import ru.ysu.lms.academic.content.ContentService
|
import ru.ysu.lms.academic.content.ContentService
|
||||||
@@ -170,12 +171,14 @@ class ContentAdminController(
|
|||||||
fun createArticle(
|
fun createArticle(
|
||||||
@RequestBody body: ArticleCreatePayload,
|
@RequestBody body: ArticleCreatePayload,
|
||||||
) = authoring.createArticlePlaced(
|
) = authoring.createArticlePlaced(
|
||||||
title = body.title,
|
ArticlePlacement(
|
||||||
bodyMd = body.bodyMd,
|
title = body.title,
|
||||||
chapterId = body.chapterId?.let { UUID.fromString(it) },
|
bodyMd = body.bodyMd,
|
||||||
newChapterTitle = body.newChapterTitle,
|
chapterId = body.chapterId?.let { UUID.fromString(it) },
|
||||||
sectionId = body.sectionId?.let { UUID.fromString(it) },
|
newChapterTitle = body.newChapterTitle,
|
||||||
newSectionTitle = body.newSectionTitle,
|
sectionId = body.sectionId?.let { UUID.fromString(it) },
|
||||||
|
newSectionTitle = body.newSectionTitle,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@PutMapping("/articles/{id}")
|
@PutMapping("/articles/{id}")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ru.ysu.lms.web.admin
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpSession
|
import jakarta.servlet.http.HttpSession
|
||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
@@ -26,38 +27,34 @@ class AdminDefenseController(
|
|||||||
@PostMapping("/labs/{labId}/defense/assign")
|
@PostMapping("/labs/{labId}/defense/assign")
|
||||||
fun assignDefense(
|
fun assignDefense(
|
||||||
@PathVariable labId: String,
|
@PathVariable labId: String,
|
||||||
@RequestParam studentId: String,
|
@ModelAttribute form: DefenseForm,
|
||||||
@RequestParam assignmentMd: String,
|
|
||||||
@RequestParam(required = false) groupId: String?,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
redirect: RedirectAttributes,
|
redirect: RedirectAttributes,
|
||||||
): String =
|
): String =
|
||||||
try {
|
try {
|
||||||
api.assignDefense(token(session), studentId, labId, assignmentMd)
|
api.assignDefense(token(session), form.studentId, labId, form.assignmentMd)
|
||||||
redirect.addFlashAttribute("notice", "Индивидуальное задание сохранено")
|
redirect.addFlashAttribute("notice", "Индивидуальное задание сохранено")
|
||||||
redirectToGroup(groupId, labId)
|
redirectToGroup(form.groupId, labId)
|
||||||
} catch (ex: ApiException) {
|
} catch (ex: ApiException) {
|
||||||
redirect.addFlashAttribute("error", ex.message)
|
redirect.addFlashAttribute("error", ex.message)
|
||||||
redirectToGroup(groupId, labId)
|
redirectToGroup(form.groupId, labId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Назначение/правка индивидуального задания со страницы группы. */
|
/** Назначение/правка индивидуального задания со страницы группы. */
|
||||||
@PostMapping("/defenses/save")
|
@PostMapping("/defenses/save")
|
||||||
fun saveDefense(
|
fun saveDefense(
|
||||||
@RequestParam studentId: String,
|
@ModelAttribute form: DefenseForm,
|
||||||
@RequestParam labId: String,
|
@RequestParam labId: String,
|
||||||
@RequestParam assignmentMd: String,
|
|
||||||
@RequestParam(required = false) groupId: String?,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
redirect: RedirectAttributes,
|
redirect: RedirectAttributes,
|
||||||
): String =
|
): String =
|
||||||
try {
|
try {
|
||||||
api.assignDefense(token(session), studentId, labId, assignmentMd)
|
api.assignDefense(token(session), form.studentId, labId, form.assignmentMd)
|
||||||
redirect.addFlashAttribute("notice", "Индивидуальное задание сохранено")
|
redirect.addFlashAttribute("notice", "Индивидуальное задание сохранено")
|
||||||
redirectToGroup(groupId, labId)
|
redirectToGroup(form.groupId, labId)
|
||||||
} catch (ex: ApiException) {
|
} catch (ex: ApiException) {
|
||||||
redirect.addFlashAttribute("error", ex.message)
|
redirect.addFlashAttribute("error", ex.message)
|
||||||
redirectToGroup(groupId, labId)
|
redirectToGroup(form.groupId, labId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/labs/{labId}/defense/import")
|
@PostMapping("/labs/{labId}/defense/import")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ru.ysu.lms.web.admin
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpSession
|
import jakarta.servlet.http.HttpSession
|
||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
@@ -29,14 +30,12 @@ class AdminDistributeController(
|
|||||||
@PostMapping("/groups/{gid}/set-level")
|
@PostMapping("/groups/{gid}/set-level")
|
||||||
fun setStudentLevel(
|
fun setStudentLevel(
|
||||||
@PathVariable gid: String,
|
@PathVariable gid: String,
|
||||||
@RequestParam studentId: String,
|
@ModelAttribute form: StudentLevelForm,
|
||||||
@RequestParam labId: String,
|
|
||||||
@RequestParam labLevelId: String,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
redirect: RedirectAttributes,
|
redirect: RedirectAttributes,
|
||||||
): String =
|
): String =
|
||||||
try {
|
try {
|
||||||
api.setLevel(token(session), LevelAssignPayload(studentId, labId, labLevelId))
|
api.setLevel(token(session), LevelAssignPayload(form.studentId, form.labId, form.labLevelId))
|
||||||
redirect.addFlashAttribute("notice", "Уровень обновлён — вариант подобран автоматически")
|
redirect.addFlashAttribute("notice", "Уровень обновлён — вариант подобран автоматически")
|
||||||
"redirect:/admin/groups/$gid"
|
"redirect:/admin/groups/$gid"
|
||||||
} catch (ex: ApiException) {
|
} catch (ex: ApiException) {
|
||||||
@@ -47,14 +46,12 @@ class AdminDistributeController(
|
|||||||
@PostMapping("/groups/{gid}/pin-variant")
|
@PostMapping("/groups/{gid}/pin-variant")
|
||||||
fun pinStudentVariant(
|
fun pinStudentVariant(
|
||||||
@PathVariable gid: String,
|
@PathVariable gid: String,
|
||||||
@RequestParam studentId: String,
|
@ModelAttribute form: PinVariantForm,
|
||||||
@RequestParam labId: String,
|
|
||||||
@RequestParam variantId: String,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
redirect: RedirectAttributes,
|
redirect: RedirectAttributes,
|
||||||
): String =
|
): String =
|
||||||
try {
|
try {
|
||||||
val result = api.assignVariant(token(session), AssignVariantPayload(studentId, labId, variantId))
|
val result = api.assignVariant(token(session), AssignVariantPayload(form.studentId, form.labId, form.variantId))
|
||||||
redirect.addFlashAttribute("notice", "Закреплён вариант ${result.variantNumber}")
|
redirect.addFlashAttribute("notice", "Закреплён вариант ${result.variantNumber}")
|
||||||
"redirect:/admin/groups/$gid"
|
"redirect:/admin/groups/$gid"
|
||||||
} catch (ex: ApiException) {
|
} catch (ex: ApiException) {
|
||||||
|
|||||||
92
web/src/main/kotlin/ru/ysu/lms/web/admin/AdminForms.kt
Normal file
92
web/src/main/kotlin/ru/ysu/lms/web/admin/AdminForms.kt
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package ru.ysu.lms.web.admin
|
||||||
|
|
||||||
|
// Формы админ-контроллеров: связываются через @ModelAttribute.
|
||||||
|
// Имена полей совпадают с именами полей в шаблонах (то же, что было в @RequestParam).
|
||||||
|
|
||||||
|
data class CreateStudentForm(
|
||||||
|
val studentNo: String = "",
|
||||||
|
val lastName: String = "",
|
||||||
|
val firstName: String? = null,
|
||||||
|
val middleName: String? = null,
|
||||||
|
val basis: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class StudentLevelForm(
|
||||||
|
val studentId: String = "",
|
||||||
|
val labId: String = "",
|
||||||
|
val labLevelId: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
data class PinVariantForm(
|
||||||
|
val studentId: String = "",
|
||||||
|
val labId: String = "",
|
||||||
|
val variantId: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DefenseForm(
|
||||||
|
val studentId: String = "",
|
||||||
|
val assignmentMd: String = "",
|
||||||
|
val groupId: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CreateLabForm(
|
||||||
|
val number: Int = 0,
|
||||||
|
val title: String = "",
|
||||||
|
val deadlineAt: String? = null,
|
||||||
|
val defenseAt: String? = null,
|
||||||
|
val pointsMax: String = "",
|
||||||
|
val status: String = "",
|
||||||
|
val descriptionMd: String = "",
|
||||||
|
val levels: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SaveLevelForm(
|
||||||
|
val levelId: String? = null,
|
||||||
|
val code: String = "",
|
||||||
|
val title: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SaveVariantForm(
|
||||||
|
val variantId: String? = null,
|
||||||
|
val labLevelId: String = "",
|
||||||
|
val number: String = "",
|
||||||
|
val title: String = "",
|
||||||
|
val statementMd: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
data class EditLabForm(
|
||||||
|
val title: String = "",
|
||||||
|
val deadlineAt: String? = null,
|
||||||
|
val defenseAt: String? = null,
|
||||||
|
val pointsMax: String = "",
|
||||||
|
val status: String = "",
|
||||||
|
val descriptionMd: String = "",
|
||||||
|
val action: String = "save",
|
||||||
|
val levelIds: List<String>? = null,
|
||||||
|
val levelTitles: List<String>? = null,
|
||||||
|
val variantIds: List<String>? = null,
|
||||||
|
val variantLevelIds: List<String>? = null,
|
||||||
|
val variantNumbers: List<String>? = null,
|
||||||
|
val variantTitles: List<String>? = null,
|
||||||
|
val variantStatements: List<String>? = null,
|
||||||
|
val newLevelCode: String? = null,
|
||||||
|
val newLevelTitle: String? = null,
|
||||||
|
val addLevelId: String? = null,
|
||||||
|
val addNumber: String? = null,
|
||||||
|
val addTitle: String? = null,
|
||||||
|
val addStatementMd: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SetGradeForm(
|
||||||
|
val studentId: String = "",
|
||||||
|
val labId: String = "",
|
||||||
|
val score: String = "",
|
||||||
|
val commentsJson: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SubmissionDownloadForm(
|
||||||
|
val group: String? = null,
|
||||||
|
val no: String? = null,
|
||||||
|
val name: String? = null,
|
||||||
|
val attempt: String? = null,
|
||||||
|
)
|
||||||
@@ -7,6 +7,7 @@ import org.springframework.http.ResponseEntity
|
|||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
import org.springframework.ui.Model
|
import org.springframework.ui.Model
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
@@ -188,7 +189,7 @@ class AdminGroupController(
|
|||||||
buildDefenseRows(t, group, labs)
|
buildDefenseRows(t, group, labs)
|
||||||
|
|
||||||
val candidates = runCatching { api.groupCandidates(t, id) }.getOrDefault(emptyList())
|
val candidates = runCatching { api.groupCandidates(t, id) }.getOrDefault(emptyList())
|
||||||
fillGroupModel(model, group, labs, candidates, rows, defenseRows, publishedLabs)
|
fillGroupModel(model, GroupViewModel(group, labs, candidates, rows, defenseRows, publishedLabs))
|
||||||
return "admin/group"
|
return "admin/group"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,36 +224,37 @@ class AdminGroupController(
|
|||||||
}
|
}
|
||||||
}.sortedWith(compareBy({ it.fullName.lowercase() }, { it.labNumber }))
|
}.sortedWith(compareBy({ it.fullName.lowercase() }, { it.labNumber }))
|
||||||
|
|
||||||
|
private data class GroupViewModel(
|
||||||
|
val group: GroupDetailDto,
|
||||||
|
val labs: List<LabSummaryDto>,
|
||||||
|
val candidates: List<StudentCandidateDto>,
|
||||||
|
val rows: List<GroupStudentRow>,
|
||||||
|
val defenseRows: List<GroupDefenseRow>,
|
||||||
|
val publishedLabs: Set<String>,
|
||||||
|
)
|
||||||
|
|
||||||
private fun fillGroupModel(
|
private fun fillGroupModel(
|
||||||
model: Model,
|
model: Model,
|
||||||
group: GroupDetailDto,
|
view: GroupViewModel,
|
||||||
labs: List<LabSummaryDto>,
|
|
||||||
candidates: List<StudentCandidateDto>,
|
|
||||||
rows: List<GroupStudentRow>,
|
|
||||||
defenseRows: List<GroupDefenseRow>,
|
|
||||||
publishedLabs: Set<String>,
|
|
||||||
) {
|
) {
|
||||||
model.addAttribute("group", group)
|
model.addAttribute("group", view.group)
|
||||||
model.addAttribute("labs", labs)
|
model.addAttribute("labs", view.labs)
|
||||||
model.addAttribute("candidates", candidates)
|
model.addAttribute("candidates", view.candidates)
|
||||||
model.addAttribute("studentGrades", rows)
|
model.addAttribute("studentGrades", view.rows)
|
||||||
model.addAttribute("defenseRows", defenseRows)
|
model.addAttribute("defenseRows", view.defenseRows)
|
||||||
model.addAttribute("publishedLabIds", publishedLabs)
|
model.addAttribute("publishedLabIds", view.publishedLabs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/groups/{id}/grades")
|
@PostMapping("/groups/{id}/grades")
|
||||||
fun setGrade(
|
fun setGrade(
|
||||||
@PathVariable id: String,
|
@PathVariable id: String,
|
||||||
@RequestParam studentId: String,
|
@ModelAttribute form: SetGradeForm,
|
||||||
@RequestParam labId: String,
|
|
||||||
@RequestParam score: String,
|
|
||||||
@RequestParam(required = false) commentsJson: String?,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
redirect: RedirectAttributes,
|
redirect: RedirectAttributes,
|
||||||
): String =
|
): String =
|
||||||
try {
|
try {
|
||||||
val comments = parseComments(commentsJson ?: "[]")
|
val comments = parseComments(form.commentsJson ?: "[]")
|
||||||
api.setGrade(token(session), GradeSetPayload(studentId, labId, score, comments))
|
api.setGrade(token(session), GradeSetPayload(form.studentId, form.labId, form.score, comments))
|
||||||
redirect.addFlashAttribute("notice", "Оценка сохранена")
|
redirect.addFlashAttribute("notice", "Оценка сохранена")
|
||||||
"redirect:/admin/groups/$id"
|
"redirect:/admin/groups/$id"
|
||||||
} catch (ex: ApiException) {
|
} catch (ex: ApiException) {
|
||||||
@@ -294,14 +296,11 @@ class AdminGroupController(
|
|||||||
@GetMapping("/submissions/{id}/download")
|
@GetMapping("/submissions/{id}/download")
|
||||||
fun downloadSubmissionAsAdmin(
|
fun downloadSubmissionAsAdmin(
|
||||||
@PathVariable id: String,
|
@PathVariable id: String,
|
||||||
@RequestParam(required = false) group: String?,
|
@ModelAttribute form: SubmissionDownloadForm,
|
||||||
@RequestParam(required = false) no: String?,
|
|
||||||
@RequestParam(required = false) name: String?,
|
|
||||||
@RequestParam(required = false) attempt: String?,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
): ResponseEntity<ByteArray> {
|
): ResponseEntity<ByteArray> {
|
||||||
val file = api.downloadSubmissionAsAdmin(token(session), id)
|
val file = api.downloadSubmissionAsAdmin(token(session), id)
|
||||||
val filename = submissionFilename(group, no, name, attempt) ?: file.filename
|
val filename = submissionFilename(form.group, form.no, form.name, form.attempt) ?: file.filename
|
||||||
val encoded = java.net.URLEncoder.encode(filename, java.nio.charset.StandardCharsets.UTF_8)
|
val encoded = java.net.URLEncoder.encode(filename, java.nio.charset.StandardCharsets.UTF_8)
|
||||||
return ResponseEntity
|
return ResponseEntity
|
||||||
.ok()
|
.ok()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import jakarta.servlet.http.HttpSession
|
|||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
import org.springframework.ui.Model
|
import org.springframework.ui.Model
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
@@ -141,7 +142,10 @@ class AdminLabController(
|
|||||||
val defaultLevel = lab.levels.firstOrNull()
|
val defaultLevel = lab.levels.firstOrNull()
|
||||||
|
|
||||||
val assignmentGroups =
|
val assignmentGroups =
|
||||||
buildAssignmentGroups(t, lab, levelByStudent, assignmentByStudent, submissionsByStudent, defaultLevel)
|
buildAssignmentGroups(
|
||||||
|
t,
|
||||||
|
LabAssignmentData(lab, levelByStudent, assignmentByStudent, submissionsByStudent, defaultLevel),
|
||||||
|
)
|
||||||
|
|
||||||
model.addAttribute("lab", lab)
|
model.addAttribute("lab", lab)
|
||||||
model.addAttribute("descriptionHtml", markdown.render(lab.descriptionMd))
|
model.addAttribute("descriptionHtml", markdown.render(lab.descriptionMd))
|
||||||
@@ -176,14 +180,18 @@ class AdminLabController(
|
|||||||
return "admin/lab"
|
return "admin/lab"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private data class LabAssignmentData(
|
||||||
|
val lab: LabDetailDto,
|
||||||
|
val levelByStudent: Map<String, LevelAssignmentResultDto>,
|
||||||
|
val assignmentByStudent: Map<String, VariantAssignmentResultDto>,
|
||||||
|
val submissionsByStudent: Map<String, SubmissionDto>,
|
||||||
|
val defaultLevel: LabLevelDetailDto?,
|
||||||
|
)
|
||||||
|
|
||||||
/** Строки «группа → студенты» со их уровнем, вариантом и последней сдачей. */
|
/** Строки «группа → студенты» со их уровнем, вариантом и последней сдачей. */
|
||||||
private fun buildAssignmentGroups(
|
private fun buildAssignmentGroups(
|
||||||
token: String,
|
token: String,
|
||||||
lab: LabDetailDto,
|
data: LabAssignmentData,
|
||||||
levelByStudent: Map<String, LevelAssignmentResultDto>,
|
|
||||||
assignmentByStudent: Map<String, VariantAssignmentResultDto>,
|
|
||||||
submissionsByStudent: Map<String, SubmissionDto>,
|
|
||||||
defaultLevel: LabLevelDetailDto?,
|
|
||||||
): List<AdminLabGroup> =
|
): List<AdminLabGroup> =
|
||||||
api.listGroups(token).map { g ->
|
api.listGroups(token).map { g ->
|
||||||
val detail = api.groupDetail(token, g.id)
|
val detail = api.groupDetail(token, g.id)
|
||||||
@@ -191,9 +199,9 @@ class AdminLabController(
|
|||||||
groupName = g.name,
|
groupName = g.name,
|
||||||
students =
|
students =
|
||||||
detail.students.map { s ->
|
detail.students.map { s ->
|
||||||
val levelState = levelByStudent[s.id]
|
val levelState = data.levelByStudent[s.id]
|
||||||
val level = lab.levels.firstOrNull { it.id == levelState?.labLevelId } ?: defaultLevel
|
val level = data.lab.levels.firstOrNull { it.id == levelState?.labLevelId } ?: data.defaultLevel
|
||||||
val va = assignmentByStudent[s.id]
|
val va = data.assignmentByStudent[s.id]
|
||||||
AdminLabStudent(
|
AdminLabStudent(
|
||||||
studentId = s.id,
|
studentId = s.id,
|
||||||
fullName = s.fullName,
|
fullName = s.fullName,
|
||||||
@@ -204,9 +212,9 @@ class AdminLabController(
|
|||||||
variantId = va?.variantId,
|
variantId = va?.variantId,
|
||||||
variantNumber = va?.variantNumber,
|
variantNumber = va?.variantNumber,
|
||||||
source = if (va?.manual == true) "вручную" else "авто",
|
source = if (va?.manual == true) "вручную" else "авто",
|
||||||
submissionId = submissionsByStudent[s.id]?.id,
|
submissionId = data.submissionsByStudent[s.id]?.id,
|
||||||
submissionSeq = submissionsByStudent[s.id]?.seq,
|
submissionSeq = data.submissionsByStudent[s.id]?.seq,
|
||||||
submissionFiles = submissionsByStudent[s.id]?.files ?: emptyList(),
|
submissionFiles = data.submissionsByStudent[s.id]?.files ?: emptyList(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -231,21 +239,14 @@ class AdminLabController(
|
|||||||
|
|
||||||
@PostMapping("/labs/create")
|
@PostMapping("/labs/create")
|
||||||
fun createLab(
|
fun createLab(
|
||||||
@RequestParam number: Int,
|
@ModelAttribute form: CreateLabForm,
|
||||||
@RequestParam title: String,
|
|
||||||
@RequestParam(required = false) deadlineAt: String?,
|
|
||||||
@RequestParam(required = false) defenseAt: String?,
|
|
||||||
@RequestParam pointsMax: String,
|
|
||||||
@RequestParam status: String,
|
|
||||||
@RequestParam descriptionMd: String,
|
|
||||||
@RequestParam levels: String,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
redirect: RedirectAttributes,
|
redirect: RedirectAttributes,
|
||||||
): String =
|
): String =
|
||||||
try {
|
try {
|
||||||
// levels: строки "код;название" по одной на строку
|
// levels: строки "код;название" по одной на строку
|
||||||
val levelList =
|
val levelList =
|
||||||
levels
|
form.levels
|
||||||
.lines()
|
.lines()
|
||||||
.map { it.trim() }
|
.map { it.trim() }
|
||||||
.filter { it.isNotBlank() }
|
.filter { it.isNotBlank() }
|
||||||
@@ -261,17 +262,17 @@ class AdminLabController(
|
|||||||
api.createLab(
|
api.createLab(
|
||||||
token(session),
|
token(session),
|
||||||
LabCreatePayload(
|
LabCreatePayload(
|
||||||
number = number,
|
number = form.number,
|
||||||
title = title.trim(),
|
title = form.title.trim(),
|
||||||
deadlineAt =
|
deadlineAt =
|
||||||
ru.ysu.lms.web.DateUtils
|
ru.ysu.lms.web.DateUtils
|
||||||
.toUtcIso(deadlineAt),
|
.toUtcIso(form.deadlineAt),
|
||||||
defenseAt =
|
defenseAt =
|
||||||
ru.ysu.lms.web.DateUtils
|
ru.ysu.lms.web.DateUtils
|
||||||
.toUtcIso(defenseAt),
|
.toUtcIso(form.defenseAt),
|
||||||
pointsMax = java.math.BigDecimal(pointsMax),
|
pointsMax = java.math.BigDecimal(form.pointsMax),
|
||||||
status = status,
|
status = form.status,
|
||||||
descriptionMd = descriptionMd,
|
descriptionMd = form.descriptionMd,
|
||||||
levels = levelList,
|
levels = levelList,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -285,18 +286,16 @@ class AdminLabController(
|
|||||||
@PostMapping("/labs/{labId}/levels/save")
|
@PostMapping("/labs/{labId}/levels/save")
|
||||||
fun saveLevel(
|
fun saveLevel(
|
||||||
@PathVariable labId: String,
|
@PathVariable labId: String,
|
||||||
@RequestParam(required = false) levelId: String?,
|
@ModelAttribute form: SaveLevelForm,
|
||||||
@RequestParam code: String,
|
|
||||||
@RequestParam title: String,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
redirect: RedirectAttributes,
|
redirect: RedirectAttributes,
|
||||||
): String =
|
): String =
|
||||||
try {
|
try {
|
||||||
if (levelId.isNullOrBlank()) {
|
if (form.levelId.isNullOrBlank()) {
|
||||||
api.addLevel(token(session), labId, LevelSavePayload(code, title))
|
api.addLevel(token(session), labId, LevelSavePayload(form.code, form.title))
|
||||||
redirect.addFlashAttribute("notice", "Уровень добавлен")
|
redirect.addFlashAttribute("notice", "Уровень добавлен")
|
||||||
} else {
|
} else {
|
||||||
api.renameLevel(token(session), labId, levelId, title)
|
api.renameLevel(token(session), labId, form.levelId, form.title)
|
||||||
redirect.addFlashAttribute("notice", "Уровень обновлён")
|
redirect.addFlashAttribute("notice", "Уровень обновлён")
|
||||||
}
|
}
|
||||||
"redirect:/admin/labs/$labId"
|
"redirect:/admin/labs/$labId"
|
||||||
@@ -418,64 +417,45 @@ class AdminLabController(
|
|||||||
@PostMapping("/labs/{id}/edit")
|
@PostMapping("/labs/{id}/edit")
|
||||||
fun editLab(
|
fun editLab(
|
||||||
@PathVariable id: String,
|
@PathVariable id: String,
|
||||||
@RequestParam title: String,
|
@ModelAttribute form: EditLabForm,
|
||||||
@RequestParam(required = false) deadlineAt: String?,
|
|
||||||
@RequestParam(required = false) defenseAt: String?,
|
|
||||||
@RequestParam pointsMax: String,
|
|
||||||
@RequestParam status: String,
|
|
||||||
@RequestParam descriptionMd: String,
|
|
||||||
@RequestParam(defaultValue = "save") action: String,
|
|
||||||
@RequestParam(required = false) levelIds: List<String>?,
|
|
||||||
@RequestParam(required = false) levelTitles: List<String>?,
|
|
||||||
@RequestParam(required = false) variantIds: List<String>?,
|
|
||||||
@RequestParam(required = false) variantLevelIds: List<String>?,
|
|
||||||
@RequestParam(required = false) variantNumbers: List<String>?,
|
|
||||||
@RequestParam(required = false) variantTitles: List<String>?,
|
|
||||||
@RequestParam(required = false) variantStatements: List<String>?,
|
|
||||||
@RequestParam(required = false) newLevelCode: String?,
|
|
||||||
@RequestParam(required = false) newLevelTitle: String?,
|
|
||||||
@RequestParam(required = false) addLevelId: String?,
|
|
||||||
@RequestParam(required = false) addNumber: String?,
|
|
||||||
@RequestParam(required = false) addTitle: String?,
|
|
||||||
@RequestParam(required = false) addStatementMd: String?,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
redirect: RedirectAttributes,
|
redirect: RedirectAttributes,
|
||||||
): String {
|
): String {
|
||||||
val t = token(session)
|
val t = token(session)
|
||||||
return try {
|
return try {
|
||||||
handleEditDeleteAction(t, id, action, redirect)?.let { return it }
|
handleEditDeleteAction(t, id, form.action, redirect)?.let { return it }
|
||||||
|
|
||||||
api.updateLab(
|
api.updateLab(
|
||||||
t,
|
t,
|
||||||
id,
|
id,
|
||||||
LabUpdatePayload(
|
LabUpdatePayload(
|
||||||
title,
|
form.title,
|
||||||
ru.ysu.lms.web.DateUtils
|
ru.ysu.lms.web.DateUtils
|
||||||
.toUtcIso(deadlineAt),
|
.toUtcIso(form.deadlineAt),
|
||||||
ru.ysu.lms.web.DateUtils
|
ru.ysu.lms.web.DateUtils
|
||||||
.toUtcIso(defenseAt),
|
.toUtcIso(form.defenseAt),
|
||||||
pointsMax,
|
form.pointsMax,
|
||||||
status,
|
form.status,
|
||||||
descriptionMd,
|
form.descriptionMd,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
applyLevelRenames(t, id, levelIds, levelTitles)
|
applyLevelRenames(t, id, form.levelIds, form.levelTitles)
|
||||||
applyVariantSaves(t, id, variantIds, variantNumbers, variantLevelIds, variantTitles, variantStatements)
|
applyVariantSaves(t, id, form.variantIds, form.variantNumbers, form.variantLevelIds, form.variantTitles, form.variantStatements)
|
||||||
when {
|
when {
|
||||||
action == "add-level" && !newLevelCode.isNullOrBlank() && !newLevelTitle.isNullOrBlank() -> {
|
form.action == "add-level" && !form.newLevelCode.isNullOrBlank() && !form.newLevelTitle.isNullOrBlank() -> {
|
||||||
api.addLevel(t, id, LevelSavePayload(newLevelCode.trim(), newLevelTitle.trim()))
|
api.addLevel(t, id, LevelSavePayload(form.newLevelCode.trim(), form.newLevelTitle.trim()))
|
||||||
redirect.addFlashAttribute("notice", "Сохранено; уровень добавлен")
|
redirect.addFlashAttribute("notice", "Сохранено; уровень добавлен")
|
||||||
}
|
}
|
||||||
action == "add-variant" && !addLevelId.isNullOrBlank() && !addNumber.isNullOrBlank() -> {
|
form.action == "add-variant" && !form.addLevelId.isNullOrBlank() && !form.addNumber.isNullOrBlank() -> {
|
||||||
api.saveVariant(
|
api.saveVariant(
|
||||||
t,
|
t,
|
||||||
id,
|
id,
|
||||||
null,
|
null,
|
||||||
VariantSavePayload(
|
VariantSavePayload(
|
||||||
addLevelId,
|
form.addLevelId,
|
||||||
addNumber.trim(),
|
form.addNumber.trim(),
|
||||||
addTitle.orEmpty().trim(),
|
form.addTitle.orEmpty().trim(),
|
||||||
addStatementMd.orEmpty(),
|
form.addStatementMd.orEmpty(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
redirect.addFlashAttribute("notice", "Сохранено; вариант добавлен")
|
redirect.addFlashAttribute("notice", "Сохранено; вариант добавлен")
|
||||||
@@ -492,16 +472,17 @@ class AdminLabController(
|
|||||||
@PostMapping("/labs/{labId}/variants/save")
|
@PostMapping("/labs/{labId}/variants/save")
|
||||||
fun saveVariant(
|
fun saveVariant(
|
||||||
@PathVariable labId: String,
|
@PathVariable labId: String,
|
||||||
@RequestParam(required = false) variantId: String?,
|
@ModelAttribute form: SaveVariantForm,
|
||||||
@RequestParam labLevelId: String,
|
|
||||||
@RequestParam number: String,
|
|
||||||
@RequestParam title: String,
|
|
||||||
@RequestParam statementMd: String,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
redirect: RedirectAttributes,
|
redirect: RedirectAttributes,
|
||||||
): String =
|
): String =
|
||||||
try {
|
try {
|
||||||
api.saveVariant(token(session), labId, variantId, VariantSavePayload(labLevelId, number, title, statementMd))
|
api.saveVariant(
|
||||||
|
token(session),
|
||||||
|
labId,
|
||||||
|
form.variantId,
|
||||||
|
VariantSavePayload(form.labLevelId, form.number, form.title, form.statementMd),
|
||||||
|
)
|
||||||
redirect.addFlashAttribute("notice", "Вариант сохранён")
|
redirect.addFlashAttribute("notice", "Вариант сохранён")
|
||||||
"redirect:/admin/labs/$labId/edit"
|
"redirect:/admin/labs/$labId/edit"
|
||||||
} catch (ex: ApiException) {
|
} catch (ex: ApiException) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.springframework.http.MediaType
|
|||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
@@ -66,11 +67,7 @@ class AdminStudentController(
|
|||||||
@PostMapping("/groups/{groupId}/students/create")
|
@PostMapping("/groups/{groupId}/students/create")
|
||||||
fun createStudent(
|
fun createStudent(
|
||||||
@PathVariable groupId: String,
|
@PathVariable groupId: String,
|
||||||
@RequestParam studentNo: String,
|
@ModelAttribute form: CreateStudentForm,
|
||||||
@RequestParam lastName: String,
|
|
||||||
@RequestParam(required = false) firstName: String?,
|
|
||||||
@RequestParam(required = false) middleName: String?,
|
|
||||||
@RequestParam(required = false) basis: String?,
|
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
redirect: RedirectAttributes,
|
redirect: RedirectAttributes,
|
||||||
): String =
|
): String =
|
||||||
@@ -80,11 +77,11 @@ class AdminStudentController(
|
|||||||
token(session),
|
token(session),
|
||||||
groupId,
|
groupId,
|
||||||
CreateStudentRequestDto(
|
CreateStudentRequestDto(
|
||||||
studentNo = studentNo.trim(),
|
studentNo = form.studentNo.trim(),
|
||||||
lastName = lastName.trim(),
|
lastName = form.lastName.trim(),
|
||||||
firstName = firstName.orEmpty().trim(),
|
firstName = form.firstName.orEmpty().trim(),
|
||||||
middleName = middleName.orEmpty().trim(),
|
middleName = form.middleName.orEmpty().trim(),
|
||||||
basis = basis.orEmpty().trim(),
|
basis = form.basis.orEmpty().trim(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
redirect.addFlashAttribute("createdStudent", result)
|
redirect.addFlashAttribute("createdStudent", result)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import ru.ysu.lms.web.api.ApiClient
|
|||||||
import ru.ysu.lms.web.api.ApiException
|
import ru.ysu.lms.web.api.ApiException
|
||||||
import ru.ysu.lms.web.api.ArticlesReorderDto
|
import ru.ysu.lms.web.api.ArticlesReorderDto
|
||||||
import ru.ysu.lms.web.api.ContentReorderDto
|
import ru.ysu.lms.web.api.ContentReorderDto
|
||||||
|
import ru.ysu.lms.web.api.CreateArticlePayload
|
||||||
import ru.ysu.lms.web.api.SectionsReorderDto
|
import ru.ysu.lms.web.api.SectionsReorderDto
|
||||||
import ru.ysu.lms.web.api.createArticle
|
import ru.ysu.lms.web.api.createArticle
|
||||||
import ru.ysu.lms.web.api.createChapter
|
import ru.ysu.lms.web.api.createChapter
|
||||||
@@ -269,12 +270,14 @@ class ContentAdminController(
|
|||||||
try {
|
try {
|
||||||
api.createArticle(
|
api.createArticle(
|
||||||
token(session),
|
token(session),
|
||||||
form.chapterId,
|
CreateArticlePayload(
|
||||||
form.sectionId,
|
chapterId = form.chapterId,
|
||||||
form.newChapterTitle?.takeIf { it.isNotBlank() },
|
sectionId = form.sectionId,
|
||||||
form.newSectionTitle?.takeIf { it.isNotBlank() },
|
newChapterTitle = form.newChapterTitle?.takeIf { it.isNotBlank() },
|
||||||
form.title.trim(),
|
newSectionTitle = form.newSectionTitle?.takeIf { it.isNotBlank() },
|
||||||
form.bodyMd,
|
title = form.title.trim(),
|
||||||
|
bodyMd = form.bodyMd,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
redirect.addFlashAttribute("notice", "Статья «${form.title.trim()}» создана")
|
redirect.addFlashAttribute("notice", "Статья «${form.title.trim()}» создана")
|
||||||
redirectBack(form.back, "redirect:/admin/content")
|
redirectBack(form.back, "redirect:/admin/content")
|
||||||
|
|||||||
@@ -231,14 +231,18 @@ fun ApiClient.deleteSectionWithMove(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class CreateArticlePayload(
|
||||||
|
val chapterId: String?,
|
||||||
|
val sectionId: String?,
|
||||||
|
val newChapterTitle: String?,
|
||||||
|
val newSectionTitle: String?,
|
||||||
|
val title: String,
|
||||||
|
val bodyMd: String,
|
||||||
|
)
|
||||||
|
|
||||||
fun ApiClient.createArticle(
|
fun ApiClient.createArticle(
|
||||||
token: String,
|
token: String,
|
||||||
chapterId: String?,
|
payload: CreateArticlePayload,
|
||||||
sectionId: String?,
|
|
||||||
newChapterTitle: String?,
|
|
||||||
newSectionTitle: String?,
|
|
||||||
title: String,
|
|
||||||
bodyMd: String,
|
|
||||||
): ContentArticleDto =
|
): ContentArticleDto =
|
||||||
call {
|
call {
|
||||||
client
|
client
|
||||||
@@ -248,12 +252,12 @@ fun ApiClient.createArticle(
|
|||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.body(
|
.body(
|
||||||
mapOf(
|
mapOf(
|
||||||
"chapterId" to chapterId,
|
"chapterId" to payload.chapterId,
|
||||||
"sectionId" to sectionId,
|
"sectionId" to payload.sectionId,
|
||||||
"newChapterTitle" to newChapterTitle,
|
"newChapterTitle" to payload.newChapterTitle,
|
||||||
"newSectionTitle" to newSectionTitle,
|
"newSectionTitle" to payload.newSectionTitle,
|
||||||
"title" to title,
|
"title" to payload.title,
|
||||||
"bodyMd" to bodyMd,
|
"bodyMd" to payload.bodyMd,
|
||||||
),
|
),
|
||||||
).retrieve()
|
).retrieve()
|
||||||
.body(ContentArticleDto::class.java)
|
.body(ContentArticleDto::class.java)
|
||||||
|
|||||||
Reference in New Issue
Block a user