diff --git a/doc/es/book.http b/doc/es/book.http
index e80fd3d..45038d3 100644
--- a/doc/es/book.http
+++ b/doc/es/book.http
@@ -38,7 +38,7 @@ PUT /book
"analyzer": "ik_smart"
},
"lastChapterUpdateTime" : {
- "type": "keyword"
+ "type": "long"
},
"picUrl" : {
"type" : "keyword",
diff --git a/pom.xml b/pom.xml
index 347f5e1..0f462b2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -109,10 +109,6 @@
com.fasterxml.jackson.core
jackson-databind
-
- com.fasterxml.jackson.datatype
- jackson-datatype-jsr310
-
mysql
diff --git a/src/main/java/io/github/xxyopen/novel/controller/front/BookController.java b/src/main/java/io/github/xxyopen/novel/controller/front/BookController.java
index 79fdf97..46327cd 100644
--- a/src/main/java/io/github/xxyopen/novel/controller/front/BookController.java
+++ b/src/main/java/io/github/xxyopen/novel/controller/front/BookController.java
@@ -6,6 +6,7 @@ import io.github.xxyopen.novel.core.common.resp.RestResp;
import io.github.xxyopen.novel.dto.req.BookSearchReqDto;
import io.github.xxyopen.novel.dto.resp.*;
import io.github.xxyopen.novel.service.BookService;
+import io.github.xxyopen.novel.service.SearchService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@@ -25,6 +26,8 @@ public class BookController {
private final BookService bookService;
+ private final SearchService searchService;
+
/**
* 小说分类列表查询接口
*/
@@ -38,7 +41,7 @@ public class BookController {
*/
@GetMapping("search_list")
public RestResp> searchBooks(BookSearchReqDto condition) {
- return bookService.searchBooks(condition);
+ return searchService.searchBooks(condition);
}
/**
diff --git a/src/main/java/io/github/xxyopen/novel/core/task/BookToEsTask.java b/src/main/java/io/github/xxyopen/novel/core/task/BookToEsTask.java
index 3c9be46..4a64be1 100644
--- a/src/main/java/io/github/xxyopen/novel/core/task/BookToEsTask.java
+++ b/src/main/java/io/github/xxyopen/novel/core/task/BookToEsTask.java
@@ -1,6 +1,7 @@
package io.github.xxyopen.novel.core.task;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch.core.BulkRequest;
import co.elastic.clients.elasticsearch.core.BulkResponse;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
@@ -17,6 +18,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
+import java.time.ZoneOffset;
import java.util.List;
/**
@@ -65,7 +67,7 @@ public class BookToEsTask {
.id(book.getId().toString())
.document(esBook)
)
- );
+ ).timeout(Time.of(t -> t.time("10s")));
maxId = book.getId();
}
@@ -103,7 +105,8 @@ public class BookToEsTask {
.workDirection(book.getWorkDirection())
.lastChapterId(book.getLastChapterId())
.lastChapterName(book.getLastChapterName())
- .lastChapterUpdateTime(book.getLastChapterUpdateTime())
+ .lastChapterUpdateTime(book.getLastChapterUpdateTime()
+ .toInstant(ZoneOffset.ofHours(8)).toEpochMilli())
.build();
}
}
diff --git a/src/main/java/io/github/xxyopen/novel/dto/es/EsBookDto.java b/src/main/java/io/github/xxyopen/novel/dto/es/EsBookDto.java
index 7d77fea..3a714bc 100644
--- a/src/main/java/io/github/xxyopen/novel/dto/es/EsBookDto.java
+++ b/src/main/java/io/github/xxyopen/novel/dto/es/EsBookDto.java
@@ -1,13 +1,9 @@
package io.github.xxyopen.novel.dto.es;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
-import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
-
-import java.time.LocalDateTime;
+import lombok.NoArgsConstructor;
/**
* Elasticsearch 存储小说 DTO
@@ -15,6 +11,8 @@ import java.time.LocalDateTime;
* @date 2022/5/23
*/
@Data
+@NoArgsConstructor
+@AllArgsConstructor
@Builder
public class EsBookDto {
@@ -96,9 +94,7 @@ public class EsBookDto {
/**
* 最新章节更新时间
*/
- @JsonDeserialize(using = LocalDateTimeDeserializer.class)
- @JsonSerialize(using = LocalDateTimeSerializer.class)
- private LocalDateTime lastChapterUpdateTime;
+ private Long lastChapterUpdateTime;
/**
* 是否收费;1-收费 0-免费
diff --git a/src/main/java/io/github/xxyopen/novel/dto/resp/BookInfoRespDto.java b/src/main/java/io/github/xxyopen/novel/dto/resp/BookInfoRespDto.java
index 29fc925..55ba809 100644
--- a/src/main/java/io/github/xxyopen/novel/dto/resp/BookInfoRespDto.java
+++ b/src/main/java/io/github/xxyopen/novel/dto/resp/BookInfoRespDto.java
@@ -1,7 +1,6 @@
package io.github.xxyopen.novel.dto.resp;
-import lombok.Builder;
-import lombok.Data;
+import lombok.*;
/**
* 小说信息 响应DTO
@@ -10,6 +9,8 @@ import lombok.Data;
* @date 2022/5/15
*/
@Data
+@NoArgsConstructor
+@AllArgsConstructor
@Builder
public class BookInfoRespDto {
diff --git a/src/main/java/io/github/xxyopen/novel/service/BookService.java b/src/main/java/io/github/xxyopen/novel/service/BookService.java
index 634c422..98e9059 100644
--- a/src/main/java/io/github/xxyopen/novel/service/BookService.java
+++ b/src/main/java/io/github/xxyopen/novel/service/BookService.java
@@ -1,9 +1,7 @@
package io.github.xxyopen.novel.service;
-import io.github.xxyopen.novel.core.common.resp.PageRespDto;
import io.github.xxyopen.novel.core.common.resp.RestResp;
import io.github.xxyopen.novel.dto.req.BookAddReqDto;
-import io.github.xxyopen.novel.dto.req.BookSearchReqDto;
import io.github.xxyopen.novel.dto.req.ChapterAddReqDto;
import io.github.xxyopen.novel.dto.req.UserCommentReqDto;
import io.github.xxyopen.novel.dto.resp.*;
@@ -19,14 +17,6 @@ import java.util.List;
*/
public interface BookService {
- /**
- * 小说搜索
- *
- * @param condition 搜索条件
- * @return 搜索结果
- */
- RestResp> searchBooks(BookSearchReqDto condition);
-
/**
* 小说点击榜查询
*
diff --git a/src/main/java/io/github/xxyopen/novel/service/SearchService.java b/src/main/java/io/github/xxyopen/novel/service/SearchService.java
new file mode 100644
index 0000000..6707ce8
--- /dev/null
+++ b/src/main/java/io/github/xxyopen/novel/service/SearchService.java
@@ -0,0 +1,25 @@
+package io.github.xxyopen.novel.service;
+
+import io.github.xxyopen.novel.core.common.resp.PageRespDto;
+import io.github.xxyopen.novel.core.common.resp.RestResp;
+import io.github.xxyopen.novel.dto.req.BookSearchReqDto;
+import io.github.xxyopen.novel.dto.resp.BookInfoRespDto;
+
+/**
+ * 搜索 服务类
+ *
+ * @author xiongxiaoyang
+ * @date 2022/5/23
+ */
+public interface SearchService {
+
+ /**
+ * 小说搜索
+ *
+ * @param condition 搜索条件
+ * @return 搜索结果
+ */
+ RestResp> searchBooks(BookSearchReqDto condition);
+
+
+}
diff --git a/src/main/java/io/github/xxyopen/novel/service/impl/BookServiceImpl.java b/src/main/java/io/github/xxyopen/novel/service/impl/BookServiceImpl.java
index 073180a..c855bb1 100644
--- a/src/main/java/io/github/xxyopen/novel/service/impl/BookServiceImpl.java
+++ b/src/main/java/io/github/xxyopen/novel/service/impl/BookServiceImpl.java
@@ -1,10 +1,8 @@
package io.github.xxyopen.novel.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.github.xxyopen.novel.core.auth.UserHolder;
import io.github.xxyopen.novel.core.common.constant.ErrorCodeEnum;
-import io.github.xxyopen.novel.core.common.resp.PageRespDto;
import io.github.xxyopen.novel.core.common.resp.RestResp;
import io.github.xxyopen.novel.core.constant.DatabaseConsts;
import io.github.xxyopen.novel.dao.entity.*;
@@ -14,7 +12,6 @@ import io.github.xxyopen.novel.dao.mapper.BookContentMapper;
import io.github.xxyopen.novel.dao.mapper.BookInfoMapper;
import io.github.xxyopen.novel.dto.AuthorInfoDto;
import io.github.xxyopen.novel.dto.req.BookAddReqDto;
-import io.github.xxyopen.novel.dto.req.BookSearchReqDto;
import io.github.xxyopen.novel.dto.req.ChapterAddReqDto;
import io.github.xxyopen.novel.dto.req.UserCommentReqDto;
import io.github.xxyopen.novel.dto.resp.*;
@@ -67,25 +64,6 @@ public class BookServiceImpl implements BookService {
private static final Integer REC_BOOK_COUNT = 4;
- @Override
- public RestResp> searchBooks(BookSearchReqDto condition) {
- Page page = new Page<>();
- page.setCurrent(condition.getPageNum());
- page.setSize(condition.getPageSize());
- List bookInfos = bookInfoMapper.searchBooks(page, condition);
- return RestResp.ok(PageRespDto.of(condition.getPageNum(), condition.getPageSize(), page.getTotal()
- , bookInfos.stream().map(v -> BookInfoRespDto.builder()
- .id(v.getId())
- .bookName(v.getBookName())
- .categoryId(v.getCategoryId())
- .categoryName(v.getCategoryName())
- .authorId(v.getAuthorId())
- .authorName(v.getAuthorName())
- .wordCount(v.getWordCount())
- .lastChapterName(v.getLastChapterName())
- .build()).toList()));
- }
-
@Override
public RestResp> listVisitRankBooks() {
return RestResp.ok(bookRankCacheManager.listVisitRankBooks());
diff --git a/src/main/java/io/github/xxyopen/novel/service/impl/DbSearchServiceImpl.java b/src/main/java/io/github/xxyopen/novel/service/impl/DbSearchServiceImpl.java
new file mode 100644
index 0000000..4418344
--- /dev/null
+++ b/src/main/java/io/github/xxyopen/novel/service/impl/DbSearchServiceImpl.java
@@ -0,0 +1,51 @@
+package io.github.xxyopen.novel.service.impl;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.github.xxyopen.novel.core.common.resp.PageRespDto;
+import io.github.xxyopen.novel.core.common.resp.RestResp;
+import io.github.xxyopen.novel.dao.entity.BookInfo;
+import io.github.xxyopen.novel.dao.mapper.BookInfoMapper;
+import io.github.xxyopen.novel.dto.req.BookSearchReqDto;
+import io.github.xxyopen.novel.dto.resp.BookInfoRespDto;
+import io.github.xxyopen.novel.service.SearchService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 数据库搜索 服务实现类
+ *
+ * @author xiongxiaoyang
+ * @date 2022/5/23
+ */
+@ConditionalOnProperty(prefix = "spring.elasticsearch", name = "enable", havingValue = "false")
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class DbSearchServiceImpl implements SearchService {
+
+ private final BookInfoMapper bookInfoMapper;
+
+ @Override
+ public RestResp> searchBooks(BookSearchReqDto condition) {
+ Page page = new Page<>();
+ page.setCurrent(condition.getPageNum());
+ page.setSize(condition.getPageSize());
+ List bookInfos = bookInfoMapper.searchBooks(page, condition);
+ return RestResp.ok(PageRespDto.of(condition.getPageNum(), condition.getPageSize(), page.getTotal()
+ , bookInfos.stream().map(v -> BookInfoRespDto.builder()
+ .id(v.getId())
+ .bookName(v.getBookName())
+ .categoryId(v.getCategoryId())
+ .categoryName(v.getCategoryName())
+ .authorId(v.getAuthorId())
+ .authorName(v.getAuthorName())
+ .wordCount(v.getWordCount())
+ .lastChapterName(v.getLastChapterName())
+ .build()).toList()));
+ }
+
+}
diff --git a/src/main/java/io/github/xxyopen/novel/service/impl/EsSearchServiceImpl.java b/src/main/java/io/github/xxyopen/novel/service/impl/EsSearchServiceImpl.java
new file mode 100644
index 0000000..3eec0d0
--- /dev/null
+++ b/src/main/java/io/github/xxyopen/novel/service/impl/EsSearchServiceImpl.java
@@ -0,0 +1,141 @@
+package io.github.xxyopen.novel.service.impl;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.SortOrder;
+import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
+import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
+import co.elastic.clients.elasticsearch.core.SearchRequest;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+import co.elastic.clients.elasticsearch.core.search.TotalHits;
+import co.elastic.clients.json.JsonData;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import io.github.xxyopen.novel.core.common.resp.PageRespDto;
+import io.github.xxyopen.novel.core.common.resp.RestResp;
+import io.github.xxyopen.novel.core.constant.EsConsts;
+import io.github.xxyopen.novel.dto.es.EsBookDto;
+import io.github.xxyopen.novel.dto.req.BookSearchReqDto;
+import io.github.xxyopen.novel.dto.resp.BookInfoRespDto;
+import io.github.xxyopen.novel.service.SearchService;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Elasticsearch 搜索 服务实现类
+ *
+ * @author xiongxiaoyang
+ * @date 2022/5/23
+ */
+@ConditionalOnProperty(prefix = "spring.elasticsearch", name = "enable", havingValue = "true")
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class EsSearchServiceImpl implements SearchService {
+
+ private final ElasticsearchClient esClient;
+
+ @SneakyThrows
+ @Override
+ public RestResp> searchBooks(BookSearchReqDto condition) {
+
+ SearchResponse response = esClient.search(s -> {
+
+ SearchRequest.Builder searchBuilder = s.index(EsConsts.IndexEnum.BOOK.getName());
+ buildSearchCondition(condition, searchBuilder);
+
+ searchBuilder.sort(o ->
+ o.field(f -> f.field(StringUtils
+ .underlineToCamel(condition.getSort().split(" ")[0]))
+ .order(SortOrder.Desc))
+ )
+ .from((condition.getPageNum() - 1) * condition.getPageSize())
+ .size(condition.getPageSize());
+ return searchBuilder;
+ },
+ EsBookDto.class
+ );
+
+ TotalHits total = response.hits().total();
+
+ List list = new ArrayList<>();
+ List> hits = response.hits().hits();
+ for (Hit hit : hits) {
+ EsBookDto book = hit.source();
+ assert book != null;
+ list.add(BookInfoRespDto.builder()
+ .id(book.getId())
+ .bookName(book.getBookName())
+ .categoryId(book.getCategoryId())
+ .categoryName(book.getCategoryName())
+ .authorId(book.getAuthorId())
+ .authorName(book.getAuthorName())
+ .wordCount(book.getWordCount())
+ .lastChapterName(book.getLastChapterName())
+ .build());
+ }
+ assert total != null;
+ return RestResp.ok(PageRespDto.of(condition.getPageNum(), condition.getPageSize(), total.value(), list));
+ }
+
+ private void buildSearchCondition(BookSearchReqDto condition, SearchRequest.Builder searchBuilder) {
+ if (!StringUtils.isBlank(condition.getKeyword())) {
+ searchBuilder.query(q -> q.match(t -> t
+ .field("bookName")
+ .query(condition.getKeyword())
+ .boost(2.0f)
+ .field("authorName")
+ .query(condition.getKeyword())
+ .boost(1.8f)
+ //.field("categoryName")
+ //.query(condition.getKeyword())
+ //.boost(1.0f)
+ //.field("bookDesc")
+ //.query(condition.getKeyword())
+ //.boost(0.1f)
+ )
+ );
+ }
+
+ if (Objects.nonNull(condition.getWorkDirection())) {
+ searchBuilder.query(MatchQuery.of(m -> m
+ .field("workDirection")
+ .query(condition.getWorkDirection())
+ )._toQuery());
+ }
+
+ if (Objects.nonNull(condition.getCategoryId())) {
+ searchBuilder.query(MatchQuery.of(m -> m
+ .field("categoryId")
+ .query(condition.getCategoryId())
+ )._toQuery());
+ }
+
+ if (Objects.nonNull(condition.getWordCountMin())) {
+ searchBuilder.query(RangeQuery.of(m -> m
+ .field("wordCount")
+ .gte(JsonData.of(condition.getWordCountMin()))
+ )._toQuery());
+ }
+
+ if (Objects.nonNull(condition.getWordCountMax())) {
+ searchBuilder.query(RangeQuery.of(m -> m
+ .field("wordCount")
+ .lt(JsonData.of(condition.getWordCountMax()))
+ )._toQuery());
+ }
+
+ if (Objects.nonNull(condition.getUpdateTimeMin())) {
+ searchBuilder.query(RangeQuery.of(m -> m
+ .field("lastChapterUpdateTime")
+ .gte(JsonData.of(condition.getUpdateTimeMin().getTime()))
+ )._toQuery());
+ }
+ }
+}