Content import: sync sections and articles (archive is the source of truth)
- importZip now updates titles/positions of existing chapters and sections and removes sections/articles of archived chapters that are missing in the archive (so restructured material no longer leaves stale sections behind); removed count is reported in ContentImportResult and in the admin flash message - docs: note about import semantics in the class diagram and sequences
This commit is contained in:
@@ -59,6 +59,8 @@ data class ContentImportResult(
|
||||
val sections: Int,
|
||||
val articles: Int,
|
||||
val assets: Int,
|
||||
/** Сколько устаревших разделов и статей удалено при синхронизации. */
|
||||
val removed: Int = 0,
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -453,24 +455,38 @@ class ContentService(
|
||||
var chCount = 0
|
||||
var secCount = 0
|
||||
var artCount = 0
|
||||
var removedCount = 0
|
||||
var currentChapter: ContentChapter? = null
|
||||
var currentSection: ContentSection? = null
|
||||
val touchedChapters = mutableSetOf<UUID>()
|
||||
val seenSections = mutableSetOf<UUID>()
|
||||
val seenArticles = mutableSetOf<UUID>()
|
||||
|
||||
for (p in parsed) {
|
||||
if (currentChapter?.title != p.chapterDir) {
|
||||
val slug = chapterSlug(p) ?: "chapter-${chCount + 1}"
|
||||
currentChapter = chapters.findBySlug(slug) ?: chapters.save(
|
||||
currentChapter = chapters.findBySlug(slug)?.apply {
|
||||
title = p.chapterDir
|
||||
position = p.chapterPos
|
||||
} ?: chapters.save(
|
||||
ContentChapter(title = p.chapterDir, slug = slug, position = p.chapterPos),
|
||||
)
|
||||
currentChapter = chapters.save(currentChapter!!)
|
||||
touchedChapters.add(currentChapter!!.id)
|
||||
chCount += 1
|
||||
currentSection = null
|
||||
}
|
||||
val chapter = currentChapter ?: throw BadRequestException("Не удалось определить главу")
|
||||
if (currentSection?.title != p.sectionDir) {
|
||||
val slug = sectionSlug(p) ?: "section-${secCount + 1}"
|
||||
currentSection = sections.findByChapterIdAndSlug(chapter.id, slug) ?: sections.save(
|
||||
currentSection = sections.findByChapterIdAndSlug(chapter.id, slug)?.apply {
|
||||
title = p.sectionDir
|
||||
position = p.sectionPos
|
||||
} ?: sections.save(
|
||||
ContentSection(chapterId = chapter.id, title = p.sectionDir, slug = slug, position = p.sectionPos),
|
||||
)
|
||||
currentSection = sections.save(currentSection!!)
|
||||
seenSections.add(currentSection!!.id)
|
||||
secCount += 1
|
||||
}
|
||||
val section = currentSection ?: throw BadRequestException("Не удалось определить раздел")
|
||||
@@ -489,10 +505,34 @@ class ContentService(
|
||||
position = p.articlePos
|
||||
}
|
||||
articles.save(article)
|
||||
seenArticles.add(article.id)
|
||||
artCount += 1
|
||||
}
|
||||
|
||||
return ContentImportResult(chapters = chCount, sections = secCount, articles = artCount, assets = linkRewrite.size)
|
||||
// Синхронизация: в главах из архива удаляем разделы и статьи, которых в архиве нет.
|
||||
for (chapterId in touchedChapters) {
|
||||
for (section in sections.findByChapterIdOrderByPositionAscIdAsc(chapterId)) {
|
||||
if (section.id in seenSections) {
|
||||
for (article in articles.findBySectionIdOrderByPositionAscIdAsc(section.id)) {
|
||||
if (article.id !in seenArticles) {
|
||||
articles.delete(article)
|
||||
removedCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
removedCount += articles.findBySectionIdOrderByPositionAscIdAsc(section.id).size + 1
|
||||
sections.delete(section)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ContentImportResult(
|
||||
chapters = chCount,
|
||||
sections = secCount,
|
||||
articles = artCount,
|
||||
assets = linkRewrite.size,
|
||||
removed = removedCount,
|
||||
)
|
||||
}
|
||||
|
||||
private fun chapterSlug(p: ParsedArticle): String? =
|
||||
|
||||
Reference in New Issue
Block a user