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.rhkb.cn/news/463963.html

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

相关文章

电子电气架构 --- 车载诊断的快速入门

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 所有人的看法和评价都是暂时的&#xff0c;只有自己的经历是伴随一生的&#xff0c;几乎所有的担忧和畏惧…

继承的内容

封装&#xff1a; 1.在类中&#xff0c;把数据和方法放在一起&#xff0c;只展示成员函数&#xff0c;不展示定义的数据为私有。 2.一个类型放到另一个类型里面&#xff0c;通过typedef成员函数调整&#xff0c;封装另一个全新的类型。相当于是一个包装。 继承&#xff1a; st…

设计模式之结构型模式---装饰器模式

目录 1.概述2.类图3.应用场景及优缺点3.1 应用场景3.2 优缺点3.2.1 优点3.2.2 缺点 4.实现4.1 案例类图4.2 代码实现4.2.1 定义抽象构建角色4.2.2 定义具体构建角色4.2.3 定义抽象装饰器角色4.2.4 定义具体装饰角色4.2.5 装饰器模式的使用 1.概述 装饰器模式是指在不改变现有对…

接口测试(十一)jmeter——断言

一、jmeter断言 添加【响应断言】 添加断言 运行后&#xff0c;在【察看结果树】中可得到&#xff0c;响应结果与断言不一致&#xff0c;就会红色标记

vue-i18n国际化多国语言i18n国际语言代码对照表

uniapp是自带有i18n这个插件 需要自己去给每一个需要国际化的字符去手动配置key&#xff0c;所以如果是已经完成的项目可能工作量就稍微有点大了 第一步&#xff1a; 语言命名是有规范的不能乱取名&#xff0c;具体可以参考国际语言代码 i18n国际语言代码对照表 zh_CN 中文(简体…

GitHub | 发布到GitHub仓库并联文件夹的方式

推送到Github 推送步骤如果你只想更新单个文件&#xff0c;只需在第 4 步中指定该文件的路径即可。可能问题一 效果 推送步骤 更新 GitHub 仓库中的文件通常涉及以下步骤&#xff1a; 克隆仓库&#xff1a; 首先&#xff0c;你需要将 GitHub 上的仓库克隆到本地。使用 git …

Docsify文档编辑器:Windows系统下个人博客的快速搭建与发布公网可访问

文章目录 前言1. 本地部署Docsify2. 使用Docsify搭建个人博客3. 安装Cpolar内网穿透工具4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在Windows环境本地部署 Docsify 这款以 markdown 为中心的文档编辑器&#xff0c;并即时生成您的文档博客网站&#xff0c;结合…

杂货 | 每日资讯 | 2024.11.1

注意&#xff1a;以下内容皆为AI总结 2024年11月1日&#xff0c;人工智能&#xff08;AI&#xff09;领域发生了多项重要事件&#xff0c;标志着技术发展的新阶段。本文将详细探讨以下三大事件&#xff1a; OpenAI为ChatGPT新增搜索功能IEEE发布《2025年及以后的技术影响》报…

RuoYi 样例框架运行步骤(测试项目自用,同学可自取)

目录 后台 API 运行导入&#xff0c;下载包端口号mysql 准备运行 PC&#xff08;电脑端&#xff09;运行安装 nodejs安装 yarn 及其依赖&#xff0c;启动服务登录admin(admin123) 或 ry(admin123) App&#xff08;移动&#xff09;运行下载 HBuilderX运行app运行注意&#xff1…

Puppeteer点击系统:解锁百度流量点击率提升的解决案例

在数字营销领域&#xff0c;流量和搜索引擎优化&#xff08;SEO&#xff09;是提升网站可见性的关键。我开发了一个基于Puppeteer的点击系统&#xff0c;旨在自动化地提升百度流量点击率。本文将介绍这个系统如何通过模拟真实用户行为&#xff0c;优化关键词排名&#xff0c;并…

项目解决方案:跨不同的物理网络实现视频监控多画面的实时视频的顺畅访问

目录 一、碰到的需求问题 二、需求分析 三、方案分析 &#xff08;一&#xff09;方法1&#xff1a;使用HTTP代理 1. 安装HTTP代理服务器 2. 配置Nginx代理 3. 重启Nginx 4. 访问视频流 &#xff08;二&#xff09;方法2&#xff1a;使用反向代理 1. 安装反向代理服务…

MQTT自动发送消息工具(自动化测试MQTT)

点击下载《MQTT客户端服务端工具》 点击下载《MQTT自动发送消息软件(自动化测试MQTT)》 1. 前言 在软件开发过程中&#xff0c;MQTT常被用作消息队列来完成特定的业务功能。当我们将相关业务代码编写完成后&#xff0c;通常需要编写额外的消息生产和消费代码来模拟消息高峰时…

东北虎豹国家公园shp格式范围

东北虎豹国家公园地处中国吉林、黑龙江两省交界的老爷岭南部&#xff08;珲春—汪清—东宁—绥阳&#xff09;区域&#xff0c;东起吉林省珲春林业局青龙台林场&#xff0c;与俄罗斯滨海边疆区接壤&#xff0c;西至吉林省大兴沟林业局岭东林场&#xff0c;南自吉林省珲春林业局…

练习LabVIEW第三十七题

学习目标&#xff1a; 刚学了LabVIEW&#xff0c;在网上找了些题&#xff0c;练习一下LabVIEW&#xff0c;有不对不好不足的地方欢迎指正&#xff01; 第三十七题&#xff1a; 利用XY GRAPH 构成李萨如图形 开始编写&#xff1a; 前面板放一个XY图控件&#xff0c;程序框图…

Pandas 数据可视化指南:从散点图到面积图的全面展示

Pandas 数据可视化指南&#xff1a;从散点图到面积图的全面展示 本文介绍了使用 Pandas 进行数据可视化的多种方法&#xff0c;包括散点图、折线图、条形图、直方图、饼图和面积图等&#xff0c;涵盖了常见的图表类型及其实现方式。通过提供详细的代码示例&#xff0c;展示了如…

Solana链上的Pump狙击机器人与跟单机器人的工作原理及盈利模式

随着加密货币市场的快速发展&#xff0c;越来越多的投资者和开发者开始关注Solana链上的自动化交易工具。尤其是Pump狙击机器人和跟单机器人&#xff0c;这两种工具为用户提供了在市场波动中获取利润的机会。本文将深入分析这两种机器人的工作原理及其盈利模式。 一、Pump狙击机…

嵌入式操作系统FreeRTOS

系列文章目录 嵌入式操作系统FreeRTOS 嵌入式操作系统FreeRTOS 系列文章目录一、FreeRTOS 一、FreeRTOS 首先看一下 FreeRTOS 的名字&#xff0c;可以分为两部分&#xff1a;“Free”和“RTOS”&#xff0c;“Free”就是免费的、自由的、不受约束的意思&#xff0c;“RTOS”全…

客如云:大型业务报表的分区化改造提升性能|OceanBase 应用实践

一 概述 1 背景 在过去两到三年的时间里&#xff0c;客如云的KPOS产品在商户数量和订单量均实现了数倍的增长&#xff0c;充分展示了产品的市场吸引力和业务模式的成功。然而&#xff0c;随着更多的大型连锁商户的加入&#xff0c;客如云商家平台迎来了前所未有的挑战。特别是…

Vue2指令原理手写

文件结构 index.js /** Author: RealRoad* Date: 2024-10-31 17:13:50* LastEditors: Do not edit* LastEditTime: 2024-10-31 17:15:57* Description: * FilePath: \project_10_08\vite-project\src\testVue\index.js*/ import Vue from ./Vue.js window.VueVue Vue.js imp…

手游和应用出海资讯:可灵AI独立APP即将上架;Rollic在英国推出芭比合并解谜手游

NetMarvel帮助游戏和应用广告主洞察全球市场、获取行业信息&#xff0c;以下为10月第四周资讯&#xff1a; ● 苹果开发全新游戏中心应用 ● Meta计划开发人工智能搜索引擎 ● 微软已拥有20个游戏IP&#xff0c;收入达10亿美元 ● OpenAI计划在12月推出其下一代前沿模型Orion ●…