• 手把手教你使用 Spring Boot 3 开发上线一个前后端分离的生产级系统(一) - 介绍


    项目简介

    novel 是一套基于时下最新 Java 技术栈 Spring Boot 3 + Vue 3 开发的前后端分离学习型开源项目,配备详细的项目开发文档手把手教你从零开始开发上线一个生产级别的 Java 系统,由小说门户系统、作家后台管理系统、平台后台管理系统等多个子系统构成。包括小说推荐、作品检索、小说排行榜、小说阅读、小说评论、会员中心、作家专区、充值订阅、新闻发布等功能。

    项目地址

    开发环境

    • MySQL 8.0
    • Redis 7.0
    • Elasticsearch 8.2.0(可选)
    • RabbitMQ 3.10.2(可选)
    • XXL-JOB 2.3.1(可选)
    • JDK 17
    • Maven 3.8
    • IntelliJ IDEA 2021.3(可选)
    • Node 16.14

    注:Elasticsearch、RabbitMQ 和 XXL-JOB 默认关闭,可通过 application.yml 配置文件中相应的enable配置属性开启。

    后端技术选型

    技术 版本 说明
    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 数据库服务
    ShardingSphere-JDBC 5.1.1 数据库分库分表支持
    Elasticsearch 8.2.0 搜索引擎服务
    RabbitMQ 3.10.2 开源消息中间件
    XXL-JOB 2.3.1 分布式任务调度平台
    Sentinel 1.8.4 流量控制组件
    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<PageRespDto<BookInfoRespDto>> searchBooks(BookSearchReqDto condition) {
    SearchResponse<EsBookDto> 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<BookInfoRespDto> list = new ArrayList<>();
    List<Hit<EsBookDto>> hits = response.hits().hits();
    for (Hit<EsBookDto> 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));
    }
  • 相关阅读:
    Day105.尚医通 后台前端搭建、Elementui、增删改查、分页条件查询、批量删除
    SharedPreference数据的读写操作
    AI五子棋 C++ 借助图形库raylib和raygui 设计模式思考过程和实现思路总结
    文件中的关键字与对应的协议
    Java“牵手”1688商品列表页数据采集+商品价格数据排序,商品销量排序数据,1688API接口采集方法
    Bash变量--用户自定义变量
    ASP.NET 开发几个知识点
    计算机竞赛 深度学习LSTM新冠数据预测
    Django Web框架
    思考思维(2):《极简思考》结构化思维
  • 原文地址:https://www.cnblogs.com/xxyopen/p/16320113.html