SpringBoot+VUE2完成WebSocket聊天(数据入库)

下载依赖

        <!-- websocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- MybatisPlus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.0</version></dependency>

数据库sql

CREATE TABLE `interrogation`  (`ID` int(0) NOT NULL AUTO_INCREMENT COMMENT 'id',`INITIATOR_ID` int(0) NULL DEFAULT NULL COMMENT '发起人的ID()',`DOCTOR` int(0) NULL DEFAULT NULL COMMENT '接受人ID',`STATUS` int(0) NULL DEFAULT NULL COMMENT '状态(1.开始聊天和2.结束)',PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '聊天列表' ROW_FORMAT = Dynamic;CREATE TABLE `interrogation_chat`  (`ID` int(0) NOT NULL AUTO_INCREMENT COMMENT 'ID',`INTERROGATION_ID` int(0) NULL DEFAULT NULL COMMENT '聊天表的id',`CONTENT` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '内容',`INITIATOR_ID` int(0) NULL DEFAULT NULL COMMENT '发送人',`CREATE_TIME` datetime(0) NULL DEFAULT NULL COMMENT '发送时间',PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 130 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '聊天记录表' ROW_FORMAT = Dynamic;

实体类 

后续业务采用mybatils-plus写的,如果不会使用手写sql也是可以的

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;import java.util.Date;/*** @description:  聊天记录表* @author: * @date: 2024/10/27 9:37**/
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class InterrogationChat {/*** ID**/@TableId(value = "ID", type = IdType.AUTO)private Integer id;/*** 聊天表的id**/@TableField("INTERROGATION_ID")private Integer interrogationId;/*** 内容**/@TableField("CONTENT")private String content;/*** 发送人**/@TableField("INITIATOR_ID")private Integer initiatorId;/*** 发送时间**/@TableField("CREATE_TIME")@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date createTime;
}

websocker配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** @author * @date 2024/10/23* @apiNote*/@Configuration
public class WebSocketConfig {/*** 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;import javax.websocket.server.ServerEndpointConfig;/*** @author * @date 2024/10/30* @apiNote*/public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware {private static volatile BeanFactory context;@Overridepublic <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException{return context.getBean(clazz);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException{System.out.println("auto load"+this.hashCode());MyEndpointConfigure.context = applicationContext;}
}import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @author * @date 2024/10/30* @apiNote*/@Configuration
public class MyConfigure {@Beanpublic MyEndpointConfigure newConfigure(){return new MyEndpointConfigure();}}import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.*;import java.io.Serializable;/*** @description: 聊天列表* @author: * @date: 2024/10/27 9:30**/
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class Interrogation implements Serializable {/*** ID**/@TableId(value = "ID", type = IdType.AUTO)private Integer id;/*** 发起人ID**/@TableField("INITIATOR_ID")private Integer initiatorId;/*** 收到人ID**/@TableField("DOCTOR")private Integer doctor;/*** 状态(1.开始聊天和2.结束)**/@TableField("STATUS")private Integer status;}

websocket业务代码

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.dai.config.MyEndpointConfigure;
import com.ruoyi.system.RemoteSendService;
import com.ruoyi.system.domain.InterrogationChat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @author * @date 2024/10/23* @apiNote*/@ServerEndpoint(value = "/imserver/{username}", configurator = MyEndpointConfigure.class)
@Component
public class WebSocketServer {@Autowiredprivate RemoteSendService remoteSendService;private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);/*** 记录当前在线连接数*/public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("username") String username) {sessionMap.put(username, session);log.info("有新用户加入,username={}, 当前在线人数为:{}", username, sessionMap.size());JSONObject result = new JSONObject();JSONArray array = new JSONArray();result.set("users", array);for (Object key : sessionMap.keySet()) {JSONObject jsonObject = new JSONObject();jsonObject.set("username", key);array.add(jsonObject);}sendAllMessage(JSONUtil.toJsonStr(result));}/*** 连接关闭调用的方法*/@OnClosepublic void onClose(Session session, @PathParam("username") String username) {sessionMap.remove(username);log.info("有一连接关闭,移除username={}的用户session, 当前在线人数为:{}", username, sessionMap.size());}/*** 收到客户端消息后调用的方法* 后台收到客户端发送过来的消息* onMessage 是一个消息的中转站* 接受 浏览器端 socket.send 发送过来的 json数据** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session session, @PathParam("username") String username) {log.info("服务端收到用户username={}的消息:{}", username, message);JSONObject obj = JSONUtil.parseObj(message);String toUsername = obj.getStr("to");String text = obj.getStr("content"); // 注意这里修改成了 "content"//非初始化消息转发和缓存if (!text.equals("初始化连接")){Session toSession = sessionMap.get(toUsername);if (toSession != null) {JSONObject jsonObject = new JSONObject();jsonObject.set("id",obj.getLong("id"));jsonObject.set("interrogationId",obj.getInt("interrogationId"));jsonObject.set("content",text); // 注意这里修改成了 "content"jsonObject.set("createTime",obj.getDate("createTime")); // 注意这里修改成了 "createTime"jsonObject.set("initiatorId",obj.getInt("initiatorId")); // 注意这里修改成了 "initiatorId"this.sendMessage(jsonObject.toString(), toSession);// 数据库记录聊天记录InterrogationChat interrogationChat = new InterrogationChat();interrogationChat.setInterrogationId(obj.getInt("interrogationId"));interrogationChat.setContent(text); // 注意这里修改成了 "content"interrogationChat.setCreateTime(obj.getDate("createTime")); // 注意这里修改成了 "createTime"interrogationChat.setInitiatorId(obj.getInt("initiatorId")); // 注意这里修改成了 "initiatorId"remoteSendService.sendMessage(interrogationChat);log.info("发送给用户username={},消息:{}", toUsername, jsonObject.toString());} else {log.info("发送失败,未找到用户username={}的session", toUsername);}}}@OnErrorpublic void onError(Session session, Throwable error) {log.error("发生错误");error.printStackTrace();}/*** 服务端发送消息给客户端*/private void sendMessage(String message, Session toSession) {try {log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);toSession.getBasicRemote().sendText(message);} catch (Exception e) {log.error("服务端发送消息给客户端失败", e);}}/*** 服务端发送消息给所有客户端*/private void sendAllMessage(String message) {try {for (Session session : sessionMap.values()) {log.info("服务端给客户端[{}]发送消息{}", session.getId(), message);session.getBasicRemote().sendText(message);}} catch (Exception e) {log.error("服务端发送消息给客户端失败", e);}}
}

聊天业务代码

controller

@RestController
@RequestMapping("/send")
public class SendController {@Resourceprivate SendService sendService;/*** 聊天消息入库**/@PostMapping("/sendMessage")public R<String> sendMessage(@RequestBody InterrogationChat message) {return R.ok(sendService.sendMessage(message));}/*** 发起一个聊天**/@PostMapping("/launchInterrogation")public R<String> launchInterrogation(@RequestBody Interrogation interrogation ){return R.ok(sendService.launchInterrogation(interrogation));}
}

service

public interface SendService {String sendMessage(InterrogationChat message);String launchInterrogation(Interrogation interrogation);
}

impl

@Service
public class SendServiceImpl implements SendService {@Resourceprivate InterrogationChatMapper interrogationChatMapper;@Resourceprivate InterrogationMapper interrogationMapper;@Overridepublic String sendMessage(InterrogationChat message) {try {interrogationChatMapper.insert(message);return "发送成功";}catch (Exception e){e.printStackTrace();return "发送失败";}}@Overridepublic String launchInterrogation(Interrogation interrogation) {// 有没有Interrogation ones = interrogationMapper.selectOne(new QueryWrapper<Interrogation>().eq("INITIATOR_ID",interrogation.getInitiatorId()).eq("DOCTOR",interrogation.getDoctor()).eq("STATUS",1));// 为空创建if (StringUtils.isNull(ones)){interrogation.setStatus(1);int  insert = interrogationMapper.insert(interrogation);return  interrogation.getId().toString();}// 有就直接返回return ones.getId().toString();}
}

mapper

@Mapper
public interface InterrogationMapper extends BaseMapper<Interrogation> {
}@Mapper
public interface InterrogationChatMapper extends BaseMapper<InterrogationChat> {
}

前端VUE代码

index.vue  第一个页面

<template><div><h2>欢迎来到聊天室</h2><div v-for="message in messages" :key="message.id" class="message-container"><div v-if="message.initiatorId !== id" class="message-received"><span class="message-text">{{ message.content }}</span></div><div v-else class="message-sent"><span class="message-text">{{ message.content }}</span><span class="time">{{ message.createTime }}</span></div></div><input type="text" v-model="newMessage" placeholder="输入消息" /><button @click="sendMessage">发送</button></div>
</template><script>
import { launchInterrogation, historyInterrogation } from '@/api/interrogation/interrogationType';export default {components: {},props: {},data() {return {interrogation: {id: null,initiatorId: 101,doctor: 1},messages: [],newMessage: '',ws: null,id: 1,isConnected: false,interrogationId: null,};},computed: {},watch: {},methods: {launchInterrogation() {launchInterrogation(this.interrogation).then(res => {this.interrogationId = res.data;historyInterrogation(this.interrogationId).then(res => {console.log("查询到的历史记录:", res.data.interrogationChats);if (res.data != null) {this.messages = res.data.interrogationChats || [];}});});},connectToWebSocket() {const wsUrl = `ws://ip:port/imserver/${this.id}`;this.ws = new WebSocket(wsUrl);this.ws.onopen = () => {console.log('WebSocket 连接已打开');this.isConnected = true;// 在 WebSocket 连接完全建立后再发送初始化消息this.sendMessageIfConnected();};this.ws.onclose = () => {console.log('WebSocket 连接已关闭');this.isConnected = false;};this.ws.onerror = (error) => {console.error('WebSocket 错误', error);this.isConnected = false;};this.ws.onmessage = (event) => {const message = JSON.parse(event.data);if (message.content !== undefined) {console.log("获取到的回调数据:", message);this.messages.push(message);} else {console.warn('接收到的数据无效或格式错误:', event.data);}};},sendMessage() {if (this.newMessage.trim() === '') return;const now = new Date();const formattedTime = now.toISOString().slice(0, 19).replace('T', ' ');const message = {interrogationId: this.interrogationId,id: Date.now(),content: this.newMessage,createTime: formattedTime,initiatorId: this.id,to: '101',};if (this.isConnected) {this.messages.push(message);this.ws.send(JSON.stringify(message));this.newMessage = '';} else {console.warn('WebSocket 连接尚未建立,消息未发送');}},sendMessageIfConnected() {if (this.isConnected) {const initialMessage = {interrogationId: this.interrogationId,id: Date.now(),content: '初始化连接',initiatorId: this.id,createTime: new Date().toISOString().slice(0, 19).replace('T', ' '),to: '101',};this.ws.send(JSON.stringify(initialMessage));}}},created() {this.connectToWebSocket();this.launchInterrogation();},mounted() {},beforeCreate() {},beforeMount() {},beforeUpdate() {},updated() {},beforeDestroy() {if (this.ws && this.ws.readyState === WebSocket.OPEN) {this.ws.close();}},destroyed() {},activated() {}
}
</script><style scoped>
.message-container {display: flex;align-items: flex-start;margin-bottom: 10px;
}.message-received {margin-right: auto;background-color: #ddd;border-radius: 10px;padding: 10px;max-width: 70%;word-wrap: break-word;
}.message-sent {margin-left: auto;background-color: #00bfff;border-radius: 10px;padding: 10px;max-width: 70%;word-wrap: break-word;display: flex;flex-direction: column;align-items: flex-end;
}.message-text {display: block;color: #333;font-size: 1em;
}.time {font-size: 0.8em;color: #999;margin-top: 5px;
}input {width: 200px;
}
button {margin-left: 10px;
}
</style>

inde1.vue  第二个页面

<template><div><h2>欢迎来到聊天室</h2><div v-for="message in messages" :key="message.id" class="message-container"><div v-if="message.initiatorId !== id" class="message-received"><span class="message-text">{{ message.content }}</span></div><div v-else class="message-sent"><span class="message-text">{{ message.content }}</span><span class="time">{{ message.createTime }}</span></div></div><input type="text" v-model="newMessage" placeholder="输入消息" /><button @click="sendMessage">发送</button></div>
</template><script>
import { launchInterrogation, historyInterrogation } from '@/api/interrogation/interrogationType';export default {components: {},props: {},data() {return {interrogation: {id: null,initiatorId: 101,doctor: 1},messages: [],newMessage: '',ws: null,id: 101,isConnected: false,interrogationId: null,};},computed: {},watch: {},methods: {launchInterrogation() {launchInterrogation(this.interrogation).then(res => {this.interrogationId = res.data;historyInterrogation(this.interrogationId).then(res => {console.log("查询到的历史记录:", res.data.interrogationChats);if (res.data != null) {this.messages = res.data.interrogationChats || [];}});});},connectToWebSocket() {const wsUrl = `ws://ip:port/imserver/${this.id}`;this.ws = new WebSocket(wsUrl);this.ws.onopen = () => {console.log('WebSocket 连接已打开');this.isConnected = true;this.sendMessageIfConnected();};this.ws.onclose = () => {console.log('WebSocket 连接已关闭');this.isConnected = false;};this.ws.onerror = (error) => {console.error('WebSocket 错误', error);this.isConnected = false;};this.ws.onmessage = (event) => {const message = JSON.parse(event.data);if (message.content !== undefined) {console.log("获取到的回调数据:", message);this.messages.push(message);} else {console.warn('接收到的数据无效或格式错误:', event.data);}};},sendMessage() {if (this.newMessage.trim() === '') return;const now = new Date();const formattedTime = now.toISOString().slice(0, 19).replace('T', ' ');const message = {//问诊记录idinterrogationId: this.interrogationId,id: Date.now(),content: this.newMessage,createTime: formattedTime,initiatorId: this.id,to: '1',};if (this.isConnected) {this.messages.push(message);this.ws.send(JSON.stringify(message));this.newMessage = '';} else {console.warn('WebSocket 连接尚未建立,消息未发送');}},sendMessageIfConnected() {if (this.isConnected) {const initialMessage = {//问诊记录idinterrogationId: this.interrogationId,id: Date.now(),content: '初始化连接',initiatorId: this.id,createTime: new Date().toISOString().slice(0, 19).replace('T', ' '),to: '1',};this.ws.send(JSON.stringify(initialMessage));}}},created() {this.connectToWebSocket();this.launchInterrogation();},mounted() {},beforeCreate() {},beforeMount() {},beforeUpdate() {},updated() {},beforeDestroy() {if (this.ws && this.ws.readyState === WebSocket.OPEN) {this.ws.close();}},destroyed() {},activated() {}
}
</script><style scoped>
.message-container {display: flex;align-items: flex-start;margin-bottom: 10px;
}.message-received {margin-right: auto;background-color: #ddd;border-radius: 10px;padding: 10px;max-width: 70%;word-wrap: break-word;
}.message-sent {margin-left: auto;background-color: #00bfff;border-radius: 10px;padding: 10px;max-width: 70%;word-wrap: break-word;display: flex;flex-direction: column;align-items: flex-end;
}.message-text {display: block;color: #333;font-size: 1em;
}.time {font-size: 0.8em;color: #999;margin-top: 5px;
}input {width: 200px;
}
button {margin-left: 10px;
}
</style>

js方法代码

import request from '@/utils/request'export function launchInterrogation(data) {return request({url: '/send/launchInterrogation',method: 'post',data})
}export function historyInterrogation(param){return request({url: '/send/historyInterrogation?id='+param,method: 'get',})
}

效果

 

刷新聊天记录不会消失

注意:因为服务器时间比系统时间慢八个小时,注意同步一下,不然可能发送完消息刷新页面,从数据库读取到的消息发送时间会慢八个小时

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/59105.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图文深入介绍Oracle DB link(一)

1. 引言&#xff1a; 本文图文深入介绍Oracle DB link&#xff0c;先介绍基本概念。 2.DB link的定义 数据库链接&#xff08;Database Link&#xff0c;简称 DB Link&#xff09;是 Oracle 数据库中的一个重要功能。它是一种在一个 Oracle 数据库实例中访问另一个 Oracle 数…

MoonBit 双周报 Vol.59:新增编译器常量支持,改进未使用警告,支持跨包函数导入...多个关键技术持续优化中!

2024-11-04 MoonBit更新 增加了编译期常量的支持。常量的名字以大写字母开头&#xff0c;用语法 const C ... 声明。常量的类型必须是内建的数字类型或 String。常量可以当作普通的值使用&#xff0c;也可以用于模式匹配。常量的值目前只能是字面量&#xff1a; const MIN_…

HTB:Shocker[WriteUP]

目录 连接至HTB服务器并启动靶机 1.How many TCP ports are listening on Shocker? 使用nmap对靶机TCP端口进行开放扫描 2.What is the name of the directory available on the webserver that is a standard name known for running scripts via the Common Gateway Int…

力扣——另一个的子树(C语言)

1.题目&#xff1a; 给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree …

【C++】对左值引用右值引用的深入理解(右值引用与移动语义)

&#x1f308; 个人主页&#xff1a;谁在夜里看海. &#x1f525; 个人专栏&#xff1a;《C系列》《Linux系列》 ⛰️ 天高地阔&#xff0c;欲往观之。 ​ 目录 前言&#xff1a;对引用的底层理解 一、左值与右值 提问&#xff1a;左值在左&#xff0c;右值在右&#xff1f;…

解决 ClickHouse 高可用集群中 VRID 冲突问题:基于 chproxy 和 keepalived 的实践分析

Part1背景描述 近期&#xff0c;我们部署了两套 ClickHouse 生产集群&#xff0c;分别位于同城的两个数据中心。这两套集群的数据保持一致&#xff0c;以便在一个数据中心发生故障时&#xff0c;能够迅速切换应用至另一个数据中心的 ClickHouse 实例&#xff0c;确保服务连续性…

B2C电商平台如何提升转化率 小程序商城如何做好运营

在竞争激烈的电商市场中&#xff0c;提升转化率是每个B2C电商平台的重要目标。转化率直接影响销售业绩和盈利能力&#xff0c;因此&#xff0c;了解如何优化用户体验、增强客户信任和提高购买动机是至关重要的。商淘云分享一些有效的策略&#xff0c;帮助B2C电商平台提升转化率…

RK3568平台开发系列讲解(字符设备驱动篇)Linux设备分类

🚀返回专栏总目录 文章目录 一、字符设备(是以字节为单位进行输入输出)二、块设备:块设备是以块为单位进行输入输出三、网络设备沉淀、分享、成长,让自己和他人都能有所收获!😄 一、字符设备(是以字节为单位进行输入输出) 串口、鼠标 字符设备没有固定的大小,也没…

STM32Fxx读写eeprom(AT24C16)

一.I2C 协议简介 I2C 通讯协议 (Inter &#xff0d; Integrated Circuit) 是由 Phiilps 公司开发的&#xff0c;由于它引脚少&#xff0c;硬件实现简单&#xff0c;可扩展性强&#xff0c;不需要 USART、CAN 等通讯协议的外部收发设备&#xff0c;现在被广泛地使用在系统内多个…

idea使用Translation插件实现翻译

1.打开idea&#xff0c;settings&#xff0c;选择plugins&#xff0c;搜索插件Translation&#xff0c;安装 2.选择翻译引擎 3.配置引擎&#xff0c;以有道词典为例 3.1 获取应用ID&#xff0c;应用秘钥 3.1.1 创建应用 点击进入有道智云控制台 3.1.2 复制ID和秘钥 3.2 idea设…

【论文精读】LPT: Long-tailed prompt tuning for image classification

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;论文精读_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 摘要 2. …

【SQL Server】解决因使用 varchar 类型存储 Unicode 字符串导致的中文显示乱码问题

问题描述 导入 SQL 到 SQL Server 数据库后&#xff0c;存在部分列的中文显示异常的问题。 原因分析 观察发现显示异常的字段的数据类型是 varchar&#xff0c;而显示正常的字段的数据类型是 nvarchar。 而且&#xff0c;SQL 文件中所有字符串前面都带有 N 的前缀。 在 SQL 中…

dify实战案例分享-基于多模态模型的发票识别

1 什么是dify Dify是一个开源的大语言模型&#xff08;LLM&#xff09;应用开发平台&#xff0c;旨在简化和加速生成式AI应用的创建和部署。它结合了后端即服务&#xff08;Backend as Service, BaaS&#xff09;和LLMOps的理念&#xff0c;使开发者能够快速搭建生产级的AI应用…

电机控制储备知识 一 电机驱动本质分析以及磁相关的使用场景

一&#xff1a;电机旋转的原因 1.电机基本认识 &#xff08;1&#xff09;电机是一种动力装置&#xff0c;能够将电能转换为动能 电机拥有体积小 、动力足&#xff0c;控制精细灵活的特点 完整的电机系统&#xff1a;电机&#xff08;减速器 传感器&#xff09; 电机驱动器&a…

ubuntu交叉编译dbus库给arm平台使用

1.下载dbus库源码 https://www.freedesktop.org/wiki/Software/dbus 克隆源码: https://gitlab.freedesktop.org/dbus/dbus/-/tree/dbus-1.12?ref_type=heads 下载1.12.20版本: 指定pkgconfig环境变量: export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$PWD/../expat-2.3.…

推荐一款音乐神器:想听就听,想下就下~

在这个音乐充斥生活的时代&#xff0c;你是否也曾想过&#xff0c;有一款软件可以随时随地畅听你喜欢的音乐&#xff1f;今天&#xff0c;我要向你推荐一款令人心动的音乐神器——MusicFree&#xff0c;让你真正体验“想听就听&#xff0c;想下就下”的乐趣&#xff01; 那么&a…

aws(学习笔记第十课) 对AWS的EBS如何备份(snapshot)以及使用snapshot恢复数据,AWS实例存储

aws(学习笔记第十课) 对AWS的EBS如何备份&#xff08;snapshot&#xff09;以及使用snapshot&#xff0c;AWS实例存储 学习内容&#xff1a; 对AWS的EBS如何备份AWS实例存储EBS和实例存储的不足 1. 对AWS的EBS如何备份&#xff08;snapshot&#xff09;以及使用snapshot恢复数…

Unity照片墙效果

Unity照片墙效果&#xff0c;如下效果展示 。 工程源码

qt QMenu详解

1、概述 QMenu是Qt框架中的一个类&#xff0c;用于创建和管理菜单。它提供了丰富的接口来添加菜单项&#xff08;通常是QAction对象&#xff09;、子菜单以及分隔符。QMenu可以嵌入到菜单栏&#xff08;QMenuBar&#xff09;中&#xff0c;也可以作为弹出菜单&#xff08;通过…

倍增 st表 RMQ问题

本章我们来谈谈&#xff0c;倍增 && st表 && RMQ问题。 倍增 倍增即成倍增长。是指我们在进行递推时&#xff0c;如果状态空间很大&#xff0c;线性递推无法满足时空要求&#xff0c;此时可以考虑成倍增长的方式&#xff0c;只递推状态空间在2的整数次幂位置上…