Compare commits
3 Commits
026c72c4de
...
46b1aa8853
| Author | SHA1 | Date | |
|---|---|---|---|
| 46b1aa8853 | |||
| cf5f7470c6 | |||
| 9e9f65da76 |
8
.idea/GitCommitMessageStorage.xml
generated
Normal file
8
.idea/GitCommitMessageStorage.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitCommitMessageStorage">
|
||||
<option name="messageStorage">
|
||||
<MessageStorage />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
16
.idea/csv-editor.xml
generated
Normal file
16
.idea/csv-editor.xml
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CsvFileAttributes">
|
||||
<option name="attributeMap">
|
||||
<map>
|
||||
<entry key="/dumpDataPreview">
|
||||
<value>
|
||||
<Attribute>
|
||||
<option name="separator" value="," />
|
||||
</Attribute>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -9,6 +9,48 @@ import {
|
||||
time,
|
||||
} from '../../util/amis.tsx'
|
||||
|
||||
const detailDialog = (bookId: string | undefined) => {
|
||||
return {
|
||||
title: '添加书架',
|
||||
size: 'md',
|
||||
body: {
|
||||
debug: commonInfo.debug,
|
||||
type: 'form',
|
||||
api: `${commonInfo.baseUrl}/chapter/save`,
|
||||
initApi: `${commonInfo.baseUrl}/chapter/detail/\${id}`,
|
||||
initFetchOn: '${id}',
|
||||
...horizontalFormOptions(),
|
||||
canAccessSuperData: false,
|
||||
body: [
|
||||
{
|
||||
type: 'hidden',
|
||||
name: 'bookId',
|
||||
value: bookId,
|
||||
},
|
||||
{
|
||||
type: 'input-number',
|
||||
name: 'sequence',
|
||||
label: '序号',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
label: '名称',
|
||||
required: true,
|
||||
clearable: true,
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
name: 'description',
|
||||
label: '描述',
|
||||
clearable: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function Book() {
|
||||
const navigate = useNavigate()
|
||||
const {id} = useParams()
|
||||
@@ -49,11 +91,11 @@ function Book() {
|
||||
sort: [
|
||||
{
|
||||
column: 'sequence',
|
||||
direction: 'ASC',
|
||||
direction: 'DESC',
|
||||
},
|
||||
{
|
||||
column: 'modifiedTime',
|
||||
direction: 'DESC',
|
||||
direction: 'ASC',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -70,7 +112,9 @@ function Book() {
|
||||
actionType: 'ajax',
|
||||
tooltip: '序号重排',
|
||||
tooltipPlacement: 'top',
|
||||
api: `get:${commonInfo.baseUrl}/chapter/generate_sequence`
|
||||
api: `get:${commonInfo.baseUrl}/chapter/generate_sequence`,
|
||||
confirmText: '确认重排序号?',
|
||||
confirmTitle: '序号重拍',
|
||||
},
|
||||
{
|
||||
type: 'action',
|
||||
@@ -142,7 +186,7 @@ function Book() {
|
||||
actionType: 'dialog',
|
||||
dialog: detailDialog(),
|
||||
},*/
|
||||
]
|
||||
],
|
||||
),
|
||||
columns: [
|
||||
{
|
||||
@@ -193,14 +237,14 @@ function Book() {
|
||||
},
|
||||
},
|
||||
},
|
||||
/*{
|
||||
{
|
||||
type: 'action',
|
||||
label: '修改',
|
||||
level: 'link',
|
||||
size: 'sm',
|
||||
actionType: 'dialog',
|
||||
dialog: detailDialog(),
|
||||
},*/
|
||||
dialog: detailDialog(id),
|
||||
},
|
||||
{
|
||||
className: 'text-danger btn-deleted',
|
||||
type: 'action',
|
||||
|
||||
@@ -96,7 +96,7 @@ function Bookshelf() {
|
||||
{
|
||||
name: 'name',
|
||||
label: '书名',
|
||||
width: 150,
|
||||
width: 120,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
@@ -112,7 +112,7 @@ function Bookshelf() {
|
||||
{
|
||||
name: 'source',
|
||||
label: '来源',
|
||||
width: 150,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
name: 'tags',
|
||||
@@ -159,6 +159,14 @@ function Bookshelf() {
|
||||
actionType: 'dialog',
|
||||
dialog: detailDialog(),
|
||||
},
|
||||
{
|
||||
type: 'action',
|
||||
label: '导出',
|
||||
level: 'link',
|
||||
size: 'sm',
|
||||
actionType: 'download',
|
||||
api: `${commonInfo.baseUrl}/book/export/\${id}`,
|
||||
},
|
||||
{
|
||||
className: 'text-danger btn-deleted',
|
||||
type: 'action',
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import React from 'react'
|
||||
import {useParams} from 'react-router'
|
||||
import {amisRender, commonInfo, crudCommonOptions, horizontalFormOptions, paginationTemplate} from '../../util/amis.tsx'
|
||||
import {
|
||||
amisRender,
|
||||
commonInfo,
|
||||
crudCommonOptions,
|
||||
horizontalFormOptions,
|
||||
paginationTemplate,
|
||||
readOnlyDialogOptions,
|
||||
} from '../../util/amis.tsx'
|
||||
|
||||
function Chapter() {
|
||||
// const navigate = useNavigate()
|
||||
@@ -52,19 +59,46 @@ function Chapter() {
|
||||
},
|
||||
...crudCommonOptions(),
|
||||
...paginationTemplate(
|
||||
undefined,
|
||||
50,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
type: 'action',
|
||||
label: '',
|
||||
icon: 'fa fa-rotate-right',
|
||||
icon: 'fa fa-book-open-reader',
|
||||
actionType: 'ajax',
|
||||
tooltip: '序号重排',
|
||||
tooltipPlacement: 'top',
|
||||
api: `get:${commonInfo.baseUrl}/line/generate_sequence/${id}`
|
||||
}
|
||||
]
|
||||
api: `get:${commonInfo.baseUrl}/line/generate_sequence/${id}`,
|
||||
confirmText: '确认重排序号?',
|
||||
confirmTitle: '序号重拍',
|
||||
},
|
||||
{
|
||||
type: 'action',
|
||||
label: '',
|
||||
icon: 'fa fa-glasses',
|
||||
actionType: 'dialog',
|
||||
tooltip: '全文阅读',
|
||||
tooltipPlacement: 'top',
|
||||
dialog: {
|
||||
title: '全文查看',
|
||||
size: 'md',
|
||||
...readOnlyDialogOptions(),
|
||||
body: {
|
||||
type: 'service',
|
||||
size: 'none',
|
||||
api: `${commonInfo.baseUrl}/chapter/content/${id}`,
|
||||
body: {
|
||||
type: 'markdown',
|
||||
value: '${detail}',
|
||||
options: {
|
||||
breaks: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
),
|
||||
columns: [
|
||||
{
|
||||
|
||||
5
pom.xml
5
pom.xml
@@ -58,6 +58,11 @@
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
package com.lanyuanxiaoyao.bookstore;
|
||||
|
||||
import cn.hutool.core.lang.Tuple;
|
||||
import cn.hutool.core.text.csv.CsvUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.blinkfox.fenix.EnableFenix;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.Chapter;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.Line;
|
||||
import com.lanyuanxiaoyao.bookstore.service.BookService;
|
||||
import com.lanyuanxiaoyao.bookstore.service.ChapterService;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 启动类
|
||||
@@ -33,44 +20,4 @@ public class BookStoreApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BookStoreApplication.class, args);
|
||||
}
|
||||
|
||||
@Resource
|
||||
private BookService bookService;
|
||||
@Resource
|
||||
private ChapterService chapterService;
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
// @EventListener(ApplicationReadyEvent.class)
|
||||
public void loadOldData() throws FileNotFoundException {
|
||||
var reader = CsvUtil.getReader(new FileReader("C:\\Users\\lanyuanxiaoyao\\Result_6.csv"));
|
||||
var rows = reader.stream()
|
||||
.map(row -> new Row(Long.parseLong(row.get(0)), row.get(1), Long.parseLong(row.get(2)), row.get(3)))
|
||||
.toList();
|
||||
var book = bookService.detailOrThrow(3602572744994816L);
|
||||
rows.stream()
|
||||
.map(row -> new Tuple(row.chapterSequence(), row.chapterTitle()))
|
||||
.distinct()
|
||||
.forEach(tuple -> {
|
||||
var chapter = new Chapter();
|
||||
chapter.setSequence(NumberUtil.toDouble(tuple.get(0)));
|
||||
chapter.setName(tuple.get(1));
|
||||
chapter.setBook(book);
|
||||
|
||||
var lines = rows.stream()
|
||||
.filter(row -> ObjectUtil.equals(row.chapterSequence(), tuple.get(0)) && ObjectUtil.equals(row.chapterTitle(), tuple.get(1)))
|
||||
.map(row -> {
|
||||
var line = new Line();
|
||||
line.setSequence(NumberUtil.toDouble(row.lineSequence()));
|
||||
line.setText(row.lineText());
|
||||
line.setChapter(chapter);
|
||||
return line;
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
chapter.setContent(lines);
|
||||
chapterService.save(chapter);
|
||||
});
|
||||
}
|
||||
|
||||
public record Row(long chapterSequence, String chapterTitle, long lineSequence, String lineText) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
package com.lanyuanxiaoyao.bookstore.controller;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.Book;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.Chapter;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.Line;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.vo.Option;
|
||||
import com.lanyuanxiaoyao.bookstore.service.BookService;
|
||||
import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -29,6 +39,39 @@ public class BookController extends SimpleControllerSupport<Book, BookController
|
||||
return bookService.tags();
|
||||
}
|
||||
|
||||
@GetMapping("export/{book_id}")
|
||||
public void export(@PathVariable("book_id") Long bookId, HttpServletResponse response) throws IOException {
|
||||
var book = bookService.detailOrThrow(bookId);
|
||||
var result = new StringBuilder();
|
||||
result.append(StrUtil.format("""
|
||||
书名:{}
|
||||
作者:{}
|
||||
简介:{}
|
||||
来源:{}
|
||||
|
||||
""", book.getName(), book.getAuthor(), book.getDescription(), book.getSource()));
|
||||
book.getChapters()
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(Chapter::getSequence))
|
||||
.forEach(chapter -> {
|
||||
result.append(StrUtil.format("第{}章 {}", chapter.getSequence(), chapter.getName()));
|
||||
result.append("\n\n");
|
||||
chapter.getContent()
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(Line::getSequence))
|
||||
.forEach(line -> {
|
||||
result.append(line.getText());
|
||||
result.append("\n");
|
||||
});
|
||||
result.append("\n\n");
|
||||
});
|
||||
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(StrUtil.format("{}.txt", book.getName()))));
|
||||
IoUtil.copy(new ByteArrayInputStream(result.toString().getBytes()), response.getOutputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<SaveItem, Book> saveItemMapper() {
|
||||
return item -> {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.lanyuanxiaoyao.bookstore.controller;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.Chapter;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.Line;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.vo.Option;
|
||||
import com.lanyuanxiaoyao.bookstore.service.BookService;
|
||||
import com.lanyuanxiaoyao.bookstore.service.ChapterService;
|
||||
@@ -8,11 +10,15 @@ import com.lanyuanxiaoyao.bookstore.service.LineService;
|
||||
import com.lanyuanxiaoyao.service.template.controller.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -43,7 +49,7 @@ public class ChapterController extends SimpleControllerSupport<Chapter, ChapterC
|
||||
public GlobalResponse<Object> saveWithContent(@RequestBody SaveWithContentItem item) {
|
||||
if (SaveWithContentItem.Mode.CREATE.equals(item.mode())) {
|
||||
var chapter = new Chapter();
|
||||
chapter.setSequence(chapterService.latestSequence(item.bookId()));
|
||||
chapter.setSequence(chapterService.latestSequence(item.bookId()) + 1);
|
||||
chapter.setName(item.name());
|
||||
chapter.setDescription(item.description());
|
||||
chapter.setBook(bookService.detailOrThrow(item.bookId()));
|
||||
@@ -62,6 +68,18 @@ public class ChapterController extends SimpleControllerSupport<Chapter, ChapterC
|
||||
return GlobalResponse.responseSuccess();
|
||||
}
|
||||
|
||||
@GetMapping("content/{chapter_id}")
|
||||
public GlobalResponse<Map<String, Object>> content(@PathVariable("chapter_id") Long chapterId) {
|
||||
var chapter = chapterService.detailOrThrow(chapterId);
|
||||
return GlobalResponse.responseDetailData(
|
||||
chapter.getContent()
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(Line::getSequence))
|
||||
.map(Line::getText)
|
||||
.collect(Collectors.joining("\n"))
|
||||
);
|
||||
}
|
||||
|
||||
@GetMapping("generate_sequence")
|
||||
public GlobalResponse<Object> generateSequence() {
|
||||
chapterService.generateSequence();
|
||||
@@ -73,7 +91,7 @@ public class ChapterController extends SimpleControllerSupport<Chapter, ChapterC
|
||||
return item -> {
|
||||
var chapter = new Chapter();
|
||||
chapter.setId(item.id());
|
||||
chapter.setSequence(chapterService.latestSequence(item.bookId()));
|
||||
chapter.setSequence(ObjectUtil.defaultIfNull(item.sequence(), chapterService.latestSequence(item.bookId()) + 1));
|
||||
chapter.setName(item.name());
|
||||
chapter.setDescription(item.description());
|
||||
chapter.setBook(bookService.detailOrThrow(item.bookId()));
|
||||
@@ -105,6 +123,7 @@ public class ChapterController extends SimpleControllerSupport<Chapter, ChapterC
|
||||
public record SaveItem(
|
||||
Long id,
|
||||
Long bookId,
|
||||
Double sequence,
|
||||
String name,
|
||||
String description
|
||||
) {
|
||||
@@ -118,10 +137,6 @@ public class ChapterController extends SimpleControllerSupport<Chapter, ChapterC
|
||||
Mode mode,
|
||||
String content
|
||||
) {
|
||||
public SaveItem toSaveItem() {
|
||||
return new SaveItem(null, bookId, name, description);
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
CREATE, OVERRIDE
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class Line extends SimpleEntity {
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
@ToString.Exclude
|
||||
@Column(nullable = false)
|
||||
@Column(nullable = false, columnDefinition = "longtext")
|
||||
private String text;
|
||||
|
||||
@ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
|
||||
|
||||
@@ -2,12 +2,12 @@ package com.lanyuanxiaoyao.bookstore.service;
|
||||
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.Chapter;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.QChapter;
|
||||
import com.lanyuanxiaoyao.bookstore.entity.vo.Option;
|
||||
import com.lanyuanxiaoyao.bookstore.repository.ChapterRepository;
|
||||
import com.lanyuanxiaoyao.service.template.service.SimpleServiceSupport;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@@ -32,7 +32,7 @@ public class ChapterService extends SimpleServiceSupport<Chapter> {
|
||||
}
|
||||
|
||||
public void generateSequence() {
|
||||
var chapters = chapterRepository.findAll(Sort.by(Sort.Direction.ASC, Chapter.Fields.sequence));
|
||||
var chapters = chapterRepository.findAll(QChapter.chapter.sequence.asc());
|
||||
for (int index = 0; index < chapters.size(); index++) {
|
||||
chapters.get(index).setSequence(NumberUtil.toDouble(index));
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ spring:
|
||||
async:
|
||||
request-timeout: 3600000
|
||||
datasource:
|
||||
url: jdbc:h2:file:./bookstore;DB_CLOSE_ON_EXIT=TRUE
|
||||
url: jdbc:mysql://mysql.lanyuanxiaoyao.com:43780/bookstore?useUnicode=true&characterEncoding=utf8&useSSL=false
|
||||
username: bookstore
|
||||
password: bookstore
|
||||
driver-class-name: org.h2.Driver
|
||||
password: EzSn+RZ*x2&fHFh9kC+H
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
jpa:
|
||||
generate-ddl: false
|
||||
hibernate:
|
||||
|
||||
Reference in New Issue
Block a user