springboot集成websokcet+uniapp开发聊天原型验证(一)

1. 整体思路

群组聊天功能实现思路
  • 需要为每个群组维护一个对应的集合(可以是 Set 等数据结构),用来存放该群组内所有在线用户的 WebSocketSession。当有消息发送到群组时,遍历该群组对应的集合,向其中的每个在线用户发送消息。
  • 在消息结构体中新增一个字段用于标识所属群组,以便后端根据这个字段来进行消息的广播分发。
离线用户处理及历史消息推送思路
  • 对于离线用户,当他们重新上线时,需要能够识别出他们之前所在的群组(可以通过用户登录等操作记录其关联群组信息)。
  • 后端要将该群组在其离线期间产生的历史消息查询出来(这可能涉及到数据库操作,将群组聊天消息存储到数据库中以便查询历史记录),然后通过 WebSocket 连接将这些历史消息逐一发送给重新上线的用户。

后端代码修改思路

1. 群组管理与消息处理

  • 群组数据结构:使用合适的数据结构(如 Map)来存储群组相关信息,以群组 ID 作为键,对应的值可以是包含该群组内在线用户 WebSocketSession 列表以及群组历史消息列表等信息的对象。
  • 消息格式定义:明确消息的格式,使其能区分是文字消息还是图片消息,并且包含必要的元数据,比如发送者、群组 ID、消息内容(文字内容或图片链接等)、时间戳等。
  • 消息分发逻辑:当接收到消息时,根据消息中的群组 ID,找到对应的群组在线用户列表,然后将消息发送给这些用户。
2. 离线用户历史消息处理

  • 用户与群组关联记录:维护用户与所属群组的关联关系,比如使用 Map 存储用户 ID 和其所属群组 ID 列表的对应关系,以便在用户重新上线时确定需要推送哪些群组的历史消息。
  • 历史消息存储与查询:将群组内的聊天消息持久化存储(实际应用中通常是存入数据库,这里可简单模拟存储结构),当离线用户重新上线时,从存储结构中查询出其所属群组的历史消息并推送给他。

直接 springboot+websokcet,感觉比原生的websocket简单一点。

  1.  集成websokcet
  2.  配置文件
  3.  handler
  4. postman测试一下
  5.  uniapp

pom添加依赖:

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>

application.yml中 端口配置(先复用应用的端口吧)

server:port: 7877tomcat:accept-count: 10000threads:max: 800min-spare: 200compression:enabled: trueservlet:context-path: /chat

配置文件(application.yml)

  • 务器端口及相关配置
    • server.port 配置为 7877,指定了 Spring Boot 应用启动后监听的端口号。
    • server.tomcat.accept-count 设置为 10000,它表示当所有的处理线程都在使用时,能够放到处理队列中的连接请求数量。server.tomcat.threads.max 设为 800 定义了最大线程数,min-spare 设为 200 则是最小备用线程数,这些配置用于优化 Tomcat 处理请求的线程资源分配。
    • server.compression.enabled 设为 true,开启了服务器响应内容的压缩功能,有助于减少网络传输的数据量,提高性能。
    • server.servlet.context-path 配置为 /chat,意味着应用的上下文路径是 /chat,后续访问应用中的资源路径都是基于这个上下文路径来构建的。

WebSocket 配置类(WebSocketConfig)

  • 这个类实现了 WebSocketConfigurer 接口,用于配置 WebSocket 相关的处理。
  • 在 registerWebSocketHandlers 方法中,将自定义的 MyWebSocketHandler 注册到了 WebSocket 处理器注册表 WebSocketHandlerRegistry 中,并且将 WebSocket 的端点路径设置为 /websocket,同时允许来自任意源(setAllowedOrigins("*"))的连接访问该 WebSocket 端点。
package com.edwin.java.config;import com.edwin.java.config.interceptor.GroupChatInterceptor;
import com.edwin.java.util.MyWebSocketHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {/*** registerWebSocketHandlers 是一个函数或方法,通常用于在 Web 应用程序中注册 WebSocket 处理程序。* WebSocket 是一种基于 TCP 的协议,可以实现客户端和服务器之间的双向通信,可以用于实时应用程序,如聊天应用、游戏、实时更新等。在 Java Web 应用程序中,可以使用 Spring 框架提供的 WebSocket 支持来处理 WebSocket 连接。* registerWebSocketHandlers 方法是 Spring WebSocket 的一个 API,它允许开发人员在应用程序中注册 WebSocket 处理程序,并将其映射到特定的 URI。在调用 registerWebSocketHandlers 方法时,需要传递一个 WebSocketHandler 实例和一个 URI 路径作为参数。当客户端请求与该 URI 路径对应的 WebSocket 连接时,Spring 将调用相应的 WebSocket 处理程序来处理连接。*/@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {//参数1:注册我们自定义的MyWebSocketHandler类//参数2:路径【UniApp中建立连接的路径】如:我的ip是192.168.1.8:8099则UniApp需要输入的url是ws://192.168.1.8:8099/websocket//参数3:setAllowedOrigins("*")设置允许全部来源【在WebSocket中,浏览器会发送一个带有Origin头部的HTTP请求来请求建立WebSocket连接。服务器可以使用setAllowedOrigins方法来设置允许的来源,即允许建立WebSocket连接的域名或IP地址。这样,服务器就可以限制建立WebSocket连接的客户端,避免来自不信任的域名或IP地址的WebSocket连接。】registry.addHandler(new MyWebSocketHandler(), "/websocket").setAllowedOrigins("*").addInterceptors(new GroupChatInterceptor());;}}

WebSocket 处理器类(MyWebSocketHandler)

  • 连接建立
    • afterConnectionEstablished 方法在 WebSocket 连接建立后被调用,会记录连接成功的日志信息,并将对应的 WebSocketSession 添加到 sessions 列表中,用于后续管理连接会话。
  • 消息处理
    • handleMessage 方法接收到消息时,会记录消息内容日志,然后遍历所有已连接的会话,尝试向每个客户端发送一条固定格式的消息(这里只是简单示例性质的消息)。
    • handleTextMessage 方法针对文本消息做更具体的处理,会对收到的请求消息进行转义和记录日志,然后构造响应消息并发送回对应的客户端会话。
  • 定时消息发送
    • 通过 @Scheduled(fixedRate = 10000) 注解定义了一个定时任务,每隔 10000 毫秒(即 10 秒)会遍历所有连接会话,如果会话处于打开状态,就向其发送一条包含当前时间的广播消息。
  • 连接关闭及其他
    • afterConnectionClosed 方法在 WebSocket 连接关闭时被调用,负责从 sessions 列表中移除对应的会话,并记录连接关闭的日志。
    • supportsPartialMessages 方法返回 false,表示不支持部分消息处理。
    • handleTransportError 方法用于处理 WebSocket 传输过程中的错误,会记录相应的错误日志。
package com.edwin.java.util;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.web.util.HtmlUtils;import java.io.IOException;
import java.time.LocalTime;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import com.fasterxml.jackson.databind.ObjectMapper;@Component
public class MyWebSocketHandler extends TextWebSocketHandler {private static final Logger LOGGER = LoggerFactory.getLogger(MyWebSocketHandler.class);// 用于存储群组信息,键为群组ID,值包含在线用户会话列表和历史消息列表private Map<String, GroupInfo> groupInfos = new HashMap<>();// 用于存储用户与群组的关联关系,键为用户ID,值为群组ID列表private Map<String, List<String>> userGroups = new HashMap<>();private ObjectMapper objectMapper = new ObjectMapper();private final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();/*** afterConnectionEstablished 是一个 WebSocket API 中的回调函数,它是在建立 WebSocket 连接之后被调用的。* 当 WebSocket 连接建立成功后,浏览器会发送一个握手请求给服务器端,如果服务器成功地接受了该请求,那么连接就会被建立起来*/@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {LOGGER.info("WebSocket已连接: {}", session.getId());// 假设从WebSocket连接的属性或者请求参数中获取用户ID和群组ID列表(实际需按业务逻辑调整获取方式)String userId = (String) session.getAttributes().get("userId");List<String> groupIds = (List<String>) session.getAttributes().get("groupIds");if (userId!= null && groupIds!= null) {userGroups.put(userId, groupIds);for (String groupId : groupIds) {groupInfos.computeIfAbsent(groupId, k -> new GroupInfo()).addSession(session);}}}/*** handleMessage 是 WebSocket API 中的回调函数,它是用来处理从客户端接收到的 WebSocket 消息的。* 当客户端通过 WebSocket 连接发送消息到服务器端时,服务器端会自动调用 handleMessage 函数并传递收到的消息作为参数,你可以在该函数中处理这个消息,并根据需要向客户端发送一些响应消息。*/@Overridepublic void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {LOGGER.info("WebSocket收到的消息: {}", message.getPayload());// 将消息反序列化,假设消息是JSON格式,这里解析为Message对象(下面定义)Message msg = objectMapper.readValue(message.getPayload().toString(), Message.class);String groupId = msg.getGroupId();GroupInfo groupInfo = groupInfos.get(groupId);if (groupInfo == null) {// 如果群组信息不存在,则创建新的群组信息,并添加当前用户的WebSocketSessiongroupInfo = new GroupInfo();groupInfo.addSession(session);groupInfos.put(groupId, groupInfo);// 同时,假设这里从WebSocket连接的属性或者请求参数中获取用户ID(实际需按业务逻辑调整获取方式)String userId = (String) session.getAttributes().get("userId");List<String> groupIds = userGroups.computeIfAbsent(userId, k -> new ArrayList<>());groupIds.add(groupId);}// 将消息添加到群组历史消息列表groupInfo.addHistoryMessage(msg);// 向群组内所有在线用户发送消息List<WebSocketSession> sessions = groupInfo.getSessions();for (WebSocketSession s : sessions) {try {s.sendMessage(new TextMessage(objectMapper.writeValueAsString(msg)));} catch (IOException e) {LOGGER.error("无法发送WebSocket消息", e);}}}@Overridepublic void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {handleMessage(session, message);}@Scheduled(fixedRate = 10000)void sendPeriodicMessages() throws IOException {// 这里可扩展定时向群组推送系统消息等功能,暂不做详细修改for (GroupInfo groupInfo : groupInfos.values()) {List<WebSocketSession> sessions = groupInfo.getSessions();for (WebSocketSession s : sessions) {if (s.isOpen()) {String broadcast = "server periodic message " + LocalDateTime.now();LOGGER.info("Server sends: {}", broadcast);s.sendMessage(new TextMessage(broadcast));}}}}// 处理用户重新上线,推送历史消息的方法public void handleUserReconnect(String userId, WebSocketSession session) {List<String> groupIds = userGroups.get(userId);if (groupIds!= null) {for (String groupId : groupIds) {GroupInfo groupInfo = groupInfos.get(groupId);if (groupInfo!= null) {List<Message> historyMessages = groupInfo.getHistoryMessages();for (Message historyMessage : historyMessages) {try {session.sendMessage(new TextMessage(objectMapper.writeValueAsString(historyMessage)));} catch (IOException e) {LOGGER.error("无法发送历史消息给重新上线用户", e);}}}}}}/*** afterConnectionClosed 是 WebSocket API 中的回调函数,它是在 WebSocket 连接关闭后被调用的。* 当客户端或服务器端主动关闭 WebSocket 连接时,afterConnectionClosed 回调函数会被调用,你可以在该函数中执行一些资源释放、清理工作等操作,比如关闭数据库连接、清理缓存等。*/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {LOGGER.info("WebSocket已断开连接: {}", session.getId());// 移除用户会话在各个群组中的关联String userId = (String) session.getAttributes().get("userId");List<String> groupIds = userGroups.get(userId);if (groupIds!= null) {for (String groupId : groupIds) {GroupInfo groupInfo = groupInfos.get(groupId);if (groupInfo!= null) {groupInfo.removeSession(session);}}userGroups.remove(userId);}}/*** supportsPartialMessages 是 WebSocket API 中的方法,它用来指示 WebSocket 消息是否支持分段传输。* WebSocket 消息可以分段传输,也就是说一个消息可以被分成多个部分依次传输,这对于大型数据传输和流媒体传输非常有用。当消息被分成多个部分传输时,WebSocket 会自动将这些部分合并成完整的消息。* supportsPartialMessages 方法用来指示服务器是否支持分段消息传输,如果支持,则可以在接收到部分消息时开始处理消息,否则需要等待接收到完整消息后才能开始处理。*/@Overridepublic boolean supportsPartialMessages() {return false;}/*** handleTransportError 是 WebSocket API 中的回调函数,它用来处理 WebSocket 传输层出现错误的情况。*当 WebSocket 传输层出现错误,比如网络中断、协议错误等,WebSocket 会自动调用 handleTransportError 函数,并传递相应的错误信息。在该函数中,我们可以处理这些错误,比如关闭 WebSocket 连接、记录错误日志等。*/@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {LOGGER.error("WebSocket错误", exception);}// 在MyWebSocketHandler类内部定义private class GroupInfo {// 存储群组内所有在线用户的WebSocketSession列表private List<WebSocketSession> sessions = new ArrayList<>();// 存储群组的历史消息列表,消息以自定义的Message对象形式存储(前面代码中已定义Message类)private List<Message> historyMessages = new ArrayList<>();// 添加一个用户的WebSocketSession到群组的在线用户列表中public void addSession(WebSocketSession session) {sessions.add(session);}// 从群组的在线用户列表中移除指定用户的WebSocketSessionpublic void removeSession(WebSocketSession session) {sessions.remove(session);}// 向群组的历史消息列表中添加一条消息public void addHistoryMessage(Message message) {historyMessages.add(message);}// 获取群组内所有在线用户的WebSocketSession列表public List<WebSocketSession> getSessions() {return sessions;}// 获取群组的历史消息列表public List<Message> getHistoryMessages() {return historyMessages;}}// 定义消息类,包含必要的消息属性,可根据实际需求扩展private static class Message {private String type; // 消息类型,如 "text" 或 "image"private String groupId; // 群组IDprivate String sender; // 发送者(可根据实际情况完善,比如用户ID等)private String content; // 消息内容,文字或图片链接等// private LocalDateTime timestamp = LocalDateTime.now(); // 时间戳// 生成必要的Getter和Setter方法(可使用Lombok简化代码,此处为清晰展示手动编写)public String getType() {return type;}public void setType(String type) {this.type = type;}public String getGroupId() {return groupId;}public void setGroupId(String groupId) {this.groupId = groupId;}public String getSender() {return sender;}public void setSender(String sender) {this.sender = sender;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}//        public LocalDateTime getTimestamp() {
//            return timestamp;
//        }
//
//        public void setTimestamp(LocalDateTime timestamp) {
//            this.timestamp = timestamp;
//        }}}

 postman验证:

注意,路径中要加chat ,因为application.yml中配置了

uniapp端页面:

<template><view class="chat-room"><!-- 聊天记录 --><scroll-view scroll-y="true" class="message-list"><view v-for="(message, index) in messages" :key="index" class="message-item"><view v-if="message.type === 'text'" class="text-message"><!--    <view class="avatar">{{ message.senderAvatar }}</view> --><view class="text">{{ message.content }}</view></view><view v-else-if="message.type === 'image'" class="image-message"><image :src="message.content" class="message-image" @click="previewImage(message.content)"></image></view></view></scroll-view><!-- 输入框 --><view class="input-area"><input v-model="inputContent" placeholder="输入内容" class="input" /><button @click="sendMessage">发送</button></view></view>
</template><script>
export default {data() {return {messages: [], // 聊天记录inputContent: '', // 输入框内容socketOpen: false, // WebSocket连接状态socket: null, // WebSocket对象};},methods: {// 初始化WebSocket连接initWebSocket() {uni.setStorageSync('userId',1);  // only testuni.setStorageSync('groupIds',2);  // only testconst userId = uni.getStorageSync('userId');const groupIds = uni.getStorageSync('groupIds');console.log(groupIds);// /wechat/client/chatthis.socket = uni.connectSocket({url: 'ws://127.0.0.1:7877/chat/websocket',data: {userId: userId,groupIds: groupIds},success: () => {console.log('WebSocket连接成功');this.socketOpen = true;},fail: () => {console.error('WebSocket连接失败');},});// 监听WebSocket消息this.socket.onMessage((res) => {const message = JSON.parse(res.data);this.messages.push(message); // 将新消息添加到聊天记录中this.$forceUpdate(); // 强制更新视图,确保新消息显示});// 监听WebSocket连接关闭this.socket.onClose(() => {console.log('WebSocket连接关闭');this.socketOpen = false;});},// 发送消息
sendMessage() {if (this.inputContent.trim() === '') {uni.showToast({title: '请输入内容',icon: 'none',});return;}const message = {type: 'text', // 消息类型,可以是text或image,这里发送文字消息示例,发送图片时修改相应字段groupId: uni.getStorageSync('groupIds'), // 从本地存储获取当前所在群组ID(需按实际情况调整获取方式)sender: uni.getStorageSync('userId'), // 从本地存储获取用户ID(需按实际情况调整获取方式)content: this.inputContent};// 通过WebSocket发送消息(或HTTP请求,根据后端接口决定)if (this.socketOpen) {this.socket.send({data: JSON.stringify(message)});} else {//todo}
},// 预览图片previewImage(url) {uni.previewImage({current: url, // 当前显示图片的http链接urls: [url], // 需要预览的图片http链接列表});},},mounted() {// 页面加载时初始化WebSocket连接this.initWebSocket();// 可以从服务器获取历史聊天记录并初始化messages数组(根据需求实现)},
};
</script><style>
.chat-room {padding: 10px;
}.message-list {height: 500px; /* 根据需要调整高度 */border-bottom: 1px solid #ccc;padding-right: 10px; /* 留出空间给滚动条 */overflow-y: auto;
}.message-item {margin-bottom: 10px;display: flex;align-items: center;
}.avatar {width: 40px;height: 40px;border-radius: 50%;margin-right: 10px;
}.text-message .text {background-color: #fff;padding: 5px 10px;border-radius: 5px;max-width: 60%; /* 根据需要调整宽度 */word-wrap: break-word; /* 防止长文本溢出 */
}.image-message .message-image {width: 100px;height: 100px;object-fit: cover;border-radius: 5px;
}.input-area {display: flex;margin-top: 10px;
}.input {flex: 1;padding: 5px;border: 1px solid #ccc;border-radius: 5px;
}button {padding: 5px 10px;margin-left: 10px;border: none;background-color: #1aad19;color: #fff;border-radius: 5px;
}
</style>

原型效果:

 

后期再补充~~~~~

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

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

相关文章

Reed-Muller(RM)码之编码

点个关注吧! 看了一些中文的博客,RM码没有很详细的资料,所以本文尝试给出推导原理。 推导 RM码由 ( r , m ) ( r , m ) (r,m

List直接使用removeAll报错

List直接使用removeAll报错 需要先将list转换才能使用 原因是&#xff1a; removeAll 方法在 Java 中用于从当前列表中删除另一个列表中存在的所有元素。如果直接对 List 接口的一个实现使用 removeAll 方法抛出异常&#xff0c;可能的原因有&#xff1a; 不同的List实现&am…

Linux -- 线程的优点、pthread 线程库

目录 线程的优点 pthread 线程库 前言 认识线程库 简单验证线程的独立栈空间 线程的优点 与进程之间的切换相比&#xff0c;线程之间的切换需要操作系统做的工作要少得多。 调度进程时&#xff0c;CPU 中有一个 cache&#xff08;缓存&#xff0c;提高运行效率&#xff0…

【magic-dash】01:magic-dash创建单页面应用及二次开发

文章目录 一、magic-dash是什么1.1 安装1.2 使用1.2.1 查看内置项目模板1.2.2 生成指定项目模板1.2.3 查看当前magic-dash版本1.2.4 查看命令说明1.2.5 内置模板列表二、创建虚拟环境并安装magic-dash三、magic-dash单页工具应用开发3.1 创建单页面项目3.1.1 使用命令行创建单页…

从零开始使用MaxKB打造本地大语言模型智能问答系统与远程交互

文章目录 前言1. 下载运行Ollama2. 安装大语言模型3. 安装Cpolar工具4. 配置公网地址5. 固定公网地址6. MaxKB 添加Olama7.创建问答应用 前言 目前大语言模型&#xff08;LLM&#xff09;已经成为了人工智能领域的一颗璀璨明星&#xff0c;从自然语言处理到智能问答系统&#…

深度解析 Pytest 中的 conftest.py

关注开源优测不迷路 大数据测试过程、策略及挑战 测试框架原理&#xff0c;构建成功的基石 在自动化测试工作之前&#xff0c;你应该知道的10条建议 在自动化测试中&#xff0c;重要的不是工具 在使用 Pytest 进行测试的过程中&#xff0c;conftest.py 文件扮演着极为重要的角色…

【python】银行客户流失预测预处理部分,独热编码·标签编码·数据离散化处理·数据筛选·数据分割

数据预处理 通过网盘分享的文件&#xff1a;银行流失预测数据和代码 链接: https://pan.baidu.com/s/1loiB8rMvZArfjJccu4KW6w?pwdpfcs 提取码: pfcs 非数值特征处理 目的&#xff1a;将非数值特征转换为数值型&#xff0c;以便模型能够处理。方法&#xff1a; 地理位置&am…

回归预测 | MATLAB实现CNN-LSSVM卷积神经网络结合最小二乘支持向量机多输入单输出回归预测

回归预测 | MATLAB实现CNN-LSSVM卷积神经网络结合最小二乘支持向量机多输入单输出回归预测 目录 回归预测 | MATLAB实现CNN-LSSVM卷积神经网络结合最小二乘支持向量机多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 回归预测 | MATLAB实现CNN-LSSVM…

slam学习笔记7---状态量求导相关数学公式

前言&#xff1a;本来打算只是归纳一下数学求导相关公式&#xff0c;后面也写了旋转求导相关内容&#xff0c;哈哈。感觉有点发散把握不住呀。水平有限&#xff0c;欢迎评论区点出。 一、基本初等函数求导公式 ( C ) ′ 0 , C (C)0,C (C)′0,C为常数 ( x μ ) ′ μ x μ −…

32单片机串口数据接收、空闲IDLE中断详解

一、前提说明 一开始写单片机程序的时候不太清楚空闲中断这个东西&#xff0c;每次用串口接收数据&#xff0c;都要再开一个定时器&#xff0c;在定时器内进行倒计时&#xff0c;每次接收数据就重置计时时间&#xff0c;计时结束就触发中断&#xff0c;再判断所有接收的数据&am…

深入探讨 Go 中的高级表单验证与翻译:Gin 与 Validator 的实践之道20241223

深入探讨 Go 中的高级表单验证与翻译&#xff1a;Gin 与 Validator 的实践之道 在现代后端开发中&#xff0c;表单验证是保证数据完整性和服务稳定性的核心环节。如何优雅、高效地实现表单验证&#xff0c;同时提供人性化的错误提示&#xff0c;是每位开发者的必修课。在本文中…

掌握 Ansys ACP 中的参考方向:简化复杂的复合材料设计

概括 在复合材料分析领域&#xff0c;精度至关重要&#xff0c;尤其是在定义纤维方向和铺层时。Ansys ACP&#xff08;Ansys Composite PrepPost&#xff09;提供了强大的工具来建立参考方向&#xff0c;这是实现精确结构模拟的关键步骤。在本博客中&#xff0c;我们将揭开在 …

Vue2学习(一)——Vue简介、Vue指令与指令修饰符

一、Vue简介 Vue是一套用于构建用户界面的渐进式框架。 所谓渐进式就是循序渐进&#xff0c;不一定非得把Vue中的所有API都学完才能开发Vue&#xff0c;可以学一点开发一点。 Vue2官网地址&#xff1a;https://v2.cn.vuejs.org/ Vue3官网地址&#xff1a;https://cn.vuejs…

Redis--通用命令学习

目录 一、引言 二、基础命令 1.set 2.get 3.keys 3.1 keys &#xff1f; 3.2 keys * 3.3 keys [abe] 3.4 keys [^] 3.5 keys [a-b] 4.exists 5.delete 6.expire 7.ttl 8.type 三、Redis中的过期策略&#xff08;面试题&#xff09; 1.惰性删除 2.定期删除 …

Linux程序设计(第四版)| 学习笔记

上次学习Linux相关内容还是上学的时候为了应付考试&#xff0c;最近有项目涉及Linux&#xff0c;重新学习以下。 很多年前关于Linux的总结 一、入门 1.概念 (1) UNIX 1)定义&#xff1a;指的是一种遵循特定规范的计算机操作系统。 2)特点&#xff1a;简单性、集中性、可重用…

PostgreSQL 的历史

title: PostgreSQL 的历史 date: 2024/12/23 updated: 2024/12/23 author: cmdragon excerpt: PostgreSQL 是一款功能强大且广泛使用的开源关系型数据库管理系统。其历史可以追溯到1986年,当时由加州大学伯克利分校的一个研究团队开发。文章将深入探讨 PostgreSQL 的起源、…

Ubuntu22.04 LTS 安装nvidia显卡驱动

准备跑老师给定的Github上的多模态源码,但是用了这么久ubuntu还没有尝试过安装nvidia驱动,好在也是一次成功,于是记录下来。 借鉴的是Ubuntu22.04安装显卡驱动(高速、避错版)-CSDN博客这篇文章,按照流程来基本没有问题,不过个人觉得有些步骤比较冗余,所以记录下来 主要…

WPS工具栏灰色怎么办

WPS离线不登录&#xff0c;开启工具栏等相关功能 当你在使用WPS的过程中&#xff0c;若因网络问题或其他特殊原因&#xff0c;导致无法登录使用WPS时&#xff0c;可根据以下步骤开启离线兼容模式&#xff0c;开启此模式后&#xff0c;可在未登录的状态下&#xff0c;激活并使用…

国标GB28181-2022平台EasyGBS:安防监控中P2P的穿透方法

在安防监控领域&#xff0c;P2P技术因其去中心化的特性而受到关注&#xff0c;尤其是在远程视频监控和数据传输方面。P2P技术允许设备之间直接通信&#xff0c;无需通过中央服务器&#xff0c;这在提高效率和降低成本方面具有明显优势。然而&#xff0c;P2P技术在实际应用中也面…

Mac Android studio 升级LadyBug 版本,所产生的bug

当Build 出现&#xff0c;这样的文字以后&#xff1a; Your build is currently configured to use incompatible Java 21.0.3 and Gradle 7.3.3. Cannot sync the project. We recommend upgrading to Gradle version 8.9. The minimum compatible Gradle version is 8.5. …