文章目录
显示
? 优质资源分享 ?
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
? Python实战微信订餐小程序 ? | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
?Python量化交易实战? | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
项目简介
novel 是一套基于时下最新 Java 技术栈 Spring Boot 3 + Vue 3 开发的前后端分离的学习型小说项目,配备详细的项目教程手把手教你从零开始开发上线一个生产级别的 Java 系统,由小说门户系统、作家后台管理系统、平台后台管理系统等多个子系统构成。包括小说推荐、作品检索、小说排行榜、小说阅读、小说评论、会员中心、作家专区、充值订阅、新闻发布等功能。
项目地址
开发环境
- MySQL 8.0
- Redis 7.0
- Elasticsearch 8.2.0(可选)
- RabbitMQ 3.10.2(可选)
- JDK 17
- Maven 3.8
- IntelliJ IDEA 2021.3(可选)
- Node 16.14
后端技术选型
技术 | 版本 | 说明 |
---|---|---|
Spring Boot | 3.0.0-SNAPSHOT | 容器 + MVC 框架 |
Mybatis | 3.5.9 | ORM 框架 |
MyBatis-Plus | 3.5.1 | Mybatis 增强工具 |
JJWT | 0.11.5 | JWT 登录支持 |
Lombok | 1.18.24 | 简化对象封装工具 |
Caffeine | 3.1.0 | 本地缓存支持 |
Redis | 7.0 | 分布式缓存支持 |
MySQL | 8.0 | 数据库服务 |
Elasticsearch | 8.2.0 | 搜索引擎服务 |
RabbitMQ | 3.10.2 | 开源消息中间件 |
Undertow | 2.2.17.Final | Java 开发的高性能 Web 服务器 |
Docker | - | 应用容器引擎 |
Jenkins | - | 自动化部署工具 |
Sonarqube | - | 代码质量控制 |
注:更多热门新技术待集成。
前端技术选型
技术 | 版本 | 说明 |
---|---|---|
Vue.js | 3.2.13 | 渐进式 JavaScript 框架 |
Vue Router | 4.0.15 | Vue.js 的官方路由 |
axios | 0.27.2 | 基于 promise 的网络请求库 |
element-plus | 2.2.0 | 基于 Vue 3,面向设计师和开发者的组件库 |
示例代码
代码严格遵守阿里编码规约。
| | /** |
| | * 小说搜索 |
| | */ |
| | @Override |
| | public RestResp> searchBooks(BookSearchReqDto condition) { |
| | |
| | SearchResponse response = esClient.search(s -> { |
| | |
| | // 搜索构建器 |
| | SearchRequest.Builder searchBuilder = s.index(EsConsts.BookIndex.INDEX\_NAME); |
| | // 构建搜索条件 |
| | buildSearchCondition(condition, searchBuilder); |
| | // 排序 |
| | if (!StringUtils.isBlank(condition.getSort())) { |
| | searchBuilder.sort(o -> |
| | o.field(f -> f.field(condition.getSort()).order(SortOrder.Desc)) |
| | ); |
| | } |
| | // 分页 |
| | searchBuilder.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(); |
| | 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()); |
| | } |
| | return RestResp.ok(PageRespDto.of(condition.getPageNum(), condition.getPageSize(), total.value(), list)); |
| | |
| | } |
| | |
| | /** |
| | * 构建搜索条件 |
| | */ |
| | private void buildSearchCondition(BookSearchReqDto condition, SearchRequest.Builder searchBuilder) { |
| | |
| | BoolQuery boolQuery = BoolQuery.of(b -> { |
| | |
| | if (!StringUtils.isBlank(condition.getKeyword())) { |
| | // 关键词匹配 |
| | b.must((q -> q.multiMatch(t -> t |
| | .fields(EsConsts.BookIndex.FIELD\_BOOK\_NAME + "^2" |
| | , EsConsts.BookIndex.FIELD\_AUTHOR\_NAME + "^1.8" |
| | , EsConsts.BookIndex.FIELD\_BOOK\_DESC + "^0.1") |
| | .query(condition.getKeyword()) |
| | ) |
| | )); |
| | } |
| | |
| | // 精确查询 |
| | if (Objects.nonNull(condition.getWorkDirection())) { |
| | b.must(TermQuery.of(m -> m |
| | .field(EsConsts.BookIndex.FIELD\_WORK\_DIRECTION) |
| | .value(condition.getWorkDirection()) |
| | ).\_toQuery()); |
| | } |
| | |
| | if (Objects.nonNull(condition.getCategoryId())) { |
| | b.must(TermQuery.of(m -> m |
| | .field(EsConsts.BookIndex.FIELD\_CATEGORY\_ID) |
| | .value(condition.getCategoryId()) |
| | ).\_toQuery()); |
| | } |
| | |
| | // 范围查询 |
| | if (Objects.nonNull(condition.getWordCountMin())) { |
| | b.must(RangeQuery.of(m -> m |
| | .field(EsConsts.BookIndex.FIELD\_WORD\_COUNT) |
| | .gte(JsonData.of(condition.getWordCountMin())) |
| | ).\_toQuery()); |
| | } |
| | |
| | if (Objects.nonNull(condition.getWordCountMax())) { |
| | b.must(RangeQuery.of(m -> m |
| | .field(EsConsts.BookIndex.FIELD\_WORD\_COUNT) |
| | .lt(JsonData.of(condition.getWordCountMax())) |
| | ).\_toQuery()); |
| | } |
| | |
| | if (Objects.nonNull(condition.getUpdateTimeMin())) { |
| | b.must(RangeQuery.of(m -> m |
| | .field(EsConsts.BookIndex.FIELD\_LAST\_CHAPTER\_UPDATE\_TIME) |
| | .gte(JsonData.of(condition.getUpdateTimeMin().getTime())) |
| | ).\_toQuery()); |
| | } |
| | |
| | return b; |
| | |
| | }); |
| | |
| | searchBuilder.query(q -> q.bool(boolQuery)); |
| | |
| | } |
转载请注明:xuhss » 手把手教你使用 Spring Boot 3 开发上线一个前后端分离的生产级系统(一) – 介绍