SpringBoot集成ElasticSearch实现支持错别字检索和关键字高亮的模糊查询

devtools/2025/3/18 6:41:34/

文章目录

  • 一、背景
  • 二、环境准备
    • 1.es8集群
    • 2.Kibana
    • 3.Canal
  • 三、集成到SpringBoot
    • 1.新增依赖
    • 2.es配置类
    • 3.建立索引
    • 4.修改查询方法
  • 四、修改前端

一、背景

我们在开发项目的搜索引擎的时候,如果当数据量庞大、同时又需要支持全文检索模糊查询,甚至你想做到像百度、CSDN等大型网站的搜索效果——支持错别字查询关键字高亮显示,搜索结果根据相关性排序,如下图所示,那么就需要集成ElasticSearch来实现了。

在这里插入图片描述

本文用若依的“通知公告”页面的搜索功能来演示如何集成ElasticSearch到SpringBoot项目中改造优化搜索引擎,最终效果如下图所示

在这里插入图片描述

二、环境准备

需要部署好的中间件如下:

  • ElasticSearch(8.14.3)【集群】
  • Kibana(8.14.3)
  • Canal(1.1.8)

本文主要讲解es项目实战使用,关于部署部分就简单说下

1.es8集群

参考:ElasticSearch8集群的安装部署_es8搭建集群-CSDN博客

我这里用了三台虚拟机来部署es集群,这里推荐一个es浏览器GUI插件——Elasticvue,可视化操作界面,很方便,可用来监看节点状态、索引数据等。如下图所示

在这里插入图片描述

elasticsearch.yml 如下所示(另外两台要改下node.name)

cluster.name: es-clusternode.name: node-1# 节点属性
node.roles: [master,data]path.data: /data/es/elasticsearch-8.14.3/datapath.logs: /data/es/elasticsearch-8.14.3/logsnetwork.host: 0.0.0.0http.port: 9200discovery.seed_hosts: ["192.168.100.182:9300", "192.168.100.183:9300", "192.168.100.184:9300"]cluster.initial_master_nodes: ["node-1","node-2","node-3"]# 安全认证
xpack.security.enabled: true
xpack.security.enrollment.enabled: true
xpack.security.http.ssl:enabled: true # 注意第一个空格keystore.path: /data/es/elasticsearch-8.14.3/config/certs/http.p12truststore.path: /data/es/elasticsearch-8.14.3/config/certs/http.p12
xpack.security.transport.ssl:enabled: trueverification_mode: certificatekeystore.path: /data/es/elasticsearch-8.14.3/elastic-certificates.p12truststore.path: /data/es/elasticsearch-8.14.3/elastic-certificates.p12
http.host: [_local_, _site_]
ingest.geoip.downloader.enabled: false
xpack.security.http.ssl.client_authentication: none

2.Kibana

参考:Elasticsearch集群以及kibana安装_elasticsearch集群安装-CSDN博客

kibana.yml

注意这里的账号是es里本身自带的,然后密码是后期随机生成的(kibana不能配置es超管用户)

server.port: 5601server.host: "0.0.0.0"elasticsearch.hosts: - "https://192.168.100.182:9200"- "https://192.168.100.183:9200"- "https://192.168.100.184:9200"elasticsearch.username: "kibana_system"
elasticsearch.password: "z3P+5vZVRh2dh10p=i-s"
elasticsearch.ssl.verificationMode: none
i18n.locale: "zh-CN"

3.Canal

参考:canal实现MySQL和ES同步实践_canal mysql es-CSDN博客

canal.adapter-1.1.8\conf\application.yml

server:port: 8081
spring:jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8default-property-inclusion: non_nullcanal.conf:mode: tcp #tcp kafka rocketMQ rabbitMQflatMessage: truezookeeperHosts:syncBatchSize: 1000retries: -1timeout:accessKey:secretKey:consumerProperties:# canal tcp consumercanal.tcp.server.host: 127.0.0.1:11111canal.tcp.zookeeper.hosts:canal.tcp.batch.size: 500canal.tcp.username:canal.tcp.password:# kafka consumerkafka.bootstrap.servers: 127.0.0.1:9092kafka.enable.auto.commit: falsekafka.auto.commit.interval.ms: 1000kafka.auto.offset.reset: latestkafka.request.timeout.ms: 40000kafka.session.timeout.ms: 30000kafka.isolation.level: read_committedkafka.max.poll.records: 1000# rocketMQ consumerrocketmq.namespace:rocketmq.namesrv.addr: 127.0.0.1:9876rocketmq.batch.size: 1000rocketmq.enable.message.trace: falserocketmq.customized.trace.topic:rocketmq.access.channel:rocketmq.subscribe.filter:# rabbitMQ consumerrabbitmq.host:rabbitmq.virtual.host:rabbitmq.username:rabbitmq.password:rabbitmq.resource.ownerId:srcDataSources:defaultDS:url: jdbc:mysql://127.0.0.1:3306/ry-vue?useUnicode=trueusername: canalpassword: canal123canalAdapters:- instance: example # canal instance Name or mq topic namegroups:- groupId: g1outerAdapters:- name: logger- name: es8hosts: https://192.168.100.183:9200 # 127.0.0.1:9200 for rest modeproperties:mode: rest  # transport or restsecurity.ca.path: C:\Users\znak\Desktop\elasticsearch.crtsecurity.ssl.verification_mode: none  # ✅ 忽略 SSL 证书验证security.auth: elastic:rwfejTWwHo666BmrQ2QW #  only used for rest modecluster.name: es-cluster

注意这里有个elasticsearch.crt证书要配置,我的Canal是部署在window本地的,es开启了ssl认证,canal adapter(客户端)需要有这个证书才能访问,【这里虽然我配置了忽略ssl验证,但是发现没效果)

cmd执行:

#生成证书
echo -n | openssl s_client -connect https://192.168.100.182:9200 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > elasticsearch.crt
#导入到Java 信任库
keytool -importcert -alias elasticsearch-cert -keystore "E:\xuyue\jre1.8\lib\security\cacerts" -file "C:\Users\znak\Desktop\elasticsearch.crt"

canal.adapter-1.1.8\conf\es8\sys_notice.yml

这个是同步mysql的相关配置,本文以若依的通知表sys_notice演示,所以这里配置的是这个表的sql,可以参考一下

dataSourceKey: defaultDS
destination: example
esMapping:_index: sys_notice_type: _doc_id: notice_idupsert: truesql: "SELECT sn.notice_id as id, sn.notice_id, sn.notice_title, sn.notice_type, CONVERT(sn.notice_content USING utf8mb4) AS notice_content, sn.status, sn.create_time, sn.update_time,sn.remark,sn.create_by,sn.update_by FROM sys_notice sn"commitBatch: 3000

启动canal后,默认是增量同步,如果原先数据表里已经有数据,想要先同步到es,可以手动调用全量同步接口,如下所示:

curl http://127.0.0.1:8081/etl/es8/sys_notice.yml -X POST

在这里插入图片描述

三、集成到SpringBoot

1.新增依赖

ruoyi-system/pom.xml

        <dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>8.1.0</version><exclusions><exclusion><artifactId>jakarta.json-api</artifactId><groupId>jakarta.json</groupId></exclusion><exclusion><artifactId>elasticsearch-rest-client</artifactId><groupId>org.elasticsearch.client</groupId></exclusion></exclusions></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>8.1.0</version><scope>compile</scope></dependency><dependency><groupId>jakarta.json</groupId><artifactId>jakarta.json-api</artifactId><version>2.1.2</version></dependency><dependency><groupId>jakarta.json.bind</groupId><artifactId>jakarta.json.bind-api</artifactId><version>3.0.0</version></dependency><!--官方文档中如果遇到ClassNotFouind:jakarta.json.spi.JsonProvider,就需要导入这个包--><dependency><groupId>org.glassfish</groupId><artifactId>jakarta.json</artifactId><version>2.0.1</version></dependency>

2.es配置类

ElasticsearchConfig.java

package com.ruoyi.system.config;import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.ssl.SSLContexts;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;@Configuration
public class ElasticsearchConfig {@Beanpublic ElasticsearchClient elasticsearchClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {// 认证信息BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "rwfejTWwHo666BmrQ2QW"));// 创建不验证证书的 SSLContextSSLContext sslContext = SSLContexts.custom().loadTrustMaterial((chain, authType) -> true)  // 信任所有证书.build();// 创建 RestClientRestClientBuilder builder = RestClient.builder(new HttpHost("192.168.100.182", 9200, "https"),new HttpHost("192.168.100.183", 9200, "https"),new HttpHost("192.168.100.184", 9200, "https")).setHttpClientConfigCallback(httpClientBuilder ->httpClientBuilder.setSSLContext(sslContext).setDefaultCredentialsProvider(credentialsProvider));// 创建 Elasticsearch Java API ClientRestClient restClient = builder.build();RestClientTransport transport = new RestClientTransport(restClient, new co.elastic.clients.json.jackson.JacksonJsonpMapper(new ObjectMapper()));return new ElasticsearchClient(transport);}}

3.建立索引

索引创建是一个一次性操作,这里我直接建了一个测试类来创建

注意:

  • 这里字段我用的下划线格式,但是实体类里的是驼峰,所以后续需要手动映射下;同时注意数据库同步到es的时候,字段也要注意映射关系
  • 需要模糊查询的用text类型,并选择合适的分词器
  • 日期这里我加了yyyy-MM-dd’T’HH:mm:ssXXX这种类型,是因为我用canal同步mysql到es的时候,日期格式一直是这种格式(虽然mysql是yyyy-MM-dd HH:mm:ss)
    @Testvoid testIndexExample() throws IOException {//1.创建索引String indexName = "sys_notice";//检查索引是否已存在BooleanResponse exists = client.indices().exists(c -> c.index(indexName));//如果存在,则删除索引if (exists.value()) {client.indices().delete(c -> c.index(indexName));} else {// 创建索引// 设置索引配置CreateIndexResponse createIndexResponse = client.indices().create(c -> c.index(indexName).settings(s -> s.numberOfShards("1").numberOfReplicas("1")).mappings(m -> m.properties("notice_id", p -> p.keyword(k -> k)).properties("notice_title", p -> p.text(t -> t.analyzer("ik_max_word"))).properties("notice_type", p -> p.keyword(k -> k)).properties("notice_content", p -> p.text(t -> t.analyzer("ik_max_word"))).properties("status", p -> p.keyword(k -> k)).properties("create_by", p -> p.keyword(k -> k)).properties("create_time", p -> p.date(d -> d.format("yyyy-MM-dd HH:mm:ss||yyyy-MM-dd'T'HH:mm:ssXXX||epoch_millis"))).properties("update_by", p -> p.keyword(k -> k)).properties("update_time", p -> p.date(d -> d.format("yyyy-MM-dd HH:mm:ss||yyyy-MM-dd'T'HH:mm:ssXXX||epoch_millis"))).properties("remark", p -> p.text(t -> t.analyzer("ik_smart")))));if (Boolean.TRUE.equals(createIndexResponse.acknowledged())) {System.out.println("索引 " + indexName + " 创建成功");} else {System.out.println("索引 " + indexName + " 创建失败");}}}

索引建好我们可以用kibana来查看

GET /sys_notice

在这里插入图片描述

4.修改查询方法

com.ruoyi.system.service.impl.SysNoticeServiceImpl


@Resource
private ElasticsearchClient client;/*** 查询公告列表* * @param notice 公告信息* @return 公告集合*/
@Override
public List<SysNotice> selectNoticeList(SysNotice notice)
{Integer pageNum = Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1);Integer pageSize = Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10);SysNoticeOfES noticeOfES = new SysNoticeOfES();BeanUtils.copyBeanProp(noticeOfES, notice);noticeOfES.setNotice_title(notice.getNoticeTitle());noticeOfES.setCreate_by(notice.getCreateBy());noticeOfES.setNotice_type(notice.getNoticeType());List<SysNoticeOfES> noticesFromEs = searchNoticesFromEs(noticeOfES, pageNum, pageSize);if (noticesFromEs != null && noticesFromEs.size() > 0) {//转化为List<SysNotice>return noticesFromEs.stream().map(noticeOfES1 -> {SysNotice sysNotice = new SysNotice();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");BeanUtils.copyBeanProp(sysNotice, noticeOfES1);sysNotice.setNoticeId(noticeOfES1.getId());sysNotice.setNoticeTitle(noticeOfES1.getNotice_title());sysNotice.setCreateBy(noticeOfES1.getCreate_by());sysNotice.setUpdateBy(noticeOfES1.getUpdate_by());sysNotice.setNoticeContent(noticeOfES1.getNotice_content());sysNotice.setNoticeType(noticeOfES1.getNotice_type());try {sysNotice.setCreateTime(sdf.parse(sdf.format(noticeOfES1.getCreate_time())));sysNotice.setUpdateTime(noticeOfES1.getUpdate_time() != null ? sdf.parse(sdf.format(noticeOfES1.getUpdate_time())) : null);} catch (ParseException e) {e.printStackTrace();throw new GlobalException("日期转换异常");}sysNotice.setUpdateBy(noticeOfES1.getUpdate_by());return sysNotice;}).collect(Collectors.toList());} else {System.out.println("从数据库中查询");return noticeMapper.selectNoticeList(notice);}
}private List<SysNoticeOfES> searchNoticesFromEs(SysNoticeOfES notice, int pageNum, int pageSize) {try {// 计算 ES 分页参数int from = (pageNum - 1) * pageSize;// 构建 ES 查询SearchRequest request = new SearchRequest.Builder().index("sys_notice") // ES 索引名称.query(q -> q.bool(b -> {if (notice.getNotice_title() != null) {b.must(m -> m.match(t -> t.field("notice_title").query(notice.getNotice_title()).fuzziness("AUTO")));}if (notice.getCreate_by() != null) {b.must(m -> m.wildcard(t -> t.field("create_by").value("*" + notice.getCreate_by() + "*")));}if (notice.getNotice_type() != null) {b.must(m -> m.term(t -> t.field("notice_type").value(notice.getNotice_type())));}return b;})).highlight(h -> h.fields("notice_title", hf -> hf.preTags("<span style='color:red;'>")  // 高亮前缀.postTags("</span>")).requireFieldMatch(false)).from(from).size(pageSize).build();// 执行查询SearchResponse<SysNoticeOfES> response = client.search(request, SysNoticeOfES.class);// 解析结果return response.hits().hits().stream().map(hit -> {SysNoticeOfES sysNoticeOfES = hit.source();  // 获取原始的 SysNotice 数据if (hit.highlight() != null && hit.highlight().containsKey("notice_title")) {// 从 highlight 中获取高亮的内容,假设只有一个高亮值assert sysNoticeOfES != null;sysNoticeOfES.setNotice_title(hit.highlight().get("notice_title").get(0));  // 更新 noticeTitle 为高亮文本}return sysNoticeOfES;  // 返回更新后的 SysNotice}).collect(Collectors.toList());} catch (Exception e) {e.printStackTrace();return new ArrayList<>();}
}

requireFieldMatch(false)是指允许模糊查询高亮

fuzziness("AUTO")表示:

  • 如果查询词 ≤ 2 个字符,不允许错别字。
  • 如果查询词 3~5 个字符,允许 1 个错别字。
  • 如果查询词 ≥ 6 个字符,允许 2 个错别字。

上面的查询用es语法相当于(用下图的参数为例):

在这里插入图片描述

GET /sys_notice/_search
{"query": {"bool": {"must": [{"match": {"notice_title": {"query": "若一集成es","fuzziness": "AUTO"}}},{"wildcard": {"create_by": {"value": "*ad*"}}},{"term": {"notice_type": "1"}}]}},"highlight": {"fields": {"notice_title": {"pre_tags": ["<span style='color:red;'>"],"post_tags": ["</span>"]}},"require_field_match": false},"from": 0,"size": 10
}

在kibana执行的结果:

{"took": 3,"timed_out": false,"_shards": {"total": 1,"successful": 1,"skipped": 0,"failed": 0},"hits": {"total": {"value": 3,"relation": "eq"},"max_score": 3.1798086,"hits": [{"_index": "sys_notice","_id": "10","_score": 3.1798086,"_source": {"notice_title": "测试若依集成es","notice_type": "1","notice_content": "<p>一条测试通知</p>","status": "0","create_time": "2025-03-13T10:39:09+08:00","update_time": null,"remark": null,"create_by": "admin","update_by": "","id": 10},"highlight": {"notice_title": ["测试<span style='color:red;'>若</span>依<span style='color:red;'>集成</span><span style='color:red;'>es</span>"]}},{"_index": "sys_notice","_id": "11","_score": 2.9974954,"_source": {"notice_title": "测试若依与es集成-3","notice_type": "1","notice_content": "<p>111</p>","status": "0","create_time": "2025-03-13T10:39:52+08:00","update_time": null,"remark": null,"create_by": "admin","update_by": "","id": 11},"highlight": {"notice_title": ["测试<span style='color:red;'>若</span>依与<span style='color:red;'>es</span><span style='color:red;'>集成</span>-3"]}},{"_index": "sys_notice","_id": "2","_score": 1.4532554,"_source": {"notice_title": "维护通知:2018-07-01 若依系统凌晨维护","notice_type": "1","notice_content": "维护内容","status": "0","create_time": "2024-04-30T11:53:10+08:00","update_time": null,"remark": "管理员","create_by": "admin","update_by": "","id": 2},"highlight": {"notice_title": ["维护通知:2018-07-01 <span style='color:red;'>若</span>依系统凌晨维护"]}}]}
}

com.ruoyi.system.domain.SysNoticeOfES

package com.ruoyi.system.domain;import com.fasterxml.jackson.annotation.JsonProperty;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.BaseEntityOfES;
import com.ruoyi.common.xss.Xss;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;/*** 通知公告表 sys_notice* * @author ruoyi*/
public class SysNoticeOfES extends BaseEntityOfES
{private static final long serialVersionUID = 1L;/** 公告ID */private Long id;/** 公告标题 */private String notice_title;/** 公告类型(1通知 2公告) */private String notice_type;/** 公告内容 */private String notice_content;/** 公告状态(0正常 1关闭) */private String status;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getNotice_title() {return notice_title;}public void setNotice_title(String notice_title) {this.notice_title = notice_title;}public String getNotice_type() {return notice_type;}public void setNotice_type(String notice_type) {this.notice_type = notice_type;}public String getNotice_content() {return notice_content;}public void setNotice_content(String notice_content) {this.notice_content = notice_content;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}
}

com.ruoyi.common.core.domain.BaseEntityOfES

package com.ruoyi.common.core.domain;import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** Entity基类* * @author ruoyi*/
public class BaseEntityOfES implements Serializable
{private static final long serialVersionUID = 1L;/** 搜索值 */@JsonIgnoreprivate String searchValue;/** 创建者 */private String create_by;/** 创建时间 */@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")private Date create_time;/** 更新者 */private String update_by;/** 更新时间 */@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")private Date update_time;/** 备注 */private String remark;/** 请求参数 */@JsonInclude(JsonInclude.Include.NON_EMPTY)private Map<String, Object> params;public String getSearchValue(){return searchValue;}public void setSearchValue(String searchValue){this.searchValue = searchValue;}public String getCreate_by() {return create_by;}public void setCreate_by(String create_by) {this.create_by = create_by;}public Date getCreate_time() {return create_time;}public void setCreate_time(Date create_time) {this.create_time = create_time;}public String getUpdate_by() {return update_by;}public void setUpdate_by(String update_by) {this.update_by = update_by;}public Date getUpdate_time() {return update_time;}public void setUpdate_time(Date update_time) {this.update_time = update_time;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}public Map<String, Object> getParams(){if (params == null){params = new HashMap<>();}return params;}public void setParams(Map<String, Object> params){this.params = params;}
}

四、修改前端

最后,前端要修改table的展示,使其能够识别html标签,实现关键字高亮显示的效果

ruoyi-ui/src/views/system/notice/index.vue

<el-table-columnlabel="公告标题"align="center"prop="noticeTitle":show-overflow-tooltip="true"><template slot-scope="scope"><span v-html="scope.row.noticeTitle"></span>  <!-- 允许 HTML 高亮显示 --></template>
</el-table-column>

http://www.ppmy.cn/devtools/168011.html

相关文章

【创作者纪念日1460天4年】我的创作纪念日

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

【FAQ】HarmonyOS SDK 闭源开放能力 —Map Kit(6)

1.问题描述&#xff1a; 使用华为内置的MapComponent&#xff0c; 发现显示不出来。查看日志&#xff0c; MapRender底层有报错。 解决方案&#xff1a; 麻烦按以下步骤检查下地图服务&#xff0c;特别是签名证书指纹那部分。 1.一般没有展示地图&#xff0c;可能和没有配置…

【视频】SRS将RTMP转WebRTC、HLS流;获取RTSP转其它流

1、安装依赖库 sudo apt install tclsh sudo apt install cmake sudo apt install autotools-dev automake m4 perl sudo apt install libtool2、源码安装 1)下载源码 https://github.com/ossrs/srs/releases/tag/v5.0-r32)配置、编译 ./configure && make -j83、…

【C++】:C++11详解 —— 入门基础

目录 C11简介 统一的列表初始化 1.初始化范围扩展 2.禁止窄化转换&#xff08;Narrowing Conversion&#xff09; 3.解决“最令人烦恼的解析”&#xff08;Most Vexing Parse&#xff09; 4.动态数组初始化 5. 直接初始化返回值 总结 声明 1.auto 类型推导 2. declty…

共享经济再中介化进程中的技术创新与模式重构研究——以“开源AI智能名片链动2+1模式S2B2C商城小程序“为例

摘要 本文基于共享经济中介化演进的双重逻辑&#xff0c;通过案例研究与技术解构&#xff0c;探讨"开源AI智能名片链动21分销机制S2B2C商城小程序"集成系统如何重构数字经济时代的价值网络。研究发现&#xff0c;该技术生态通过三维需求匹配、动态价值分配与智能风险…

PyQt6内嵌http.server Web 和Flask Web服务器方法详解

PyQt6 可以内嵌一个简单的 Web 服务器。虽然 PyQt6 本身不提供直接的 Web 服务器功能&#xff0c;但可以结合 Python 的标准库&#xff08;如 http.server&#xff09;或其他 Web 框架&#xff08;如 Flask、FastAPI 等&#xff09;来实现。 示例&#xff1a;使用 http.server…

遗传算法基础讲解

一、遗传算法基础 1. 什么是遗传算法? 一种模拟生物进化过程的优化算法,基于达尔文的“自然选择”和“遗传学理论”。核心思想:通过选择(优胜劣汰)、交叉(基因重组)、变异(基因突变)操作,逐步逼近问题的最优解。2. 为什么用遗传算法? 适用性强:解决复杂的非线性、…

C语言的软件工程

C语言的软件工程 引言 C语言作为一种历史悠久、功能强大的编程语言&#xff0c;在软件工程领域有着广泛的应用。自1972年由Dennis Ritchie开发以来&#xff0c;C语言凭借其高效性能、灵活性以及可移植性&#xff0c;成为了操作系统、嵌入式系统及高性能应用程序开发的首选语言…