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',})
}
效果
刷新聊天记录不会消失

注意:因为服务器时间比系统时间慢八个小时,注意同步一下,不然可能发送完消息刷新页面,从数据库读取到的消息发送时间会慢八个小时
相关文章:
SpringBoot+VUE2完成WebSocket聊天(数据入库)
下载依赖 <!-- websocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- MybatisPlus --><dependency><groupId>com.ba…...
理解 CSS 中的绝对定位与 Flex 布局混用
理解 CSS 中的绝对定位与 Flex 布局混用 在现代网页设计中,CSS 布局技术如 flex 和绝对定位被广泛使用。然而,这两者结合使用时,可能会导致一些意想不到的布局问题。本文将探讨如何正确使用绝对定位元素,避免它们受到 flex 布局的…...
Redis 事务 问题
前言 相关系列 《Redis & 目录》《Redis & 事务 & 源码》《Redis & 事务 & 总结》《Redis & 事务 & 问题》 参考文献 《Redis事务详解》 Redis事务是什么? 标准的事务是指执行时具备原子性/一致性/隔离性/持久性的一系列操作。…...
Cpp学习手册-进阶学习
C标准库和C20新特性 C标准库概览: 核心库组件介绍: 容器: C 标准库提供了多种容器,它们各有特点,适用于不同的应用场景。 std::vector: vector:动态数组,支持快速随机访问。 #in…...
代码随想录-字符串-反转字符串中的单词
题目 题解 法一:纯粹为了做出本题,暴力解 没有技巧全是感情 class Solution {public String reverseWords(String s) {//首先去除首尾空格s s.trim();String[] strs s.split("\\s");StringBuilder sb new StringBuilder();//定义一个公共的字符反转…...
勒索软件通过易受攻击的 CyberPanel 实例攻击网络托管服务器
一个威胁行为者(或可能多个)使用 PSAUX 和其他勒索软件攻击了大约 22,000 个易受攻击的 CyberPanel 实例以及运行该实例的服务器上的加密文件。 PSAUX 赎金记录(来源:LeakIX) CyberPanel 漏洞 CyberPane…...
Open WebUI + openai API / vllm API ,实战部署教程
介绍Open WebUI + Ollama 的使用: https://www.dong-blog.fun/post/1796 介绍vllm 的使用:https://www.dong-blog.fun/post/1781 介绍 Ollama 的使用: https://www.dong-blog.fun/post/1797 本篇博客玩个花的,Open WebUI 本身可以兼容openai 的api, 那来尝试一下。 仅供…...
InsuranceclaimsController
目录 1、 InsuranceclaimsController 1.1、 保险理赔结算 1.2、 生成预约单号 1.3、 保存索赔表 InsuranceclaimsController using QXQPS.Models; using QXQPS.Vo; using System; using System.Collections; using System.Collections.Generic; using System.Li…...
如何成为开源代码库Dify的contributor:解决issue并提交PR
前言 Dify 是一个开源的大语言模型(LLM)应用开发平台,它融合了后端即服务(Backend as Service)和LLMOps的理念,旨在简化和加速生成式AI应用的创建和部署。Dify提供了一个用户友好的界面和一系列强大的工具…...
SQL进阶技巧:巧用异或运算解决经典换座位问题
目录 0 问题描述 1 数据准备 2 问题分析 2.1 什么是异或 2.2异或有什么特性? 2.3 异或应用 2.4 本问题采用异或SQL解决方案 3 小结 0 问题描述 表 seat中有2个字段id和student id 是该表的主键(唯一值)列,student表示学生姓名。 该表的每一行都表示学生的姓名和 ID。…...
【MySQL】 运维篇—数据库监控:使用MySQL内置工具(如SHOW命令、INFORMATION_SCHEMA)进行监控
随着应用程序的增长,数据库的性能和稳定性变得至关重要。监控数据库的状态和性能可以帮助数据库管理员(DBA)及时发现问题,进行故障排查,并优化数据库的运行效率。通过监控工具,DBA可以获取实时的性能指标、…...
【温酒笔记】DMA
参考文档:野火STM32F103 1. Direct Memory Access-直接内存访问 DMA控制器独立于内核 是一个单独的外设 DMA1有7个通道DMA2有5个通道DMA有四个等级,非常高,高,中,低四个优先级如果优先等级相同,通道编号越…...
力扣判断字符是否唯一(位运算)
文章目录 给一个数n,判断它的二进制位中第x位是0还是1(从0开始计数)将一个数n的二进制位第X位修改为1(从0开始计数)将一个数n的二进制第x位修改为0(从0开始计数)提取一个数n二进制中最右侧的1去掉一个数n二进制表示中最右侧的1 今天我们通过判断字符是否唯一这个题来了解位运算…...
GPU和CPU区别?为什么挖矿、大模型都用GPU?
GPU(图形处理单元)和CPU(中央处理单元)是计算机中两种不同类型的处理器,它们在设计和功能上有很大的区别。 CPU是计算机的大脑,专门用于执行各种通用任务,如操作系统管理、数据处理、多任务处理等。它的架构设计旨在适应多种任务,…...
新兴斗篷cloak技术,你了解吗?
随着互联网技术的飞速发展,网络营销领域也经历了翻天覆地的变革。 从最早的网络横幅广告到如今主流的搜索引擎和社交媒体营销,广告形式变得越来越多样。 其中,搜索引擎广告一直以其精准投放而备受青睐,但近年来,一项名…...
【抽代复习笔记】34-群(二十八):不变子群的几道例题
例1:证明,交换群的任何子群都是不变子群。 证:设(G,o)是交换群,H≤G, 对任意的a∈G,显然都有aH {a o h|h∈H} {h o a|h∈H} Ha。 所以H⊿G。 【注:规范的不变子群符号是一个顶角指向左边…...
Chrome和Firefox如何保护用户的浏览数据
在当今数字化时代,保护用户的浏览数据变得尤为重要。浏览器作为我们日常上网的主要工具,其安全性直接关系到个人信息的保密性。本文将详细介绍Chrome和Firefox这两款主流浏览器如何通过一系列功能来保护用户的浏览数据。(本文由https://chrom…...
CentOS 7镜像下载
新版本系统镜像下载(当前最新是CentOS 7.4版本) CentOS官网 官网地址 http://isoredirect.centos.org/centos/7.4.1708/isos/x86_64/ http://mirror.centos.org/centos/7/isos/ 国内的华为云,超级快:https://mirrors.huaweiclou…...
opencv-windows-cmake-Mingw-w64,编译opencv源码
Windows_MinGW_64_OpenCV在线编译动态库,并使用在C项目: (mingw-w64 cmakegithub actions方案) 修改版opencv在线编译: 加入opencv-contrib库, 一起编译生成动态库,在线编译好的opencv动态库,可以下载使用.验证opencv动态库是否可用的模板项目,测试opencv动态库是否可用的模板…...
Puppeteer点击系统:解锁百度流量点击率提升的解决案例
在数字营销领域,流量和搜索引擎优化(SEO)是提升网站可见性的关键。我开发了一个基于Puppeteer的点击系统,旨在自动化地提升百度流量点击率。本文将介绍这个系统如何通过模拟真实用户行为,优化关键词排名,并…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...
【记录坑点问题】IDEA运行:maven-resources-production:XX: OOM: Java heap space
问题:IDEA出现maven-resources-production:operation-service: java.lang.OutOfMemoryError: Java heap space 解决方案:将编译的堆内存增加一点 位置:设置setting-》构建菜单build-》编译器Complier...

