diff --git a/novel-admin/pom.xml b/novel-admin/pom.xml index bef14df..9cb9f59 100644 --- a/novel-admin/pom.xml +++ b/novel-admin/pom.xml @@ -5,7 +5,7 @@ com.java2nb novel-admin - 5.2.6 + 5.3.0 jar novel-admin diff --git a/novel-admin/src/main/java/com/java2nb/common/config/Constant.java b/novel-admin/src/main/java/com/java2nb/common/config/Constant.java index 91b8a57..f4636b7 100644 --- a/novel-admin/src/main/java/com/java2nb/common/config/Constant.java +++ b/novel-admin/src/main/java/com/java2nb/common/config/Constant.java @@ -15,7 +15,7 @@ public class Constant { //通知公告阅读状态-已读 public static int OA_NOTIFY_READ_YES = 1; //部门根节点id - public static Long DEPT_ROOT_ID = 0l; + public static Long DEPT_ROOT_ID = 0L; //缓存方式 public static String CACHE_TYPE_REDIS = "redis"; @@ -23,5 +23,6 @@ public class Constant { public static final String UPLOAD_FILES_PREFIX = "/files/"; + public static final String BOOK_IS_DOWNLOADING_KEY = "bookIsDownloading:"; } diff --git a/novel-admin/src/main/java/com/java2nb/common/utils/SortWhitelistUtil.java b/novel-admin/src/main/java/com/java2nb/common/utils/SortWhitelistUtil.java index a6acdec..94e415c 100644 --- a/novel-admin/src/main/java/com/java2nb/common/utils/SortWhitelistUtil.java +++ b/novel-admin/src/main/java/com/java2nb/common/utils/SortWhitelistUtil.java @@ -11,7 +11,7 @@ import java.util.Set; public class SortWhitelistUtil { // 白名单字段 - private static final Set ALLOWED_COLUMNS = Set.of("id", "name", "order_num"); + private static final Set ALLOWED_COLUMNS = Set.of("id", "name", "order_num","index_num"); // 白名单排序方式 private static final Set ALLOWED_ORDERS = Set.of("asc", "desc"); diff --git a/novel-admin/src/main/java/com/java2nb/novel/controller/BookController.java b/novel-admin/src/main/java/com/java2nb/novel/controller/BookController.java index 0d03432..371ac48 100644 --- a/novel-admin/src/main/java/com/java2nb/novel/controller/BookController.java +++ b/novel-admin/src/main/java/com/java2nb/novel/controller/BookController.java @@ -1,10 +1,24 @@ package com.java2nb.novel.controller; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import com.java2nb.common.config.Constant; +import com.java2nb.novel.domain.BookContentDO; +import com.java2nb.novel.domain.BookIndexDO; +import com.java2nb.novel.service.BookContentService; +import com.java2nb.novel.service.BookIndexService; +import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -22,6 +36,9 @@ import com.java2nb.common.utils.PageBean; import com.java2nb.common.utils.Query; import com.java2nb.common.utils.R; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + /** * 小说表 * @@ -30,12 +47,34 @@ import com.java2nb.common.utils.R; * @date 2020-12-01 03:49:46 */ +@Slf4j @Controller @RequestMapping("/novel/book") public class BookController { + @Autowired private BookService bookService; + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private BookIndexService bookIndexService; + + @Autowired + private BookContentService bookContentService; + + + private final Pattern PATTERN_BR = Pattern.compile("", Pattern.CASE_INSENSITIVE); + private final Pattern PATTERN_NBSP = Pattern.compile(" ", Pattern.CASE_INSENSITIVE); + private final Pattern PATTERN_ANCHOR_OPEN = Pattern.compile("]*>", Pattern.CASE_INSENSITIVE); + private final Pattern PATTERN_ANCHOR_CLOSE = Pattern.compile("", Pattern.CASE_INSENSITIVE); + private final Pattern PATTERN_DIV_OPEN = Pattern.compile("]*>", Pattern.CASE_INSENSITIVE); + private final Pattern PATTERN_DIV_CLOSE = Pattern.compile("", Pattern.CASE_INSENSITIVE); + private final Pattern PATTERN_EMPTY_PARAGRAPH_WITH_LINK = Pattern.compile("]*>[^<]*]*>[^<]*\\s*

", Pattern.CASE_INSENSITIVE); + private final Pattern PATTERN_P_OPEN = Pattern.compile("]*>", Pattern.CASE_INSENSITIVE); + private final Pattern PATTERN_P_CLOSE = Pattern.compile("

", Pattern.CASE_INSENSITIVE); + @GetMapping() @RequiresPermissions("novel:book:book") String Book() { @@ -66,7 +105,7 @@ public class BookController { @GetMapping("/edit/{id}") @RequiresPermissions("novel:book:edit") String edit(@PathVariable("id") Long id, Model model) { - BookDO book = bookService.get(id); + BookDO book = bookService.get(id); model.addAttribute("book", book); return "novel/book/edit"; } @@ -75,7 +114,7 @@ public class BookController { @GetMapping("/detail/{id}") @RequiresPermissions("novel:book:detail") String detail(@PathVariable("id") Long id, Model model) { - BookDO book = bookService.get(id); + BookDO book = bookService.get(id); model.addAttribute("book", book); return "novel/book/detail"; } @@ -87,7 +126,7 @@ public class BookController { @ResponseBody @PostMapping("/save") @RequiresPermissions("novel:book:add") - public R save( BookDO book) { + public R save(BookDO book) { if (bookService.save(book) > 0) { return R.ok(); } @@ -101,8 +140,8 @@ public class BookController { @ResponseBody @RequestMapping("/update") @RequiresPermissions("novel:book:edit") - public R update( BookDO book) { - bookService.update(book); + public R update(BookDO book) { + bookService.update(book); return R.ok(); } @@ -113,7 +152,7 @@ public class BookController { @PostMapping("/remove") @ResponseBody @RequiresPermissions("novel:book:remove") - public R remove( Long id) { + public R remove(Long id) { if (bookService.remove(id) > 0) { return R.ok(); } @@ -128,8 +167,83 @@ public class BookController { @ResponseBody @RequiresPermissions("novel:book:batchRemove") public R remove(@RequestParam("ids[]") Long[] ids) { - bookService.batchRemove(ids); + bookService.batchRemove(ids); return R.ok(); } + /** + * 小说下载 + */ + @RequestMapping(value = "/download") + public void download(@RequestParam("bookId") Long bookId, @RequestParam("bookName") String bookName, + HttpServletResponse resp) { + try { + OutputStream out = resp.getOutputStream(); + Boolean success = redisTemplate + .opsForValue() + .setIfAbsent(Constant.BOOK_IS_DOWNLOADING_KEY + bookId, "1", 10, TimeUnit.MINUTES); + if (Boolean.FALSE.equals(success)) { + resp.setContentType("text/html;charset=UTF-8"); + out.write("该小说正在下载中,请稍后重试!".getBytes(StandardCharsets.UTF_8)); + out.close(); + return; + } + //设置响应头,对文件进行url编码 + bookName = URLEncoder.encode(bookName, StandardCharsets.UTF_8); + //解决手机端不能下载附件的问题 + resp.setContentType("application/octet-stream"); + resp.setHeader("Content-Disposition", "attachment;filename=" + bookName + ".txt"); + + Map params = new HashMap<>(); + params.put("bookId", bookId); + params.put("sort", "index_num"); + params.put("order", "asc"); + params.put("limit", 9999); + params.put("offset", 0); + List bookIndexBigList = bookIndexService.list(params); + if (!bookIndexBigList.isEmpty()) { + List> bookIndexSmallList = bookIndexBigList.stream().collect( + Collectors.groupingBy(item -> bookIndexBigList.indexOf(item) / 100)).values().stream().toList(); + for (List bookIndexList : bookIndexSmallList) { + // 获取集合中所有的ID + List bookIndexIds = bookIndexList.stream().map(BookIndexDO::getId).toList(); + List bookContentList = bookContentService.listByIndexIds(bookIndexIds); + Map bookContentMap = bookContentList.stream() + .collect(Collectors.toMap(BookContentDO::getIndexId, BookContentDO::getContent)); + for (BookIndexDO bookIndex : bookIndexList) { + String indexName = bookIndex.getIndexName(); + if (indexName != null) { + String content = bookContentMap.get(bookIndex.getId()); + out.write(indexName.getBytes(StandardCharsets.UTF_8)); + out.write("\r\n".getBytes(StandardCharsets.UTF_8)); + content = PATTERN_BR.matcher(content).replaceAll("\r\n"); + content = PATTERN_NBSP.matcher(content).replaceAll(" "); + content = PATTERN_ANCHOR_OPEN.matcher(content).replaceAll(""); + content = PATTERN_ANCHOR_CLOSE.matcher(content).replaceAll(""); + content = PATTERN_DIV_OPEN.matcher(content).replaceAll(""); + content = PATTERN_DIV_CLOSE.matcher(content).replaceAll(""); + content = PATTERN_EMPTY_PARAGRAPH_WITH_LINK.matcher(content).replaceAll(""); + content = PATTERN_P_OPEN.matcher(content).replaceAll(""); + content = PATTERN_P_CLOSE.matcher(content).replaceAll("\r\n"); + out.write(content.getBytes(StandardCharsets.UTF_8)); + out.write("\r\n".getBytes(StandardCharsets.UTF_8)); + out.write("\r\n".getBytes(StandardCharsets.UTF_8)); + out.flush(); + } + } + } + + } + + out.close(); + + + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + redisTemplate.delete(Constant.BOOK_IS_DOWNLOADING_KEY + bookId); + } + + } + } diff --git a/novel-admin/src/main/java/com/java2nb/novel/dao/BookContentDao.java b/novel-admin/src/main/java/com/java2nb/novel/dao/BookContentDao.java index d070322..d520f05 100644 --- a/novel-admin/src/main/java/com/java2nb/novel/dao/BookContentDao.java +++ b/novel-admin/src/main/java/com/java2nb/novel/dao/BookContentDao.java @@ -4,6 +4,7 @@ import com.java2nb.common.annotation.SanitizeMap; import com.java2nb.novel.domain.BookContentDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; @@ -33,4 +34,6 @@ public interface BookContentDao { int batchRemove(Long[] ids); int removeByIndexIds(Long[] indexIds); + + List listByIndexIds(@Param("indexIds") List indexIds); } diff --git a/novel-admin/src/main/java/com/java2nb/novel/service/BookContentService.java b/novel-admin/src/main/java/com/java2nb/novel/service/BookContentService.java index 83e7353..f2a3501 100644 --- a/novel-admin/src/main/java/com/java2nb/novel/service/BookContentService.java +++ b/novel-admin/src/main/java/com/java2nb/novel/service/BookContentService.java @@ -27,4 +27,6 @@ public interface BookContentService { int remove(Long id); int batchRemove(Long[] ids); + + List listByIndexIds(List indexIds); } diff --git a/novel-admin/src/main/java/com/java2nb/novel/service/impl/BookContentServiceImpl.java b/novel-admin/src/main/java/com/java2nb/novel/service/impl/BookContentServiceImpl.java index 8c6bbd4..0adebae 100644 --- a/novel-admin/src/main/java/com/java2nb/novel/service/impl/BookContentServiceImpl.java +++ b/novel-admin/src/main/java/com/java2nb/novel/service/impl/BookContentServiceImpl.java @@ -1,5 +1,6 @@ package com.java2nb.novel.service.impl; +import org.apache.ibatis.annotations.Param; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -51,5 +52,10 @@ public class BookContentServiceImpl implements BookContentService { public int batchRemove(Long[] ids){ return bookContentDao.batchRemove(ids); } - + + @Override + public List listByIndexIds(List indexIds) { + return bookContentDao.listByIndexIds(indexIds); + } + } diff --git a/novel-admin/src/main/resources/mybatis/novel/BookContentMapper.xml b/novel-admin/src/main/resources/mybatis/novel/BookContentMapper.xml index c6de681..7652d25 100644 --- a/novel-admin/src/main/resources/mybatis/novel/BookContentMapper.xml +++ b/novel-admin/src/main/resources/mybatis/novel/BookContentMapper.xml @@ -38,6 +38,17 @@ + + insert into book_content (`id`, diff --git a/novel-admin/src/main/resources/mybatis/novel/BookSettingMapper.xml b/novel-admin/src/main/resources/mybatis/novel/BookSettingMapper.xml index 75fcd33..881db56 100644 --- a/novel-admin/src/main/resources/mybatis/novel/BookSettingMapper.xml +++ b/novel-admin/src/main/resources/mybatis/novel/BookSettingMapper.xml @@ -43,7 +43,7 @@