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

Spring AI 实现智能对话

1. 实现效果

2. Spring Boot 3 后端

2.1 pom.xml

<!-- 管理包依赖。通过 Spring AI 的 BOM 文件统一管理所有 Spring AI 相关依赖的版本,确保版本一致性,减少冲突 --><dependencyManagement><dependencies><!-- Spring AI --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>1.0.0-M6</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><!-- Spring Ai OpenAI 模型依赖 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId></dependency>

2.2 application.yml

spring:ai:openai:base-url: https://dashscope.aliyuncs.com/compatible-mode  # 默认使用 v1 版本号,如果添加会变成 /compatible-mode/v1/v1api-key: xxx   # 阿里云百炼平台获取chat:options:model: qwen-max   # 模型名称temperature: 0.8  # 模型温度,值越大,输出结果越随机embedding:options:model: text-embedding-v1  # RAG 指定文本向量化嵌入模型

2.3 AI 对话表,存储对话信息

CREATE TABLE `ai_conversation` (`id` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '主键',`title` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '标题',`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT '内容',`creator_id` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID',`create_time` datetime NOT NULL COMMENT '创建时间',`ts` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`delete_flag` int NOT NULL DEFAULT '0' COMMENT '逻辑删除字段 0.正常 1.删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='AI对话表';

2.4 Config(AI对话配置类)

package com.dragon.springboot3vue3.config;import com.dragon.springboot3vue3.tools.UserTools;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;@Configuration
public class OpenAIConfig {@Autowiredprivate UserTools userTools;/*** 配置会话存储方式:内存* 建议存储在 Redis* @return*/@Beanpublic ChatMemory chatMemory() {return new InMemoryChatMemory(); // 将历史会话临时存储在应用内存中(重启应用后数据丢失)。生产环境需要持久化存储(如 Redis、数据库)}/*** 配置向量数据库存储方式:内存* 建议存储在 Redis* @param embeddingModel* @return*/@Beanpublic VectorStore vectorStore(EmbeddingModel embeddingModel) {return SimpleVectorStore.builder(embeddingModel).build();}/*** Spring Boot 启动时运行,将知识库存入向量数据库* @param vectorStore* @param resource* @return*/@Beanpublic CommandLineRunner commandLineRunner(VectorStore vectorStore,@Value("classpath:/rag/knowledge.txt") Resource resource) {return args -> {vectorStore.write(                               // 3. 写入向量数据库new TokenTextSplitter().transform(       // 2. 转换为向量数据库文档new TextReader(resource).read()  // 1. 读取知识库));};}@Beanpublic ChatClient chatClient(OpenAiChatModel model, ChatMemory chatMemory) {return ChatClient.builder(model).defaultSystem("""你是韶君,一个温柔可爱机智的智能助手。请遵守以下规则:1. 当用户问"你是谁"时,必须且只能回答:"我是韶君,你的智能助手,有什么可以帮你的吗?"2. 不要提及RAG、检索或其他技术实现细节3. 使用自然、友好的语气回答问题4. 数学计算使用标准符号(如 18 ÷ 6 = 3)""").defaultAdvisors(new SimpleLoggerAdvisor(),                // 配置日志 Advisor,需在 yml 开启日志级别(默认:debug)new MessageChatMemoryAdvisor(chatMemory)  // 配置会话记忆).defaultTools(userTools)  // Tool Calling.build();}
}

2.5 Controller

package com.dragon.springboot3vue3.controller;import cn.dev33.satoken.util.SaResult;
import com.dragon.springboot3vue3.controller.dto.entityDto.AiConversationDto;
import com.dragon.springboot3vue3.controller.dto.entityDto.AiConversationHistoryDto;
import com.dragon.springboot3vue3.controller.dto.entityDto.ChatDto;
import com.dragon.springboot3vue3.entity.AiConversation;
import com.dragon.springboot3vue3.service.IAiConversationService;
import com.dragon.springboot3vue3.tools.UserTools;
import com.dragon.springboot3vue3.utils.StringDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import java.util.List;@Tag(name = "Spring AI 对话接口")
@RestController
@RequestMapping("/aiConversation")
public class AiConversationController {@Autowiredprivate IAiConversationService aiConversationService;@Autowiredprivate ChatMemory chatMemory;@Autowiredprivate ChatClient chatClient;@Autowiredprivate UserTools userTools;@Autowiredprivate VectorStore vectorStore;/*** 流式输出(要设置流式输出格式)*/@Operation(summary = "对话")@PostMapping(value = "/conversation",produces = "text/html;charset=utf-8")public Flux<String> chatClient(@RequestBody ChatDto chatDto) {return chatClient.prompt().user(chatDto.getPrompt()).advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.builder().query(chatDto.getPrompt()).build())) // 对话时,使用向量数据库.advisors(item -> item.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, chatDto.getConversationId())) // 根据会话ID,在ChatMemory存取会话.stream().content();}@Operation(summary = "新增或更新对话")@PostMapping("/saveOrUpdate")public SaResult saveOrUpdate(@RequestBody @Validated AiConversationDto aiConversationDto){AiConversation aiConversation=new AiConversation();BeanUtils.copyProperties(aiConversationDto,aiConversation);aiConversationService.saveOrUpdate(aiConversation);return SaResult.ok();}@Operation(summary = "所有对话列表")@GetMapping("/getAll")public SaResult getAll(){List<AiConversation> list = aiConversationService.lambdaQuery().orderByDesc(AiConversation::getCreateTime).list();return SaResult.ok().setData(list);}@Operation(summary = "对话的历史记录")@PostMapping("/history")public SaResult history(@RequestBody StringDTO stringDTO){List<Message> messages = chatMemory.get(stringDTO.getStr(), Integer.MAX_VALUE);if(messages == null || messages.isEmpty()){return SaResult.ok().setData(List.of());}return SaResult.ok().setData(messages.stream().map(AiConversationHistoryDto::new).toList());}
}

2.6 Service

package com.dragon.springboot3vue3.service;import com.dragon.springboot3vue3.entity.AiConversation;
import com.baomidou.mybatisplus.extension.service.IService;public interface IAiConversationService extends IService<AiConversation> {}

2.7 ServiceImpl

package com.dragon.springboot3vue3.service.impl;import com.dragon.springboot3vue3.entity.AiConversation;
import com.dragon.springboot3vue3.mapper.AiConversationMapper;
import com.dragon.springboot3vue3.service.IAiConversationService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;@Service
public class AiConversationServiceImpl extends ServiceImpl<AiConversationMapper, AiConversation> implements IAiConversationService {}

2.8 ChatDto、AiConversationDto 、StringDTO 

package com.dragon.springboot3vue3.controller.dto.entityDto;import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;import java.io.Serializable;@Data
public class ChatDto implements Serializable {private static final long serialVersionUID = 1L;@Schema(description = "会话ID")String conversationId;@Schema(description = "提示词")String prompt;
}
package com.dragon.springboot3vue3.controller.dto.entityDto;import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;@Schema(description = "前端请求参数封装")
@Data
public class AiConversationDto {@Schema(description = "主键")private String id;@Schema(description = "标题")private String title;
}
package com.dragon.springboot3vue3.utils;import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;@Data
public class StringDTO {@NotEmpty@Schema(description = "字符串")private String str;
}

2.9 rag -> knowledge.txt

3. Vue 3 + ElementPlus 前端

3.1 安装 marked(将 Markdown 转换为 HTML)、dompurify(防止XXS [跨站脚本攻击])

npm i marked
npm i dompurify

3.2 AiConversation.vue

<template><div class="container"><el-card class="left"><div class="newConversationBox"><el-button class="newConversationBtn" @click="saveOrUpdate()" type="primary" :icon="Plus" round >开启新对话</el-button></div><div class="conversation" :class="{ 'active': item.id === conversationModel.id }" v-for="item in conversationList" :key="item.id" ><el-text class="text" @click="selectConversation(item)" line-clamp="1"> {{ item.title == "" ? item.id : item.title }} </el-text><el-dropdown trigger="click"><el-icon class="moreFilled" :size="20"> <MoreFilled /> </el-icon><template #dropdown><el-dropdown-menu><el-dropdown-item :icon="EditPen" @click="rename(item)">重命名</el-dropdown-item></el-dropdown-menu></template></el-dropdown><!-- 重命名弹窗 --><el-dialog v-model="visible" width="30%" @close="close"><template #header><h1>重命名</h1></template><el-form :model="conversationModel" ref="formRef" label-width="100px"><el-form-item label="id" prop="id"><el-input v-model="conversationModel.id" placeholder="请输入会话标题"></el-input></el-form-item><el-form-item label="会话标题" prop="title"><el-input v-model="conversationModel.title" placeholder="暂无会话标题"></el-input></el-form-item><el-form-item><el-button @click="visible = false">取消</el-button><el-button type="primary" @click="saveOrUpdate()">确认</el-button></el-form-item></el-form></el-dialog></div></el-card><el-card class="right"><div class="conversation-content" ><el-scrollbar ref="scrollbarRef" height="600px"><div class="history" v-for="(item, index) in history" :key="index"><div class="user" v-if="item.role === 'user'" ><span class="userContent" >{{ item.content }}</span><el-avatar :icon="UserFilled" /></div><div class="assistant" v-if="item.role === 'assistant'"><span><el-avatar :icon="Platform" /></span><div class="assistantContent" v-html="item.content"></div></div></div></el-scrollbar></div><div class="sendBox" v-if="conversationModel.id"><el-input class="custom-textarea"v-model="model.prompt"@keydown.enter="send":disabled="loadingFlag"type="textarea":rows="3":autosize="{ minRows: 2, maxRows: 6 }":placeholder="loadingFlag ? '加载中...' : '给 AI 发送消息'" /><el-button @click="send" :disabled="loadingFlag" :loading="loadingFlag" type="primary" :icon="Position" circle size="large"></el-button></div></el-card></div>
</template><script setup lang="ts">import { ref,reactive,onMounted,nextTick } from 'vue'import { Plus,Position,MoreFilled,EditPen,Platform,UserFilled } from '@element-plus/icons-vue'import conversationApi from '@/api/ai/conversation';import { ElMessage } from 'element-plus'import { marked } from 'marked'    // 将 Markdown 转换为 HTMLimport DOMPurify from 'dompurify'  // 防止XXS(跨站脚本攻击)// 发送消息需要的 modellet model = reactive({conversationId: '',prompt: ''})// 新增会话需要的 modellet conversationModel = reactive({id: '',title: ''})const initConversationModel = { ...conversationModel }// 对话历史 Refconst scrollbarRef = ref()// 新增、编辑弹窗是否可见const visible = ref(false)// 是否加载中const loadingFlag = ref(false)// 重命名表单 Refconst formRef=ref()// 存储当前对话的历史记录const history = ref([]) as any// 对话列表let conversationList =  ref([]) as any// 发送消息const send = async()=>{// 去除前后空格后检查是否为空if (!model.prompt || !model.prompt.trim()) {ElMessage.warning("发送的消息不能为空!");return;}loadingFlag.value = true;history.value.push({role: "user",content: model.prompt})scrollToBottom()// 先添加一条空的 assistant 消息history.value.push({role: "assistant",content: ""})// 获取最后一条消息的引用const lastMessage = history.value[history.value.length - 1];try {// 获取流式响应const reader = await conversationApi.chat(model) as any;// 清空发送的消息model.prompt = ""// 创建解码器(用于解析二进制流)const decoder = new TextDecoder('utf-8');while (true) {const { done, value } = await reader.read();    // 读取一块数据if (done) break;                                // 流结束时退出       // 解码数据并追加到最后一条消息的contentlastMessage.content += decoder.decode(value);// 强制触发Vue的响应式更新(重要!)history.value = [...history.value];}// 使用 marked 将 Markdown 转换为 HTML// 使用 DOMPurify 清理 HTML,防止 XSS 攻击lastMessage.content = DOMPurify.sanitize(marked(lastMessage.content) as any);history.value = [...history.value];} catch (error) {console.error('API Error:', error);alert('发送消息失败,请稍后重试');} finally {loadingFlag.value = false;scrollToBottom()}}// 滚动到底部const scrollToBottom = async() => {// 等待DOM更新await nextTick();// 滚动到底部scrollbarRef.value.setScrollTop(scrollbarRef.value.wrapRef.scrollHeight || 0);}// 新增或更新会话const saveOrUpdate = async()=>{const response = await conversationApi.saveOrUpdate(conversationModel) as any;ElMessage.success(response.msg);// 刷新对话列表getAll();}// 所有会话列表const getAll = async()=>{const response = await conversationApi.getAll();conversationList.value = response.dataif(conversationList.value != null){conversationModel.id = conversationList.value[0].id  // 选中第1个对话model.conversationId = conversationModel.id}getHistoryToHtml(model.conversationId)}// 选择对话const selectConversation = async(item:any)=>{history.value = []conversationModel.id = item.idif(item.title != ""){conversationModel.title = item.title}model.conversationId = conversationModel.idgetHistoryToHtml(item.id)}// 根据对话ID请求后端,将后端响应数据转为 HTMLconst getHistoryToHtml = async(conversationId:string)=>{const res = await conversationApi.getHistory(conversationId);// 数组判空if(Array.isArray(res.data) && res.data.length !=0 ){// 会话历史转为 HTMLhistory.value = res.data.map(((item:any) => ({...item,content: item.role === 'assistant' ? DOMPurify.sanitize(marked(item.content) as any) : item.content})))scrollToBottom()}}// 对话重命名const rename = (item:any)=>{visible.value = true;conversationModel.title = item.title}// 关闭重命名弹窗const close= ()=>{// 清空数据Object.assign(model, initConversationModel);visible.value = false}onMounted(()=>{getAll()})</script><style scoped lang="less">
// 覆盖用户代理样式
body div#app {display: flex;
}.container{height: 100%;box-sizing: border-box; display: flex;justify-content: space-between;
}
.left{height: 100%;width: 20%;
}
.right{height: 100%;width: 79%;display: flex;flex-direction: column;.conversation-content{// height: 100%;flex: 1;.history{margin: 0 20px;.user{display: flex;justify-content: flex-end;margin: 20px 0;.userContent{margin-right: 10px;}}.assistant{display: flex;margin: 20px 0;.assistantContent{margin-left: 10px;}}}}.sendBox{display: flex;justify-content: center;align-items: center;margin-top: 20px;}.custom-textarea{margin: 0 10px;}.custom-textarea ::v-deep .el-textarea__inner {resize: none; /* 禁用手动调整大小 */}
}.conversation{display: flex;align-items: center;height: 30px;margin: 5px 0;border-radius: 20px;padding: 0 8px;justify-content: space-between;.text{flex: 1;}&.active{background-color: #f3f2ff;.text{color: black;}.moreFilled{color: black;}}
}
.newConversationBox{display: flex;justify-content: center;align-items: center;margin-bottom: 20px;
}</style>

3.3 api -> ai -> conversation.ts

import request from "@/utils/request";
import {useTokenStore} from '@/stores/token'// 聊天 API
// fetch API 来处理流式响应
export default{async chat(value:any){try{const response = await fetch(`${request.defaults.baseURL}/aiConversation/conversation`, {method: 'POST',headers: {'Content-Type': 'application/json','X-Token': useTokenStore().token,},body: JSON.stringify(value) // 显式转换为JSON字符串})return response.body?.getReader()} catch (error) {console.error('Conversation API Error:', error)throw error}},// 新增或更新会话saveOrUpdate(value:any){return request.post('/aiConversation/saveOrUpdate',value);},// 所有会话列表getAll(){return request.get('/aiConversation/getAll');},// 所有会话列表getHistory(str:string){return request.post('/aiConversation/history',{ str });},
}

 

 

相关文章:

Spring AI 实现智能对话

1. 实现效果 2. Spring Boot 3 后端 2.1 pom.xml <!-- 管理包依赖。通过 Spring AI 的 BOM 文件统一管理所有 Spring AI 相关依赖的版本&#xff0c;确保版本一致性&#xff0c;减少冲突 --><dependencyManagement><dependencies><!-- Spring AI -->…...

2025 网络安全技术深水区探索:从 “攻防对抗” 到 “数字韧性” 的范式跃迁

引言&#xff1a;当攻击成本趋近于零&#xff0c;防御逻辑必须重构 2025 年&#xff0c;网络安全领域正经历三重根本性变革&#xff1a; 攻击者门槛坍缩&#xff1a;生成式 AI 将网络钓鱼开发效率提升 300%&#xff0c;勒索软件即服务&#xff08;RaaS&#xff09;订阅用户突破…...

学习笔记—双指针算法—移动零

双指针算法 移动零 283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进…...

计算机网络全栈精讲:从 TCP/UDP 原理到 Socket 编程与 HTTP 协议实战(含代码实现)

计算机网络作为现代信息技术的基石&#xff0c;支撑着互联网、物联网、云计算等众多领域的发展。无论是前端开发者、后端工程师&#xff0c;还是运维人员&#xff0c;深入理解计算机网络原理都至关重要。本文将从网络分层模型出发&#xff0c;逐步深入讲解 TCP/UDP 协议、Socke…...

IP地址如何切换到国内别的省份?一步步指导

使用换IP工具的主要目的是通过更换设备的公网IP地址来满足特定需求&#xff0c;例如绕过限制、保护隐私或完成特定任务。以下是常见的应用场景和原因&#xff1a; 一、ip应用场景 1. 绕过IP限制 访问地域限制内容&#xff1a;某些网站或服务&#xff08;如游戏、社交平台 &am…...

c++流对象

核心概念回顾&#xff1a; C 的流库 (<iostream>, <fstream>, <sstream>) 提供了一种统一的方式来处理输入和输出&#xff0c;无论数据是来自键盘、文件还是内存中的字符串。它们都基于 std::istream (输入流基类) 和 std::ostream (输出流基类)&#xff0c…...

华为IP(5)

交换机的堆叠与集群 堆叠和集群指的是同一件事 前言: 随着企业的发展&#xff0c;企业网络的规模越来越大&#xff0c;这对企业网络提出了更高的要求&#xff1a;更高的可靠性、更低的故障恢复时间、设备更加易于管理等。 传统的园区网高可靠性技术出现故障时很难做到毫秒级…...

零信任架构下的等保 2.0 与密评密改双合规

随着《网络安全等级保护2.0》和《商用密码应用安全性评估》的深入实施&#xff0c;企业面临双重合规压力&#xff1a;既要满足等保2.0对“主动防御”和“动态防护”的要求&#xff0c;又要通过密评密改强化密码技术的合规性。传统安全架构依赖边界防护和静态密码策略&#xff0…...

华为 MRAG:多模态检索增强生成技术论文阅读

GitHub项目链接&#xff1a;https://github.com/PanguIR/MRAGSurvey 总览 多模态检索增强生成&#xff08;MRAG&#xff09;通过将文本、图像、视频等多模态数据整合到检索与生成过程中&#xff0c;显著提升了多模态大语言模型&#xff08;MLLM&#xff09;的性能。传统检索增…...

文字光影扫过动效

列子1 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>光影扫过文字动效</title><styl…...

SpringBoot配置RestTemplate并理解单例模式详解

在日常开发中&#xff0c;RestTemplate 是一个非常常用的工具&#xff0c;用来发起HTTP请求。今天我们通过一个小例子&#xff0c;不仅学习如何在SpringBoot中配置RestTemplate&#xff0c;还会深入理解单例模式在Spring中的实际应用。 1. 示例代码 我们首先来看一个基础的配置…...

计算机网络应用层(5)-- P2P文件分发视频流和内容分发网

&#x1f493;个人主页&#xff1a;mooridy &#x1f493;专栏地址&#xff1a;《计算机网络&#xff1a;自顶向下方法》 大纲式阅读笔记_mooridy的博客-CSDN博客 &#x1f493;本博客内容为《计算机网络&#xff1a;自顶向下方法》第二章应用层第五、六节知识梳理 关注我&…...

C++ TCP通信原理与实现

C 中 TCP 通信的原理基于 TCP/IP 协议栈的实现&#xff0c;以下是核心原理和关键步骤的详细说明&#xff1a; 一、TCP 通信核心原理 面向连接 通信双方需通过 三次握手 建立可靠连接&#xff0c;确保通信通道稳定。通过 四次挥手 断开连接&#xff0c;保证数据完整性。 可靠传…...

计算机网络-运输层(1)

计算机网络-运输层(1) 文章目录 计算机网络-运输层(1)5.1 运输层概述5.2 运输层端口号、复用与分用端口号基本概念端口号特性端口号分类重要说明 5.3 UDP与TCP协议对比关键区别说明 5.1 运输层概述 计算机网络体系结构中的物理层、数据链路层以及网络层共同解决了主机通过异构…...

学习spark-streaming收获

1.流处理的核心概念 •实时 vs微批处理&#xff1a;理解了 Spark Streaming 的微批处理&#xff08;Micro-Batch&#xff09;模型&#xff0c;将流数据切分为小批次&#xff08;如1秒间隔&#xff09;进行处理&#xff0c;与真正的流处理&#xff08;如Flink&#xff09;的区…...

蓝桥杯 14. 奇怪的数列

奇怪的数列 原题目链接 题目描述 从 X 星截获一份电码&#xff0c;是一些数字&#xff0c;如下&#xff1a; 13 1113 3113 132113 1113122113 ⋯⋯YY 博士经彻夜研究&#xff0c;发现了规律&#xff1a; 第一行的数字随便是什么&#xff0c;以后每一行都是对上一行 “读出…...

前端高频面试题day2

如何在vue3中使用defineAsyncComponent实现异步组件加载 在 Vue 3 中&#xff0c;使用 defineAsyncComponent 实现异步组件加载的步骤如下&#xff1a; 引入方法&#xff1a;从 Vue 中导入 defineAsyncComponent。定义异步组件&#xff1a;通过 defineAsyncComponent 包装一个…...

Linux系统之设置开机启动运行桌面环境

Linux 开机运行级别介绍与 Ubuntu 桌面环境配置指南 一、Linux 开机运行级别(Runlevel) 在传统的 Linux 系统(如 SysV init 初始化系统)中,运行级别定义了系统启动时加载的服务和资源。常见的运行级别如下: 运行级别模式用途0Halt(停机模式)关闭系统1Single User Mode…...

Python PyAutoGUI库【GUI 自动化库】深度解析与实战指南

一、核心工作原理 底层驱动机制&#xff1a; 通过操作系统原生API模拟输入使用ctypes库调用Windows API/Mac Cocoa/Xlib屏幕操作依赖Pillow库进行图像处理 事件模拟流程&#xff1a; #mermaid-svg-1CGDRNzFNEffhvSa {font-family:"trebuchet ms",verdana,arial,sans…...

【MobaXterm】win10下载v25.1安装流程

【下载地址】 官网&#xff1a; https://mobaxterm.mobatek.net/ 下载安装版&#xff0c;解压使用更快一些 【v20.0中文安装包】 夸克网盘&#xff1a;https://pan.quark.cn/s/2ad5b59e6d8e#/list/share 对应的指导教程&#xff1a; MobaXterm中文版安装使用教程-附安装包…...

2025.4.22 JavaScript 常用事件学习笔记

一、事件概述 JavaScript 事件是指在用户与网页交互或网页状态发生变化时所触发的操作。通过使用事件&#xff0c;可以为网页添加丰富的动态功能&#xff0c;实现用户与页面之间的互动&#xff0c;让网页不再只是静态的展示内容。 二、常见鼠标事件 click 事件 简介 &#xf…...

Android 13.0 MTK Camera2 设置默认拍照尺寸功能实现

Android 13.0 MTK Camera2 设置默认拍照尺寸功能实现 文章目录 需求&#xff1a;参考资料架构图了解Camera相关专栏零散知识了解部分相机源码参考&#xff0c;学习API使用&#xff0c;梳理流程&#xff0c;偏应用层Camera2 系统相关 修改文件-修改方案修改文件&#xff1a;修改…...

Linux:基础IO 文件系统

Linux:基础IO && 文件系统 一、系统IO&#xff08;一&#xff09;系统文件操作接口1、open2、write3、read &#xff08;二&#xff09;文件描述符1、概念2、标准输入、标准输出、标准错误 &#xff08;三&#xff09;dup系统调用&#xff08;重定向原理&#xff09; 二…...

近期有哪些断链危机?如何提升供应链风险管理能力?

全球供应链格局正经历深刻变革&#xff0c;其网络架构愈发复杂&#xff0c;涉及多国企业主体且涵盖多个节点与复杂环节&#xff0c;管理难度显著增大。从原材料采购到终端交付&#xff0c;运输、仓储、加工等任一环节均存在潜在风险&#xff0c;单一环节效率滞后易引发系统性连…...

知识科普|褪黑素的发展历程及应用研究进展

睡眠作为维持人体健康的重要生理机制&#xff0c;其节律性受到精密调控。在昼夜节律系统的调控下&#xff0c;人类普遍遵循周期性单次睡眠模式&#xff0c;这一过程涉及复杂的神经-体液调控网络。其中神经元活动、神经递质传导、激素分泌及遗传调控机制共同作用于睡眠觉醒系统&…...

企业如何构建一个全面的Web安全防护体系

企业如何构建一个全面的Web安全防护体系 企业构建全面的Web安全防护体系需融合战略规划、技术防御、持续运营和风险治理四大维度&#xff0c;以下是基于行业最佳实践的系统化方案&#xff1a; 一、顶层设计&#xff1a;治理架构与安全战略 战略规划与合规驱动 制定网络安全愿…...

T8332FN凯钰LED驱动芯片多拓扑车规级AEC-Q100

T8332FN是一款支持多拓扑结构的恒流LED驱动控制芯片&#xff0c;适用于汽车照明及高功率LED应用&#xff0c;具备宽电压输入、高精度调光及多重保护功能。 核心特性 - 输入与拓扑&#xff1a;支持5-60V宽电压输入&#xff0c;适配Boost、Buck、Buck-Boost、SEPIC四种拓扑结构&…...

Redis一些小记录

Redis一些小记录 SpringData Redis&#xff1a;RedisTemplate配置与数据操作 操作String类型数据 String是Redis中最基本的数据类型&#xff0c;可以存储字符串、整数或浮点数。RedisTemplate提供了ValueOperations接口来操作String类型的数据&#xff0c;支持设置值、获取值、…...

Win10安装 P104-100 驱动

安装完之后总结一下, 之前做了不少功课, 在网上搜了很多教程, 视频的文字的, 但是很多已经比较陈旧了. 最后发现的这个 GitHub 项目 NVIDIA-patcher 是最有用的, 因为这是现在这些魔改驱动的来源. NVIDIA-patcher 仓库地址: https://github.com/dartraiden/NVIDIA-patcher 安…...

Android开机动画资源包制作(测试使用)

开机动画资源包需要采用仅存储的方式进行压缩&#xff0c;不能使用压缩软件直接压缩生成。 如果是系统开发人员&#xff0c;可以在源码目录中&#xff0c;采用bootanim程序提供的制作方式进行&#xff0c;下面可供测试人员自行制作。 制作流程 1&#xff09;基于设备中已有开…...

PWN基础-利用格式化字符串漏洞泄露canary结合栈溢出getshell

测试源码&#xff1a; #include<stdio.h> void exploit() {system("/bin/sh"); } void func() {char str[0x20];read(0, str, 0x50);printf(str);read(0, str, 0x50); } int main() {func();return 0; } 编译&#xff0c;开启 canary 保护&#xff0c;关闭 p…...

Kafka HA集群配置搭建与SpringBoot使用示例总结

Kafka HA集群配置搭建与SpringBoot使用示例总结 一、Kafka高可用(HA)集群搭建 1. 环境准备 至少3台服务器&#xff08;推荐奇数台&#xff0c;如3、5、7&#xff09;已安装Java环境&#xff08;JDK 1.8&#xff09;下载Kafka二进制包&#xff08;如kafka_2.13-3.2.1.tgz&…...

MSO-Player:基于vlc的Unity直播流播放器,支持主流RTSP、RTMP、HTTP等常见格式

MSO-Player 基于libVLC的Unity视频播放解决方案 支持2D视频和360度全景视频播放的Unity插件 &#x1f4d1; 目录 &#x1f3a5; MSO-Player &#x1f4cb; 功能概述&#x1f680; 快速入门&#x1f4da; 关键组件&#x1f4dd; 使用案例&#x1f50c; 依赖项&#x1f4cb; 注意…...

97A6-ASEMI无人机专用功率器件97A6

编辑&#xff1a;ll 97A6-ASEMI无人机专用功率器件97A6 型号&#xff1a;97A6 品牌&#xff1a;ASEMI 封装&#xff1a;SOT-23 批号&#xff1a;最新 引脚数量&#xff1a;3 特性&#xff1a;双向可控硅 工作温度&#xff1a;-40℃~150℃ ‌97A6双向可控硅&#xff1a…...

body Param Query 三个 不同的入参 分别是什么意思 在前端 要怎么传 这三种不同的参数

在 NestJS 中&#xff0c;Body()、Param() 和 Query() 用于处理不同类型的请求参数。以下是它们的含义及前端传递方式&#xff1a; Body()&#xff1a;请求体参数 • 含义&#xff1a;用于获取请求体中的数据&#xff08;如 POST/PUT 请求中提交的 JSON、表单数据等&#xff09…...

生成式人工智能认证(GAI认证)含金量怎么样?

当生成式人工智能(Generative AI)的浪潮以摧枯拉朽之势重塑职业版图时,一个尖锐的问题正悬在无数人的心头:在技术迭代比眨眼更快的时代,如何证明自己具备驾驭AI的核心能力? 这场认知革命的背后,一张认证证书的价值早已超越了纸面——它既是个人能力的“信用背书”,也是…...

环境DNA宏条形码技术,鱼类检测引物如何选择?

环境DNA&#xff08;eDNA&#xff09;宏条形码技术在鱼类多样性调查研究中的优势明显&#xff0c;相比于传统调查方式&#xff0c;eDNA宏条形码技术灵敏度更高&#xff0c;能够更好地揭示鱼类的丰富度&#xff0c;并且具有高时效性。然而&#xff0c;在使用这个技术的过程中&am…...

Scala集合操作与WordCount案例实战总结

集合计算简单函数 1、说明 &#xff08;1&#xff09;求和 &#xff08;2&#xff09;求乘积 &#xff08;3&#xff09;最大值 &#xff08;4&#xff09;最小值 &#xff08;5&#xff09;排序 2、案例实操 object demo29{ def main(args: Array[String]): Unit { val…...

Spark-Streaming核心编程(四)总结

有状态转化操作 - UpdateStateByKey ‌功能描述‌ UpdateStateByKey原语用于在DStream中跨批次维护状态&#xff0c;例如流计算中的累加wordcount。 它允许对一个状态变量进行访问和更新&#xff0c;适用于键值对形式的DStream。 ‌工作原理‌ 给定一个由(键&#xff0c;事…...

关系型数据库PostgreSQL for Mac 保姆级使用教程

第一部分&#xff1a;安装PostgreSQL 方法一&#xff1a;使用Postgres.app&#xff08;最简单&#xff09; 访问 Postgres.app官网 下载最新版本&#xff0c;将 Postgres.app 移动到 “Applications” 文件夹。 双击Postgres.app打开应用&#xff0c;点击"Initialize&q…...

新增 29 个专业,科技成为关键赛道!

近日&#xff0c;教育部正式发布《普通高等学校本科专业目录&#xff08;2025年&#xff09;》&#xff0c;新增 29 个本科专业&#xff0c;包括区域国别学、碳中和科学与工程、海洋科学与技术、健康与医疗保障、智能分子工程、医疗器械与装备工程、时空信息工程、国际邮轮管理…...

云计算市场的重新分类研究

云计算市场传统分类方式&#xff0c;比如按服务类型分为IaaS、PaaS、SaaS&#xff0c;或者按部署模式分为公有云、私有云、混合云。主要提供计算资源、存储和网络等基础设施。 但随着AI大模型的出现&#xff0c;云计算市场可以分为计算云和智算云&#xff0c;智算云主要是AI模…...

大模型时代的具身智能:从虚拟到现实的智能体进化革命

一、具身智能&#xff1a;重新定义 AI 与物理世界的交互范式 &#xff08;一&#xff09;概念解析&#xff1a;从 "离身" 到 "具身" 的认知革命 具身智能&#xff08;Embodied AI&#xff09;是融合大模型决策能力与物理实体执行能力的新型智能系统&…...

鸿蒙NEXT开发正则工具类(ArkTs)

import { FormatUtil } from ./FormatUtil;/*** 正则工具类* author CSDN-鸿蒙布道师* since 2025/04/27*/ export class RegexUtil {/*** 英文字母、数字和下划线*/static readonly REG_GENERAL "^\\w$";/*** 数字*/static readonly REG_NUMBERS "^\\d$"…...

Flink维表深度解析

一、维表的概念与作用 维表&#xff08;Dimension Table&#xff09; 是数据仓库中的核心概念&#xff0c;通常用于存储静态或缓慢变化的业务实体信息&#xff08;如用户资料、商品信息、地理位置等&#xff09;。在实时流处理场景中&#xff0c;维表的作用是为主数据流&#…...

基于ArcGIS的洪水灾害普查、风险评估及淹没制图技术研究​

一、洪水普查技术规范解读 1.1 全国水旱灾害风险普查实施方案解读 1.2 洪水风险区划及防治区划编制技术要求解读 1.3 山丘区中小河流洪水淹没图编制技术要求解读 二、ArcGIS介绍及数据管理 2.1 ArcGIS界面及数据加载 2.2 ArcGIS常见数据格式 2.3基于Geodatabase的洪水灾…...

初识数据结构——二叉树从基础概念到实践应用

数据结构专栏 ⬅(click) 初识二叉树&#xff1a;从基础概念到实践应用&#x1f333; 一、树型结构基础 1.1 树的基本概念 树是一种非线性的数据结构&#xff0c;由n(n>0)个有限节点组成一个具有层次关系的集合。它看起来像一棵倒挂的树&#xff0c;根朝上而叶朝下。 关键特…...

手搓传染病模型(SEIR)

先看模型 在本模型中&#xff0c;人群有四种自然史状态&#xff1a;易感者(S)&#xff0c;暴露者(E)&#xff0c;感染者(I)以及康复者(R) 2.模型假设人群分布是同质均匀的&#xff0c;未考虑人群出生、死亡、迁入迁出对疾病传播的影响 3.康复者永久免疫&#xff1a;康复者永久免…...

企业数据赋能 | 应用模板分享:汽车销售仪表板

实时监控销售数据&#xff0c;比较车型、地区业绩~ 今天&#xff0c;小编向大家分享 Tableau 应用分析模板&#xff1a;由 Imran Shaikh 搭建的汽车销售仪表板。借助此仪表板&#xff0c;企业可以实时跟踪销售情况&#xff0c;了解市场趋势&#xff0c;并比较不同车型、地区和销…...

C++?动态内存管理!!!

一、引言 之前我们一起讨论了类和对象的相关知识&#xff0c;接下来我们将继续完善我们的知识体系&#xff0c;为以后继续深入学习C知识添砖加瓦&#xff0c;在本期我们将一起学习C中关于动态内存管理的相关知识&#xff0c;在学习之前将要先回顾C语言中是如何进行动态内存管理…...