1
0

feat: 优化书籍和章节的导出

This commit is contained in:
2024-12-23 00:06:33 +08:00
parent a3985ed087
commit b5d2c9f310
6 changed files with 110 additions and 48 deletions

View File

@@ -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()
)
}
}

View File

@@ -1,5 +1,7 @@
package com.lanyuanxiaoyao.bookstore package com.lanyuanxiaoyao.bookstore
import cn.hutool.core.convert.Convert
// @Configuration // @Configuration
// class ProcessorConfiguration { // class ProcessorConfiguration {
// @Bean // @Bean
@@ -22,39 +24,29 @@ interface Optimization {
fun handle(line: String): String 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 { class CharWidthOptimization : Optimization {
override fun handle(line: String): String { override fun handle(line: String): String {
if (line.isBlank()) return line if (line.isBlank()) return line
return Convert.toDBC(line)
}
}
class QuoteCovertChineseOptimization : Optimization {
override fun handle(line: String): String {
return line return line
.map { char -> .replace(",", "")
if (char in ''..'') .replace("?", "")
char.code - 65248 .replace("!", "")
else .replace(";", "")
char .replace(":", "")
} .replace("(", "")
.joinToString("") .replace(")", "")
} }
} }
interface ProcessTemplate {
fun process(text: String): List<String>
}
open class AbstractProcessTemplate(
private val slicer: Slicer,
private val optimizations: List<Optimization>,
) : ProcessTemplate {
override fun process(text: String): List<String> {
return slicer.slice(text)
.map { line ->
optimizations.fold(line) { result, optimization -> optimization.handle(result) }
}
}
}
class SexInSexProcessTemplate : AbstractProcessTemplate(
RegexSlicer(Regex("")),
listOf(
CharWidthOptimization(),
),
)

View File

@@ -7,6 +7,7 @@ import cn.hutool.core.util.URLUtil
import com.lanyuanxiaoyao.bookstore.Book import com.lanyuanxiaoyao.bookstore.Book
import com.lanyuanxiaoyao.bookstore.BookRepository import com.lanyuanxiaoyao.bookstore.BookRepository
import com.lanyuanxiaoyao.bookstore.PageResponse import com.lanyuanxiaoyao.bookstore.PageResponse
import com.lanyuanxiaoyao.bookstore.defaultExporterTemplate
import jakarta.annotation.Resource import jakarta.annotation.Resource
import jakarta.servlet.http.HttpServletResponse import jakarta.servlet.http.HttpServletResponse
import jakarta.transaction.Transactional import jakarta.transaction.Transactional
@@ -75,24 +76,12 @@ class BookController {
@GetMapping("export/{bookId}") @GetMapping("export/{bookId}")
fun export(@PathVariable("bookId") bookId: String, response: HttpServletResponse) { fun export(@PathVariable("bookId") bookId: String, response: HttpServletResponse) {
val book = bookRepository.findById(bookId).orElseThrow() val book = bookRepository.findById(bookId).orElseThrow()
val builder = StringBuilder() val result = defaultExporterTemplate.exportBook(book)
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()
}
response.setHeader("Access-Control-Expose-Headers", "Content-Type") response.setHeader("Access-Control-Expose-Headers", "Content-Type")
response.setHeader("Content-Type", "text/plain") response.setHeader("Content-Type", "text/plain")
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition") response.setHeader("Access-Control-Expose-Headers", "Content-Disposition")
response.setHeader("Content-Disposition", StrUtil.format("attachment; filename={}", URLUtil.encodeAll("${book.name}.txt"))) response.setHeader("Content-Disposition", StrUtil.format("attachment; filename={}", URLUtil.encodeAll("${result.name}.txt")))
IoUtil.copy(builder.toString().byteInputStream(), response.outputStream) IoUtil.copy(result.content.byteInputStream(), response.outputStream)
} }
data class ViewItem( data class ViewItem(

View File

@@ -1,6 +1,9 @@
package com.lanyuanxiaoyao.bookstore.controller package com.lanyuanxiaoyao.bookstore.controller
import cn.hutool.core.io.IoUtil
import cn.hutool.core.util.IdUtil 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.Book
import com.lanyuanxiaoyao.bookstore.BookRepository import com.lanyuanxiaoyao.bookstore.BookRepository
import com.lanyuanxiaoyao.bookstore.Chapter import com.lanyuanxiaoyao.bookstore.Chapter
@@ -9,7 +12,9 @@ import com.lanyuanxiaoyao.bookstore.Line
import com.lanyuanxiaoyao.bookstore.LineRepository import com.lanyuanxiaoyao.bookstore.LineRepository
import com.lanyuanxiaoyao.bookstore.PageResponse import com.lanyuanxiaoyao.bookstore.PageResponse
import com.lanyuanxiaoyao.bookstore.SingleResponse import com.lanyuanxiaoyao.bookstore.SingleResponse
import com.lanyuanxiaoyao.bookstore.defaultExporterTemplate
import jakarta.annotation.Resource import jakarta.annotation.Resource
import jakarta.servlet.http.HttpServletResponse
import jakarta.transaction.Transactional import jakarta.transaction.Transactional
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.data.domain.PageRequest import org.springframework.data.domain.PageRequest
@@ -40,7 +45,7 @@ class ChapterController {
fun list( fun list(
@PathVariable("bookId") bookId: String, @PathVariable("bookId") bookId: String,
@RequestParam("page", defaultValue = "1") page: Int, @RequestParam("page", defaultValue = "1") page: Int,
@RequestParam("size", defaultValue = "10") size: Int @RequestParam("size", defaultValue = "10") size: Int,
): PageResponse<ViewItem> { ): PageResponse<ViewItem> {
val pageable = chapterRepository.findAll({ root, _, builder -> val pageable = chapterRepository.findAll({ root, _, builder ->
builder.equal(root.get<Book>("book").get<String>("bookId"), bookId) builder.equal(root.get<Book>("book").get<String>("bookId"), bookId)
@@ -101,11 +106,26 @@ class ChapterController {
lineRepository lineRepository
.findAll({ root, _, builder -> .findAll({ root, _, builder ->
builder.equal(root.get<Chapter>("chapter").get<String>("chapterId"), chapterId) builder.equal(root.get<Chapter>("chapter").get<String>("chapterId"), chapterId)
}, Sort.by(Sort.Direction.DESC, "sequence")) }, Sort.by(Sort.Direction.ASC, "sequence"))
.joinToString("\n") { "<p>${it.text}</p>" } .joinToString("\n") { "<p>${it.text}</p>" }
) )
} }
@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 @Transactional
@GetMapping("remove/{chapterId}") @GetMapping("remove/{chapterId}")
fun remove(@PathVariable("chapterId") chapterId: String) { fun remove(@PathVariable("chapterId") chapterId: String) {
@@ -128,6 +148,6 @@ class ChapterController {
data class ImportItem( data class ImportItem(
val override: Boolean = false, val override: Boolean = false,
val text: String val text: String,
) )
} }

View File

@@ -208,6 +208,12 @@
}, },
}, },
}, },
{
type: 'action',
label: '导出',
actionType: 'download',
api: '${base}/chapter/export/${chapterId}',
},
{ {
type: 'action', type: 'action',
label: '编辑正文', label: '编辑正文',

View File

@@ -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)
}
}