diff --git a/src/main/kotlin/com/lanyuanxiaoyao/bookstore/Exporter.kt b/src/main/kotlin/com/lanyuanxiaoyao/bookstore/Exporter.kt new file mode 100644 index 0000000..11d29a9 --- /dev/null +++ b/src/main/kotlin/com/lanyuanxiaoyao/bookstore/Exporter.kt @@ -0,0 +1,36 @@ +package com.lanyuanxiaoyao.bookstore + +data class ExporterItem(val name: String, val content: String) + +interface ExporterTemplate { + fun exportBook(book: Book): ExporterItem + fun exportChapter(chapter: Chapter): ExporterItem +} + +val defaultExporterTemplate = object : ExporterTemplate { + override fun exportBook(book: Book): ExporterItem { + return ExporterItem( + name = book.name, + content = StringBuilder().apply { + for (chapter in book.chapters.sortedBy { it.sequence }) { + val (chapterName, chapterContent) = exportChapter(chapter) + appendLine(chapterName) + appendLine() + appendLine(chapterContent) + } + appendLine() + }.toString() + ) + } + + override fun exportChapter(chapter: Chapter): ExporterItem { + return ExporterItem( + name = "第${chapter.sequence}章${if (chapter.name.isNullOrBlank()) "" else "-${chapter.name}"}", + content = StringBuilder().apply { + for (line in chapter.content.sortedBy { it.sequence }) { + appendLine(line.text) + } + }.toString() + ) + } +} diff --git a/src/main/kotlin/com/lanyuanxiaoyao/bookstore/Processor.kt b/src/main/kotlin/com/lanyuanxiaoyao/bookstore/Processor.kt index e76b0fd..89b6fab 100644 --- a/src/main/kotlin/com/lanyuanxiaoyao/bookstore/Processor.kt +++ b/src/main/kotlin/com/lanyuanxiaoyao/bookstore/Processor.kt @@ -1,5 +1,7 @@ package com.lanyuanxiaoyao.bookstore +import cn.hutool.core.convert.Convert + // @Configuration // class ProcessorConfiguration { // @Bean @@ -22,39 +24,29 @@ interface Optimization { fun handle(line: String): String } +class TrimOptimization : Optimization { + override fun handle(line: String): String { + if (line.isBlank()) return line + return line.trim() + } +} + class CharWidthOptimization : Optimization { override fun handle(line: String): String { if (line.isBlank()) return line + return Convert.toDBC(line) + } +} + +class QuoteCovertChineseOptimization : Optimization { + override fun handle(line: String): String { return line - .map { char -> - if (char in '!'..'~') - char.code - 65248 - else - char - } - .joinToString("") + .replace(",", ",") + .replace("?", "?") + .replace("!", "!") + .replace(";", ";") + .replace(":", ":") + .replace("(", "(") + .replace(")", ")") } } - -interface ProcessTemplate { - fun process(text: String): List -} - -open class AbstractProcessTemplate( - private val slicer: Slicer, - private val optimizations: List, -) : ProcessTemplate { - override fun process(text: String): List { - return slicer.slice(text) - .map { line -> - optimizations.fold(line) { result, optimization -> optimization.handle(result) } - } - } -} - -class SexInSexProcessTemplate : AbstractProcessTemplate( - RegexSlicer(Regex("")), - listOf( - CharWidthOptimization(), - ), -) diff --git a/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/BookController.kt b/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/BookController.kt index 1e34454..80167d0 100644 --- a/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/BookController.kt +++ b/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/BookController.kt @@ -7,6 +7,7 @@ import cn.hutool.core.util.URLUtil import com.lanyuanxiaoyao.bookstore.Book import com.lanyuanxiaoyao.bookstore.BookRepository import com.lanyuanxiaoyao.bookstore.PageResponse +import com.lanyuanxiaoyao.bookstore.defaultExporterTemplate import jakarta.annotation.Resource import jakarta.servlet.http.HttpServletResponse import jakarta.transaction.Transactional @@ -75,24 +76,12 @@ class BookController { @GetMapping("export/{bookId}") fun export(@PathVariable("bookId") bookId: String, response: HttpServletResponse) { val book = bookRepository.findById(bookId).orElseThrow() - val builder = StringBuilder() - for (chapter in book.chapters.sortedBy { it.sequence }) { - if (chapter.name.isNullOrBlank()) { - builder.appendLine("第${chapter.sequence}章") - } else { - builder.appendLine("第${chapter.sequence}章 ${chapter.name}") - } - builder.appendLine() - for (line in chapter.content.sortedBy { it.sequence }) { - builder.appendLine(line.text) - } - builder.appendLine() - } + val result = defaultExporterTemplate.exportBook(book) response.setHeader("Access-Control-Expose-Headers", "Content-Type") response.setHeader("Content-Type", "text/plain") response.setHeader("Access-Control-Expose-Headers", "Content-Disposition") - response.setHeader("Content-Disposition", StrUtil.format("attachment; filename={}", URLUtil.encodeAll("${book.name}.txt"))) - IoUtil.copy(builder.toString().byteInputStream(), response.outputStream) + response.setHeader("Content-Disposition", StrUtil.format("attachment; filename={}", URLUtil.encodeAll("${result.name}.txt"))) + IoUtil.copy(result.content.byteInputStream(), response.outputStream) } data class ViewItem( diff --git a/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/ChapterController.kt b/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/ChapterController.kt index a62970f..5a7e76b 100644 --- a/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/ChapterController.kt +++ b/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/ChapterController.kt @@ -1,6 +1,9 @@ package com.lanyuanxiaoyao.bookstore.controller +import cn.hutool.core.io.IoUtil import cn.hutool.core.util.IdUtil +import cn.hutool.core.util.StrUtil +import cn.hutool.core.util.URLUtil import com.lanyuanxiaoyao.bookstore.Book import com.lanyuanxiaoyao.bookstore.BookRepository import com.lanyuanxiaoyao.bookstore.Chapter @@ -9,7 +12,9 @@ import com.lanyuanxiaoyao.bookstore.Line import com.lanyuanxiaoyao.bookstore.LineRepository import com.lanyuanxiaoyao.bookstore.PageResponse import com.lanyuanxiaoyao.bookstore.SingleResponse +import com.lanyuanxiaoyao.bookstore.defaultExporterTemplate import jakarta.annotation.Resource +import jakarta.servlet.http.HttpServletResponse import jakarta.transaction.Transactional import org.slf4j.LoggerFactory import org.springframework.data.domain.PageRequest @@ -40,7 +45,7 @@ class ChapterController { fun list( @PathVariable("bookId") bookId: String, @RequestParam("page", defaultValue = "1") page: Int, - @RequestParam("size", defaultValue = "10") size: Int + @RequestParam("size", defaultValue = "10") size: Int, ): PageResponse { val pageable = chapterRepository.findAll({ root, _, builder -> builder.equal(root.get("book").get("bookId"), bookId) @@ -101,11 +106,26 @@ class ChapterController { lineRepository .findAll({ root, _, builder -> builder.equal(root.get("chapter").get("chapterId"), chapterId) - }, Sort.by(Sort.Direction.DESC, "sequence")) + }, Sort.by(Sort.Direction.ASC, "sequence")) .joinToString("\n") { "

${it.text}

" } ) } + @Transactional + @GetMapping("export/{chapterId}") + fun export(@PathVariable("chapterId") chapterId: String, response: HttpServletResponse) { + val chapter = chapterRepository.findById(chapterId).orElseThrow() + val result = defaultExporterTemplate.exportChapter(chapter) + response.setHeader("Access-Control-Expose-Headers", "Content-Type") + response.setHeader("Content-Type", "text/plain") + response.setHeader("Access-Control-Expose-Headers", "Content-Disposition") + response.setHeader( + "Content-Disposition", + StrUtil.format("attachment; filename={}", URLUtil.encodeAll("${result.name}.txt")) + ) + IoUtil.copy(result.content.byteInputStream(), response.outputStream) + } + @Transactional @GetMapping("remove/{chapterId}") fun remove(@PathVariable("chapterId") chapterId: String) { @@ -128,6 +148,6 @@ class ChapterController { data class ImportItem( val override: Boolean = false, - val text: String + val text: String, ) } \ No newline at end of file diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 011669a..f2a77c1 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -208,6 +208,12 @@ }, }, }, + { + type: 'action', + label: '导出', + actionType: 'download', + api: '${base}/chapter/export/${chapterId}', + }, { type: 'action', label: '编辑正文', diff --git a/src/test/kotlin/com/lanyuanxiaoyao/bookstore/ChapterOptimization.kt b/src/test/kotlin/com/lanyuanxiaoyao/bookstore/ChapterOptimization.kt new file mode 100644 index 0000000..513b2a4 --- /dev/null +++ b/src/test/kotlin/com/lanyuanxiaoyao/bookstore/ChapterOptimization.kt @@ -0,0 +1,19 @@ +package com.lanyuanxiaoyao.bookstore + +import java.io.File +import org.junit.jupiter.api.Test + +class ChapterOptimization { + @Test + fun run() { + val optimizations = listOf( + TrimOptimization(), + CharWidthOptimization(), + QuoteCovertChineseOptimization(), + ) + val content = File("C:\\Users\\lanyuanxiaoyao\\Downloads\\第141章-反攻云岚宗.txt") + .readLines() + .joinToString("\n") { optimizations.fold(it) { line, optimization -> optimization.handle(line) } } + println(content) + } +} \ No newline at end of file