当前位置: 首页 > article >正文

SpringBoot项目集成ElasticSearch

1. 项目背景

处于失业找工作的阶段,随便写写吧~ 没啥背景,没啥意义,Java后端越来越卷了。第一学历不是本科,感觉真的是没有一点路可走。

如果有路过的小伙伴,如果身边还有坑位,不限第一学历的话,麻烦帮忙捞一下!非常感谢!

2. 项目环境

说下项目环境吧,尽量使用和自己SpringBoot匹配的版本,不然整合的过程中可能会有很多的坑~

image-20250305195052710

本文环境如下:

  • Springboot:2.7.12
  • JDK:1.8
  • elasticsearch:7.17.3

3. 下载安装

贴个ES下载链接,windows环境:https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.3-windows-x86_64.zip

然后还一个,相当于ES的可视化界面吧:https://github.com/mobz/elasticsearch-head

windows环境下很方便,这里就不多说了,简单说说需要修改的地方吧

3.1 elasticsearch配置

安装就是下载解压,官网下载速度很快,下载完直接解压就行。解压完大致如下:

image-20250305200144883

然后配置一下环境变量,ES_HOMEES_JAVA_HOME,注意改成自己的解压目录哈 ~

ES_HOME: D:\software\elasticsearch-7.17.3
ES_JAVA_HOME: D:\software\elasticsearch-7.17.3\jdk

image-20250305200437919

然后修改下/config下的elasticsearch.yml文件,最后添加一下内容,不然使用elasticsearch-head会报跨域的错误

http.cors.enabled: true
http.cors.allow-origin: "*"

好了然后直接前往/bin目录下直接双击elasticsearch.bat文件即可启动,默认9200端口。

启动后直接去浏览器输入http://127.0.0.1:9200/,访问到下面页面代表ES启动成功。

image-20250305200943691

3.2 elasticsearch-head配置

elasticsearch-head直接在github上拉代码就行。

这个依赖node环境,需要使用npm下载依赖。

进入到D:\software\elasticsearch-head,先通过npm i安装依赖,然后通过npm run start启动。

npm i
npm run start

启动访问http://127.0.0.1:9100/,点击了连接,可以看到以下界面 ,我的有点不纯了😂

image-20250305201448537

4. SpringBoot 集成Es

4.1 添加依赖

在Springboot中pom.xml中添加ES依赖

 		<!-- 引入ES依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>

4.2 项目使用

我这里直接用了笑小枫网站博客的内容,方便后面演示。

首先创建一个对象,用来定义存储数据的对象,对应ES中的mappings。对象如下:

package com.maple.es.bean;import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.util.Date;
import java.util.List;/*** @author 笑小枫 <https://www.xiaoxiaofeng.com/>* @date 2023/1/30*/
@Data
@Document(indexName = "blog_title")
public class BlogDoc {@Idprivate Long id;@Field(type = FieldType.Long)private Long categoryId;@Field(type = FieldType.Text)private String title;@Field(type = FieldType.Text)private String content;@Field(type = FieldType.Keyword)private String description;@Field(type = FieldType.Keyword)private String keywords;@Field(type = FieldType.Keyword)private List<String> keywordList;private String status;@Field(type = FieldType.Integer)private Integer readNum;@Field(type = FieldType.Integer)private Integer collectNum;@Field(type = FieldType.Date)private Date createTime;
}

ES中对象类型这里只演示一下String、数值和时间。

image-20250305203028700

创建BlogRepository继承ElasticsearchRepository<BlogDoc, Long>接口。

其中BlogDoc是我们上面的对象。Long对应我们的主键,标 @Id的那个。

你可以在里面定义一些接口,也可以不定义。我这里只用到了继承的简单增删接口,查询使用ElasticsearchRestTemplate

package com.maple.es.repository;import com.maple.es.bean.BlogDoc;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;public interface BlogRepository extends ElasticsearchRepository<BlogDoc, Long> {}

直接上BlogServiceImpl吧 ,详细的注释都放在代码里了,通义千问生成,大体懂。

核心部分看查询getList方法,涉及到分页了,简单定义了一个分页的对象

Page对象

package com.maple.es.bean;import lombok.Data;@Data
public class Page {private Integer page;private Integer size;private Long total;
}

简单的封装一下分页对象、请求参数和返回结果的对象

package com.maple.es.bean;import lombok.Data;import java.util.List;@Data
public class BlogPageDoc {private Page page;private BlogDoc query;private List<BlogDoc> result;
}

【本文重点】Service实现类

package com.maple.es.service.impl;import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.maple.es.bean.BlogDoc;
import com.maple.es.bean.BlogPageDoc;
import com.maple.es.bean.Page;
import com.maple.es.mapper.BlogMapper;
import com.maple.es.repository.BlogRepository;
import com.maple.es.service.BlogService;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;@Service
public class BlogServiceImpl implements BlogService {@Autowiredprivate BlogRepository blogRepository;@Autowiredprivate ElasticsearchRestTemplate elasticsearchRestTemplate;@Autowiredprivate BlogMapper blogTitleMapper;/*** 创建单条数据* <p>* 该方法负责将给定的BlogDoc对象保存到ES中** @param blogBean 代表要创建的博客文章的文档对象*/@Overridepublic void createBlog(BlogDoc blogBean) {blogRepository.save(blogBean);}/*** 更新博客文档* <p>* 使用Elasticsearch的脚本更新功能来更新博客文档的标题和内容** @param blogBean 包含要更新的博客信息的BlogDoc对象,包括博客的标题、内容和ID*/@Overridepublic void updateBlog(BlogDoc blogBean) {// 构建更新脚本,用于更新Elasticsearch文档的标题和内容// ctx._source 固定写法String script = "ctx._source.title = '" + blogBean.getTitle() + "';" +"ctx._source.content = '" + blogBean.getTitle() + "'";// 构建更新查询对象,指定要更新的文档ID和更新脚本UpdateQuery build = UpdateQuery.builder(String.valueOf(blogBean.getId())).withScript(script).build();// 指定要更新的索引名称IndexCoordinates indexCoordinates = IndexCoordinates.of("blog_title");// 执行更新操作,并获取更新响应elasticsearchRestTemplate.update(build, indexCoordinates);}/*** 初始化数据方法* <p>* 该方法用于从数据库中加载博客标题的相关信息,并处理关键词字符串,以便于后续的搜索或展示* 它首先从数据库中选择所有博客标题的信息,然后遍历这些信息,将每个博客标题的关键词字符串* 转换为关键词列表,最后将处理后的博客标题信息保存到博客仓库中*/public void initData() {// 从数据库中选择所有博客标题的信息List<BlogDoc> list = blogTitleMapper.selectTitleList();// 遍历博客标题列表,处理每个博客标题的关键词for (BlogDoc blogTitleVo : list) {// 如果博客标题的关键词不为空,则将其转换为关键词列表if (blogTitleVo.getKeywords() != null) {blogTitleVo.setKeywordList(Arrays.asList(blogTitleVo.getKeywords().split(",")));}}// 将处理后的博客标题列表保存到博客仓库中blogRepository.saveAll(list);}/*** 删除指定ID的博客** @param id 博客的唯一标识符*/@Overridepublic void deleteBlog(Long id) {blogRepository.deleteById(id);}@Overridepublic BlogPageDoc getListBlogTitle(BlogPageDoc blogTitleVo) {return getList(blogTitleVo);}/*** 根据查询条件和分页信息获取博客列表** @param blogPageDoc 包含查询条件和分页信息的博客页面文档* @return 返回填充了查询结果的博客页面文档*/private BlogPageDoc getList(BlogPageDoc blogPageDoc) {// 获取查询条件BlogDoc blogDoc = blogPageDoc.getQuery();// 获取分页信息Page page = blogPageDoc.getPage();// 创建高亮构建器HighlightBuilder highlightBuilder = new HighlightBuilder();// 设置需要高亮的字段highlightBuilder.field("title");highlightBuilder.field("description");// 设置高亮显示的前缀和后缀highlightBuilder.preTags("<span style=\"font-color :red\">");highlightBuilder.postTags("</span>");// 创建布尔查询构建器BoolQueryBuilder queryBuilders = QueryBuilders.boolQuery();// 如果标题关键字不为空,则必须匹配keywordList中的关键字if (StringUtils.isNotBlank(blogDoc.getTitle())) {// list中有一个匹配成功,就代表成功,虽然指定了keywordList的类型,但是在es中还是text(不知道是不是哪个地方bug了,暂未验证),所以指定keywordList.keywordqueryBuilders.must(QueryBuilders.termsQuery("keywordList.keyword", blogDoc.getKeywordList()));}// 如果类别ID不为空,则必须匹配类别IDif (blogDoc.getCategoryId() != null) {queryBuilders.must(QueryBuilders.termQuery("categoryId", blogDoc.getCategoryId()));}// 如果blogDoc的阅读数量不为空,则在查询中添加阅读数量的范围条件if (blogDoc.getReadNum() != null) {// 构建一个范围查询,筛选出阅读数量大于当前博客阅读数量的文档queryBuilders.must(QueryBuilders.rangeQuery("readNum").gt(blogDoc.getReadNum()));}// 如果标题不为空,则应匹配标题、描述或内容中的标题if (StringUtils.isNotBlank(blogDoc.getTitle())) {queryBuilders.should(QueryBuilders.matchQuery("title", blogDoc.getTitle()));queryBuilders.should(QueryBuilders.matchQuery("description", blogDoc.getTitle()));queryBuilders.should(QueryBuilders.matchQuery("content", blogDoc.getTitle()));// 设置至少应匹配的条件数queryBuilders.minimumShouldMatch(1);}// 执行搜索并获取结果SearchHits<BlogDoc> searchHits = elasticsearchRestTemplate.search(new NativeSearchQueryBuilder().withQuery(queryBuilders) // 设置查询条件.withSorts(SortBuilders.fieldSort("readNum").order(SortOrder.DESC)) // 设置排序条件.withHighlightBuilder(highlightBuilder)  // 设置高亮构建器.withPageable(PageRequest.of(page.getPage() - 1, page.getSize()))  // 设置分页信息.build(),BlogDoc.class);// 创建结果列表List<BlogDoc> result = new ArrayList<>();// 设置总记录数page.setTotal(searchHits.getTotalHits());// 遍历搜索结果并处理高亮显示searchHits.getSearchHits().forEach(hits -> {BlogDoc blogTitle = hits.getContent();Map<String, List<String>> highlightFields = hits.getHighlightFields();// 如果标题的高亮字段不为空,则替换标题if (!CollectionUtils.isEmpty(highlightFields.get("title"))) {blogTitle.setTitle(hits.getHighlightFields().get("title").get(0));}// 如果描述的高亮字段不为空,则替换描述if (!CollectionUtils.isEmpty(highlightFields.get("description"))) {blogTitle.setDescription(hits.getHighlightFields().get("description").get(0));}// 将处理后的结果添加到列表中result.add(blogTitle);});// 设置查询结果blogPageDoc.setResult(result);// 返回填充了查询结果的博客页面文档return blogPageDoc;}
}

可以在elasticsearch-head看到对应的数据,这里就不一步一步演示了

image-20250305212410963

5.结语

文中只放了一些核心的代码

一些调用数据的代码和Controller的代码都没贴,非核心

需要的小伙伴可以前往github拉源码看一下。

本文源码:https://github.com/hack-feng/maple-product/tree/main/maple-es

🐾我是笑小枫,全网皆可搜的【笑小枫】

相关文章:

SpringBoot项目集成ElasticSearch

1. 项目背景 处于失业找工作的阶段&#xff0c;随便写写吧~ 没啥背景&#xff0c;没啥意义&#xff0c;Java后端越来越卷了。第一学历不是本科&#xff0c;感觉真的是没有一点路可走。 如果有路过的小伙伴&#xff0c;如果身边还有坑位&#xff0c;不限第一学历的话&#xff0…...

DeepSeek大模型深度解析:架构、技术与应用全景

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。https://www.captainbed.cn/north 文章目录 一、大模型时代与DeepSeek的定位1.1 大模型发展历程回顾大模型发展历程时间轴&#xff08;20…...

SQL AND OR 操作符详解

SQL AND & OR 操作符详解 在SQL(结构化查询语言)中,AND 和 OR 是两种非常重要的逻辑操作符,它们用于在查询条件中组合多个条件。理解并正确使用这些操作符对于编写有效的SQL查询至关重要。 引言 在处理数据库查询时,我们常常需要根据多个条件来筛选数据。AND 和 OR…...

Dubbo+Zookeeper

Apache ZooKeeper 通过当前页面下载Zookeeper 在这里启动zookeeper 可以根据这个页面简单学习一下&#xff0c;但是没有集成mysql&#xff0c;也会出现一些报错&#xff0c;且在这之后我们要使用的管理页面是vue的dubbo-admin dubbo学习三&#xff1a;springboot整合dubbozo…...

从源到目标:深度学习中的迁移学习与领域自适应实践

引言&#xff1a;数据驱动的智能时代与迁移挑战 在深度学习快速发展的今天&#xff0c;模型训练对数据量和质量的依赖成为核心瓶颈。面对新场景时&#xff0c;标注数据不足、数据分布差异等问题常导致模型性能骤降。迁移学习&#xff08;Transfer Learning&#xff09;与领域自…...

从厨电模范到数字先锋,看永洪科技如何助力方太集团开启数字新征程

在数字化洪流席卷全球的宏大背景下&#xff0c;企业转型升级的紧迫性与重要性日益凸显&#xff0c;成为驱动行业进步的关键引擎。在这一波澜壮阔的转型浪潮中&#xff0c;方太集团——厨电领域的璀璨明珠&#xff0c;以其前瞻性的战略视野和不懈的创新精神&#xff0c;携手数据…...

Redis大key

Redis大key基本概念&#xff0c;影响 Redis 大 key 指在 Redis 中存储了大量数据的键&#xff0c;它会对 Redis 的性能和内存管理产生影响。 大key的定义与value的大小和元素数量有关&#xff0c;但这个定义并不是绝对的&#xff0c;而是相对的&#xff0c;具体取决于系统的使用…...

在 Apache Tomcat 中,部署和删除项目

在 Apache Tomcat 中&#xff0c;部署和删除 WAR 文件是常见的操作。以下是详细步骤&#xff1a; 1. 删除 WAR 文件 (1) 停止应用 进入 Tomcat 的管理界面&#xff08;默认地址&#xff1a;http://localhost:8080/manager/html&#xff09;。 找到需要删除的应用&#xff0c;…...

前端基础之组件自定义事件

我们可以通过使用给组件绑定事件&#xff0c;当组件触发该事件时&#xff0c;就能进行值得返回 我们可以使用v-on属性来给子组件绑定自定义事件&#xff0c;此时该事件就会存在vc中&#xff0c;然后通过this.$emit来触发绑定的事件&#xff0c; 这样就能实现不需要app.vue来给子…...

在 Docker 中,无法直接将外部多个端口映射到容器内部的同一个端口

Docker 的端口映射是一对一的&#xff0c;即一个外部端口只能映射到容器内部的一个端口。 1. 为什么不能多对一映射&#xff1f; 端口冲突&#xff1a; 如果外部多个端口映射到容器内部的同一个端口&#xff0c;Docker 无法区分外部请求应该转发到哪个内部端口&#xff0c;会…...

基于DeepSeek(本地部署)和RAGFlow构建个人知识库

总结自视频&#xff08;很强的小姐姐视频&#xff0c;讲解清晰明了&#xff09;&#xff1a;【知识科普】【纯本地化搭建】【不本地也行】DeepSeek RAGFlow 构建个人知识库_哔哩哔哩_bilibili 1. 背景 deepseek官方网页版也虽然很强&#xff0c;能够满足绝大部分需求&#xf…...

学习工具的一天之(burp)

第一呢一定是先下载 【Java环境】&#xff1a;Java Downloads | Oracle 下来是burp的下载 Download Burp Suite Community Edition - PortSwigger 【下载方法二】关注的一个博主 【BurpSuite 安装激活使用详细上手教程 web安全测试工具】https://www.bilibili.com/video/BV…...

2025-03-05 学习记录--C/C++-PTA 习题5-8 空心的数字金字塔

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、题目描述 ⭐️ 二、解题步骤 ⭐️ 下面以n5举例&#xff1a;&#x1f98b; 第1步 &#x1f380;、外层循环&#xff08;从1到…...

C++课程设计【宿舍管理查询软件】

宿舍管理查询软件 一、题目描述二、源码以及说明宿舍管理查询软件设计与实现1. 系统设计思路1.1 功能需求1.2 数据结构2. 系统实现3. 代码说明3.1 数据结构3.2 功能实现3.3 文件存储4. 示例运行输入输出5. 总结其他QT文章推荐一、题目描述 (一)问题描述 为宿舍管理人员编写一…...

Stable Diffusion模型采样方法与参数配置详解(含步数及画风适配表)

Stable Diffusion模型采样方法与参数配置详解&#xff08;含步数及画风适配表&#xff09; 以下为当前主流采样方法的性能对比及参数配置建议&#xff0c;结合显存占用、生成速度、适用场景等维度分类总结&#xff1a; 一、采样方法对比表 采样方法推荐步数显存占用生成速度…...

极狐GitLab 17.9 正式发布,40+ DevSecOps 重点功能解读【三】

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…...

vue+neo4j 四大名著知识图谱问答系统

编号: D039 视频 vueneo4j四大名著知识图谱问答系统 技术架构 vuedjangoneo4jmysql技术实现 功能模块图 问答&#xff1a;基于知识图谱检索、支持图多跳、显示推理路径 姜维的师傅的主公的臣是谁&#xff1a; 马谡 知识图谱&#xff1a;四大名著总共4个图谱 红楼梦图谱 …...

【智能体架构:Agent】LangChain智能体类型ReAct、Self-ASK的区别

1. 什么是智能体 将大语言模型作为一个推理引擎。给定一个任务&#xff0c; 智能体自动生成完成任务所需步骤&#xff0c; 执行相应动作&#xff08;例如选择并调用工具&#xff09;&#xff0c; 直到任务完成。 2. 先定义工具&#xff1a;Tools 可以是一个函数或三方 API也…...

基于eRDMA实测DeepSeek开源的3FS

DeepSeek昨天开源了3FS分布式文件系统, 通过180个存储节点提供了 6.6TiB/s的存储性能, 全面支持大模型的训练和推理的KVCache转存以及向量数据库等能力, 每个客户端节点支持40GB/s峰值吞吐用于KVCache查找. 发布后, 我们在阿里云ECS上进行了快速的复现, 并进行了性能测试, ECS…...

Vue的简单入门 三

目录 侦听器 watch 注意 表单输入绑定 v-model v-model修饰符​编辑 lazy number Trim 模板引用 组件组成 组件引用三步走 组件的嵌套关系 header Main Aside Aritice Item App.vue组件引入三个子组件 组件的注册方式 全局注册组件的方法 (1) Vue 2 语…...

指纹细节提取(Matlab实现)

指纹细节提取概述指纹作为人体生物特征识别领域中应用最为广泛的特征之一&#xff0c;具有独特性、稳定性和便利性。指纹细节特征对于指纹识别的准确性和可靠性起着关键作用。指纹细节提取&#xff0c;即从指纹图像中精确地提取出能够表征指纹唯一性的关键特征点&#xff0c;是…...

使用wifi连接手机adb进行调试|不使用数据线adb调试手机|找应用错误日志和操作日志

手机在开发者选项里要开启无线调试 在手机设置中查看WiFi的IP地址 设置 -> WLAN -> 已连接的WiFi -> IP地址 使用手机的IP地址连接 adb connect 192.168.1.12:xxxxx 检查连接状态 adb devices 断开特定设备 adb disconnect 192.168.x.x:xxxxx 断开所有设备 …...

STM32——串口通信 UART

一、基础配置 Universal Asynchronous Receiver Transmitter 异步&#xff0c;串行&#xff0c;全双工 TTL电平 &#xff1a;高电平1 低电平0 帧格式&#xff1a; 起始位1bit 数据位8bit 校验位1bit 终止位1bit NVIC Settings一栏使能接受中断。 之前有设置LCD&#xff0c;…...

PHP fastadmin 学习

安装php环境安装mysql插件 修改 php.ini下载 phpstudy、fastadmin 错误 安装FastAdmin could not find driver 参考链接 安装插件 创建1.php <? phpinfo(); ?>运行 http://127.0.0.1/1.php 查看 POD 页面访问404 伪静态 Apache <IfModule mod_rewrite.c> O…...

Autojs无线连接vscode方法

1.获得电脑的IP 在电脑的CMD界面输入 ipconfig 然后找到ipv4的那一行&#xff0c;后面的即是你的电脑IP地址 2.打开vscode的autojs服务 安装autojs插件 在vscode界面按下ctrlshiftp 输入autojs 找到 点击 之后打开手机上的autojs 之后输入刚刚电脑上的地址 可以看到vsc…...

面试基础--MySQL SQL 优化深度解析

MySQL SQL 优化深度解析&#xff1a;EXPLAIN、索引优化与分库分表实践 引言 在互联网大厂的高并发场景下&#xff0c;数据库的性能优化是至关重要的。MySQL 作为最流行的关系型数据库之一&#xff0c;SQL 查询的性能直接影响了系统的响应时间和吞吐量。本文将深入探讨 MySQL …...

python之爬虫入门实例

链家二手房数据抓取与Excel存储 目录 开发环境准备爬虫流程分析核心代码实现关键命令详解进阶优化方案注意事项与扩展 一、开发环境准备 1.1 必要组件安装 # 安装核心库 pip install requests beautifulsoup4 openpyxl pandas# 各库作用说明&#xff1a; - requests&#x…...

版本控制器Git和gdb

一.版本控制器Git 1.版本控制简单来讲可以对每一份代码版本进行复制保存&#xff0c;保证每一版代码都可查 2.仓库的本质也是一个文件夹 3.git既是一个客户端&#xff0c;也是一个服务器&#xff0c;是一个版本控制器。而gitee和GitHub都是基于git的网站或平台 4.git的基本…...

大白话面试前的准备工作

面试前的准备工作非常重要&#xff0c;就像打仗前要做好各种准备一样&#xff0c;主要包括以下几个方面&#xff1a; 了解公司和岗位 公司情况&#xff1a;要知道这个公司是做什么的&#xff0c;比如是生产电子产品的&#xff0c;还是提供互联网服务的。还要了解它在行业里的…...

Dify 开源大语言模型应用开发平台使用(一)

文章目录 一、创建锂电池专业知识解答应用1.1 应用初始化 二、核心功能模块详解2.1 知识库构建2.2 工作流与节点编排节点类型说明工作流设计示例&#xff1a;锂电池选型咨询 2.3 变量管理 三、测试与调试3.1 单元测试3.2 压力测试3.3 安全验证 四、部署与优化建议4.1 部署配置4…...