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

群组功能实现指南:从数据库设计到前后端交互,上班第二周

目录

  • 用户管理系统的流程curd
    • 注册
      • 解决方案
      • 修改代码
        • 1. 在 `User` 实体类中添加 `@TableField` 注解
        • 2. 使用 `LambdaQueryWrapper` 进行查询
      • 问题小结
  • 添加群功能
    • ✅ 可行性结论
    • 🧩 补充
      • 1. 数据迁移脚本(如从 `public_chat_status` 迁移)
      • 2. 枚举字段定义建议(MySQL `ENUM` 或 Java 枚举类)
        • MySQL 层面定义:
        • Java 层面定义:
      • 3. 幂等性处理(防止重复加群)
      • 4. 统一返回结构(推荐)
      • 5. 日志与监控建议
    • 加群前端代码
    • 新增“全局群搜索”接口 /groups/list
    • 梳理如何将“加群/退群”功能完整落地,
  • 总结

学到这里,我需要梳理一下我目前学习登录注册的逻辑问题。

用户管理系统的流程curd

注册

前端提交的注册数据,给到后端服务,
用户名是否重复,可以在添加数据表单时id添加唯一索引,也可以在接口书写是否已注册,返回未注册,
密码进行加密,写入用户数据(加密密码,创建时间),返回写入成功,关于邮箱注册与手机号注册,我还没有尝试过。待定

关于如何登录
前端页面提交数据(密码,用户信息),到数据库中查看用户信息是否存在,密码进行加密,查看加密密码与数据库的密码是否一致。
推荐使用 BCrypt(自动生成盐值,无需单独存储)

// 注册时加密
String encryptedPwd = new BCryptPasswordEncoder().encode(rawPassword);// 登录时校验
boolean isMatch = new BCryptPasswordEncoder().matches(rawPassword, dbPassword);

练习可以使用 MD5(需自己处理盐值,已逐渐被淘汰)
因为数据不大
在这里插入图片描述

我需要将校验用户名是否存在数据库中,检测密码时需要加密,可以加盐,验证数据库的密码hash值,对比。
注册就是,用户名,密码,确认密码,

唯一标识校验
手机号 / 邮箱 / 用户名需全局唯一

-- 数据库唯一索引
ALTER TABLE `user` ADD UNIQUE `idx_user_phone` (`user_phone`);-- 后端查询校验
User existUser = userMapper.findByPhone(userPhone);
if (existUser != null) {throw new BizException("手机号已注册");
}

下面就是注意的一点,关于登录的需要注意的点,我可以允许他存在一台设备上,当进行二次登录,将第一次登录的设备会被弹出,这个时候的

  1. 登录态保持(Token 机制)
    方案:JWT(JSON Web Token)
    流程:
    登录成功时生成 JWT:
// JJWT 库示例
String token = Jwts.builder().setSubject(userId).setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)) // 1小时过期.signWith(SignatureAlgorithm.HS256, "your-secret-key").compact();

前端存储 Token(localStorage 或 Cookie)
后续请求携带 Token(Authorization 头)


com.bushuo.bushuopicturebackend.exception.BusinessException: 用户不存在或密码错误
在这里插入图片描述
数据库也是userAccount
在这里插入图片描述

在使用 QueryWrapper 进行查询时遇到了“用户不存在或密码错误”的异常。

现在的问题在于 userAccount 字段在数据库中的实际列名为 user_account,而代码中直接使用了 userAccount 导致字段映射不一致。

解决方案

  1. 字段映射不一致:在 MyBatis Plus 中,如果实体类字段名与数据库表的实际列名不完全一致(包括大小写),需要使用 @TableField 注解进行显式映射。
  2. 推荐使用 LambdaQueryWrapper:相比 QueryWrapperLambdaQueryWrapper 提供更高的类型安全性和可维护性,避免字段名拼写错误。

修改代码

1. 在 User 实体类中添加 @TableField 注解

首先需要在 User 实体类中对 userAccount 字段添加 @TableField 注解,明确指定其对应的数据库列名。

2. 使用 LambdaQueryWrapper 进行查询

UserServiceImpl 类的 userLogin 方法中,推荐使用 LambdaQueryWrapper 来提高查询的安全性和可维护性。

问题小结

  • 修改点 1:在 User 实体类中为 userAccount 字段添加 @TableField("user_account") 注解,确保字段映射正确。
  • 修改点 2:在 UserServiceImpluserLogin 方法中,使用 LambdaQueryWrapper 替代 QueryWrapper,提高查询的安全性和可维护性。

这样可以有效解决由于字段映射不一致导致的“用户不存在或密码错误”问题。

其实只需要将数据库名称的下划线由_变成-这个差不多就可以了
在这里插入图片描述

关于数据库,我安装了宝塔连接数据库后,不知道为什么数据库密码变了,我查了一轮,发现密码在宝塔里面,因为我不知道为什么重置mysql密码不行,我一看我的数据库密码。
在这里插入图片描述
密码也可以修改,关于数据库8.0版本,我看了需要修改地区时效

spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/bushuo-picture?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456

他还要我修改地址,将utc换成Asia/Shanghai,8.0以上需要。
在这里插入图片描述
还有数据库的驱动下载
在这里插入图片描述
在这里插入图片描述
寻找驱动下载,可以将5.0的删掉“-”;
解决了一点点问题。挺开心的。


添加群功能

✅ 可行性结论

模块可行性说明
数据库设计✅✅✅表结构合理、索引完备、支持软删除与角色扩展
后端实现(Spring Boot + MyBatis)✅✅✅分层清晰、事务控制、接口规范、易于维护
可扩展性✅✅✅支持角色、权限、昵称、禁言等后续功能扩展
安全性与健壮性✅✅提供了 JWT 校验、幂等性、状态管理等安全机制
团队协作与文档规范✅✅接口命名规范、代码结构清晰,适合多人协作

🧩 补充

1. 数据迁移脚本(如从 public_chat_status 迁移)

如果你之前使用了 public_chat_status 来记录用户群组关系,可以写一个迁移脚本将有效数据迁移到 group_members 中:

INSERT INTO group_members (group_id, user_id, role, status)
SELECT group_id, user_id, 'member', 1
FROM public_chat_status
WHERE status = 1;
  • 后续逐步弃用 public_chat_status 的相关逻辑。

2. 枚举字段定义建议(MySQL ENUM 或 Java 枚举类)

MySQL 层面定义:
role ENUM('member', 'admin', 'owner') DEFAULT 'member'
Java 层面定义:
public enum GroupRole {MEMBER("member"),ADMIN("admin"),OWNER("owner");private final String value;GroupRole(String value) {this.value = value;}public String getValue() {return value;}
}
  • 使用枚举能提升代码可读性和类型安全性。

3. 幂等性处理(防止重复加群)

在 Controller 层或 Service 层做幂等判断:

if (groupMembersMapper.findByGroupIdAndUserId(groupId, userId) != null) {return ResponseEntity.badRequest().body("您已在该群中");
}
  • 防止前端误操作或网络重发导致的重复请求。

4. 统一返回结构(推荐)

统一使用 Result<T> 返回结构,便于前端解析:

public class Result<T> {private int code;private String message;private T data;// success / error 方法
}

示例返回:

return Result.success("加入成功");

5. 日志与监控建议

  • 在关键操作(如加群、退群)中添加日志记录。
  • 可结合 AOP 实现接口调用日志追踪。
  • 若接入监控系统(如 Prometheus),可记录加群频率、失败次数等指标。

  1. 数据库建表
CREATE TABLE group_members (id INT PRIMARY KEY AUTO_INCREMENT,group_id INT NOT NULL,user_id INT NOT NULL,join_time DATETIME DEFAULT CURRENT_TIMESTAMP,role ENUM('member', 'admin', 'owner') DEFAULT 'member',status INT DEFAULT 1, -- 1=正常,0=退出UNIQUE KEY uq_group_user (group_id, user_id),KEY idx_group_id (group_id),KEY idx_user_id (user_id)
);
  1. Java 实体类
package com.life.lifechat.domain;import lombok.Data;import java.util.Date;@Data
public class GroupMembers {private Integer id;private Integer groupId;private Integer userId;private Date joinTime;private String role;private Integer status;// getters and setters
}
  1. Mapper 接口
package com.life.lifechat.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.life.lifechat.domain.GroupMembers;
import org.apache.ibatis.annotations.Param;
import java.util.List;public interface GroupMembersMapper extends BaseMapper<GroupMembers> {GroupMembers findByGroupIdAndUserId(@Param("groupId") Integer groupId, @Param("userId") Integer userId);List<GroupMembers> findByGroupId(@Param("groupId") Integer groupId);List<GroupMembers> findByUserId(@Param("userId") Integer userId);int updateStatusByGroupIdAndUserId(@Param("groupId") Integer groupId, @Param("userId") Integer userId, @Param("status") Integer status);
}

四、Mapper XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.life.lifechat.mapper.GroupMembersMapper"><select id="findByGroupIdAndUserId" resultType="com.life.lifechat.domain.GroupMembers">SELECT * FROM group_members WHERE group_id = #{groupId} AND user_id = #{userId} AND status = 1</select><select id="findByGroupId" resultType="com.life.lifechat.domain.GroupMembers">SELECT * FROM group_members WHERE group_id = #{groupId} AND status = 1</select><select id="findByUserId" resultType="com.life.lifechat.domain.GroupMembers">SELECT * FROM group_members WHERE user_id = #{userId} AND status = 1</select><update id="updateStatusByGroupIdAndUserId">UPDATE group_members SET status = #{status}WHERE group_id = #{groupId} AND user_id = #{userId}</update></mapper>

五、Service 层
新建 GroupMembersService.java

package com.life.lifechat.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.life.lifechat.domain.GroupMembers;/*** 群成员服务接口*/
public interface GroupMembersService extends IService<GroupMembers> {
}
  1. 新建实现类 GroupMembersServiceImpl.java
package com.life.lifechat.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.life.lifechat.domain.GroupMembers;
import com.life.lifechat.mapper.GroupMembersMapper;
import com.life.lifechat.service.GroupMembersService;
import org.springframework.stereotype.Service;@Service
public class GroupMembersServiceImpl extends ServiceImpl<GroupMembersMapper, GroupMembers> implements GroupMembersService {
}

✅ 在原有 GroupsService 中调用新服务
如果你在 GroupsService 中需要操作群成员数据,可以注入 GroupMembersService:

package com.life.lifechat.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.life.lifechat.domain.Groups;
import com.life.lifechat.domain.GroupMembers;
import com.life.lifechat.dto.GroupWithLastMessageDTO;import java.util.List;/*** 原有群组服务接口*/
public interface GroupsService extends IService<Groups> {List<GroupWithLastMessageDTO> getGroupsWithLastMessage(Integer userId);// 示例方法:获取用户加入的群成员列表List<GroupMembers> getUserGroups(Integer userId);
}

对应的实现类中注入并使用:

@Autowired
private GroupMembersService groupMembersService;@Override
public List<GroupMembers> getUserGroups(Integer userId) {return groupMembersService.listByUserId(userId); // 需要你在 GroupMembersService 中定义该方法
}

✅ 补充:在 GroupMembersService 中加常用方法
修改 GroupMembersService.java

public interface GroupMembersService extends IService<GroupMembers> {List<GroupMembers> listByGroupId(Integer groupId);List<GroupMembers> listByUserId(Integer userId);boolean joinGroup(Integer groupId, Integer userId);boolean quitGroup(Integer groupId, Integer userId);
}

修改 GroupMembersServiceImpl.java

@Override
public List<GroupMembers> listByGroupId(Integer groupId) {return lambdaQuery().eq(GroupMembers::getGroupId, groupId).eq(GroupMembers::getStatus, 1).list();
}@Override
public List<GroupMembers> listByUserId(Integer userId) {return lambdaQuery().eq(GroupMembers::getUserId, userId).eq(GroupMembers::getStatus, 1).list();
}@Override
@Transactional
public boolean joinGroup(Integer groupId, Integer userId) {GroupMembers existing = lambdaQuery().eq(GroupMembers::getGroupId, groupId).eq(GroupMembers::getUserId, userId).eq(GroupMembers::getStatus, 1).one();if (existing != null) return false;GroupMembers member = new GroupMembers();member.setGroupId(groupId);member.setUserId(userId);member.setRole("member");member.setStatus(1);return save(member);
}@Override
public boolean quitGroup(Integer groupId, Integer userId) {return lambdaUpdate().eq(GroupMembers::getGroupId, groupId).eq(GroupMembers::getUserId, userId).set(GroupMembers::getStatus, 0).update();
}

加群前端代码

前端目录
src/
├── api/ # 接口封装
│ ├── axios.js # Axios 实例配置
│ └── group.js # 群相关接口
├── views/ # 页面组件
│ ├── Groups.vue # 群列表页
│ └── GroupChat.vue # 群聊天页(可选)
├── store/ # 状态管理
│ └── userStore.js # 用户信息存储
├── utils/ # 工具函数
│ └── errorHandler.js # 统一错误处理
├── router/ # 路由配置
│ └── index.js
├── App.vue
└── main.js

  1. axios.js:统一请求配置
// src/api/axios.js
import axios from 'axios';const instance = axios.create({baseURL: import.meta.env.VITE_API_URL || '/api',timeout: 5000,
});instance.interceptors.request.use(config => {const token = localStorage.getItem('token');if (token) {config.headers.Authorization = `Bearer ${token}`;}return config;
});export default instance;
  1. group.js:群相关 API 封装
// src/api/group.js
import axios from '@/api/axios';export function joinGroup(groupId) {return axios.post(`/groups/${groupId}/join`);
}export function getGroupList() {return axios.get('/groups/list');
}
  1. errorHandler.js:统一错误提示工具
// src/utils/errorHandler.js
import { ElMessage } from 'element-plus';export function handleApiError(error, defaultMessage = '操作失败') {const message = error.response?.data?.message || defaultMessage;ElMessage.error(message);
}
  1. userStore.js:用户状态管理(Pinia)
// src/store/userStore.js
import { defineStore } from 'pinia';export const useUserStore = defineStore('user', {state: () => ({userId: null,nickname: '',avatar: '',}),actions: {setUser(userInfo) {Object.assign(this, userInfo);},clearUser() {this.userId = null;this.nickname = '';this.avatar = '';}}
});
  1. Groups.vue:群列表页面组件
<template><div class="groups-container"><h2>群聊列表</h2><el-table :data="groupList" border style="width: 100%"><el-table-column prop="groupName" label="群名称" /><el-table-column label="操作"><template #default="scope"><el-buttontype="primary"@click="handleJoin(scope.row.id)":loading="loading":disabled="scope.row.joined">{{ scope.row.joined ? '已加入' : '加入群聊' }}</el-button></template></el-table-column></el-table></div>
</template><script setup>
import { ref, onMounted } from 'vue';
import { useUserStore } from '@/store/userStore';
import { useRouter } from 'vue-router';
import { joinGroup, getGroupList } from '@/api/group';
import { handleApiError } from '@/utils/errorHandler';const userStore = useUserStore();
const router = useRouter();const groupList = ref([]);
const loading = ref(false);onMounted(() => {fetchGroupList();
});function fetchGroupList() {getGroupList().then(res => {groupList.value = res.data.data || [];}).catch(err => {handleApiError(err, '获取群列表失败');});
}function handleJoin(groupId) {loading.value = true;joinGroup(groupId).then(res => {ElMessage.success(res.data.message || '加入成功');const group = groupList.value.find(g => g.id === groupId);if (group) group.joined = true;// 跳转到群聊天页router.push({ name: 'GroupChat', params: { groupId } });}).catch(err => {handleApiError(err, '加入失败');}).finally(() => {loading.value = false;});
}
</script><style scoped>
.groups-container {padding: 20px;
}
</style>
  1. router/index.js:路由配置
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Groups from '../views/Groups.vue';
import GroupChat from '../views/GroupChat.vue';const routes = [{ path: '/groups', component: Groups, name: 'Groups' },{ path: '/group/:groupId/chat', component: GroupChat, name: 'GroupChat' },
];const router = createRouter({history: createWebHistory(),routes,
});export default router;
  1. main.js:主入口文件
// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { createPinia } from '@pinia/vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';const app = createApp(App);
const pinia = createPinia();app.use(router);
app.use(pinia);
app.use(ElementPlus);app.mount('#app');

在这里插入图片描述
1.运行
npm install pinia

2.修改 src/main.js:
import { createPinia } from ‘pinia’;
// …
app.use(createPinia());
在这里插入图片描述
解决方法

  1. 用的是 Vue CLI(不是 Vite)
  2. 如何修复?
import axios from 'axios';// Vue CLI 用 process.env.VUE_APP_API_URL
const baseURL = process.env.VUE_APP_API_URL || '/api';const instance = axios.create({baseURL,timeout: 5000,
});instance.interceptors.request.use(config => {const token = localStorage.getItem('token');if (token) {config.headers.Authorization = `Bearer ${token}`;}return config;
});export default instance;

后续在index修改添加加群按钮
你输入内容,点击“搜索”按钮,前端请求 /groups/list?keyword=xxx。
后端返回群聊列表,前端渲染出来。
点击“加入群聊”按钮,前端请求 /groups/{groupId}/join,成功后按钮变为“已加入”。
/groups/list?keyword=1

新增“全局群搜索”接口 /groups/list

✅ 一、新增接口:/groups/list 用于全局群搜索

  1. Controller 层
// GroupsController.java@GetMapping("/groups/list")
public Map<String, Object> searchGroups(@RequestParam String keyword) {Map<String, Object> result = new HashMap<>();try {List<GroupWithLastMessageDTO> groups = groupsService.searchGroups(keyword);result.put("code", 200);result.put("msg", "success");result.put("data", groups);} catch (Exception e) {result.put("code", 500);result.put("msg", "系统繁忙,请稍后重试");}return result;
}

✅ 二、Service 层

  1. GroupsService.java 接口定义
// GroupsService.java
List<GroupWithLastMessageDTO> searchGroups(String keyword);
  1. GroupsServiceImpl.java 实现类
// GroupsServiceImpl.java
@Autowired
private GroupsMapper groupsMapper;@Override
public List<GroupWithLastMessageDTO> searchGroups(String keyword) {return groupsMapper.searchGroups(keyword);
}

✅ 三、Mapper 层

  1. GroupsMapper.java
// GroupsMapper.java
List<GroupWithLastMessageDTO> searchGroups(String keyword);
  1. GroupsMapper.xml
<!-- GroupsMapper.xml -->
<select id="searchGroups" parameterType="string" resultType="com.life.lifechat.dto.GroupWithLastMessageDTO">SELECT g.id,g.group_name AS groupName,g.group_img AS groupImg,NULL AS lastMessage, -- 可选字段,加群页可能不需要显示最后一条消息NULL AS timeFROM groups gWHERE g.group_name LIKE CONCAT('%', #{keyword}, '%')
</select>

✅ 四、DTO 类定义

// GroupWithLastMessageDTO.java
package com.life.lifechat.dto;import java.util.Date;public class GroupWithLastMessageDTO {private Integer id;private String groupName;private String groupImg;private String lastMessage;private Date time;// Getters and Setters
}

SELECT
g.id AS id,
g.group_name AS groupName,
g.group_img AS groupImg FROM groups g WHERE g.group_name LIKE ‘%技术%’

梳理如何将“加群/退群”功能完整落地,

直接集成和运行。

  1. 后端实现(Spring Boot + MyBatis Plus)
    1.1 DTO
    在 lifeChat/src/main/java/com/life/lifechat/dto/ 下新建 GroupJoinDTO.java
package com.life.lifechat.dto;public class GroupJoinDTO {private Integer groupId;private Integer userId;// getters and setters
}

1.2 Mapper
在 GroupMembersMapper.java 中补充:

  @Select("SELECT * FROM group_members WHERE group_id = #{groupId} AND user_id = #{userId} AND status = 1")GroupMembers selectByGroupIdAndUserId(@Param("groupId") Integer groupId, @Param("userId") Integer userId);@Update("UPDATE group_members SET status = 0 WHERE group_id = #{groupId} AND user_id = #{userId}")int quitGroup(@Param("groupId") Integer groupId, @Param("userId") Integer userId);
  1. Service 层
    GroupMembersService.java
public interface GroupMembersService {boolean joinGroup(Integer groupId, Integer userId);boolean quitGroup(Integer groupId, Integer userId);
}

实现 GroupMembersServiceImpl.java:

@Service
public class GroupMembersServiceImpl implements GroupMembersService {@Overridepublic boolean joinGroup(Integer groupId, Integer userId) {GroupMembers existing = baseMapper.selectByGroupIdAndUserId(groupId, userId);if (existing != null && existing.getStatus() == 1) {return false;}GroupMembers member = new GroupMembers();member.setGroupId(groupId);member.setUserId(userId);member.setRole("member");member.setStatus(1);return baseMapper.insert(member) > 0; }@Overridepublic boolean quitGroup(Integer groupId, Integer userId) {return baseMapper.quitGroup(groupId, userId) > 0;}
}

1.4 Controller
在 GroupsController.java 中添加:

 @Autowiredprivate GroupMembersService groupMembersService; // 👈 注意变量名和类型@PostMapping("/groups/join")public Map<String, Object> joinGroup(@RequestBody GroupJoinDTO dto) {Map<String, Object> result = new HashMap<>();try {boolean success = groupMembersService.joinGroup(dto.getGroupId(), dto.getUserId());if (success) {result.put("code", 200);result.put("msg", "加入成功");} else {result.put("code", 400);result.put("msg", "您已在该群中或群不存在");}} catch (Exception e) {e.printStackTrace();result.put("code", 500);result.put("msg", "系统繁忙,请稍后再试");}return result;}@PostMapping("/groups/quit")public Map<String, Object> quitGroup(@RequestBody GroupJoinDTO dto) {Map<String, Object> result = new HashMap<>();try {boolean success = groupMembersService.quitGroup(dto.getGroupId(), dto.getUserId());if (success) {result.put("code", 200);result.put("msg", "退出成功");} else {result.put("code", 400);result.put("msg", "退出失败,请确认是否在群中");}} catch (Exception e) {e.printStackTrace();result.put("code", 500);result.put("msg", "系统繁忙,请稍后再试");}return result;}

总结

完成登录注册基本流程,包括 BCrypt 密码加密、JWT Token 生成与验证、数据库交互,并解决了字段映射不一致(@TableField注解)和 MySQL 8.0 连接(时区 + 驱动)问题。
明确了数据库设计(group_members表)、前后端实现步骤(实体类 / 接口 / 页面 / 状态管理),定义了核心逻辑(加群查重、退群软删除)和调试方法。
待弥补 ,优化。Token 管理优化与 加密与安全增强、 前后端交互与用户体验

http://www.dtcms.com/a/288620.html

相关文章:

  • 【数据结构】揭秘二叉树与堆--用C语言实现堆
  • 人工智能之数学基础:随机实验、样本空间、随机事件
  • Docker Desktop 入门教程(Windows macOS)
  • 深度学习图像分类数据集—百种病虫害分类
  • Python绘图小工具开发:从零构建数据可视化利器
  • 股票及金融笔记
  • 如何升级Docker部署的Dify
  • Materials Studio学习笔记(二十九)——尿素的几何优化
  • 私有云新势力:Puter+CPolar如何低成本替代商业网盘?
  • 【Linux性能优化】常用工具和实战指令
  • 小架构step系列20:请求和响应的扩展点
  • 制作mac 系统U盘
  • macOs上交叉编译ffmpeg及安装ffmpeg工具
  • pages.json页面路由中,globalStyle的各个属性
  • RPG62.制作敌人攻击波数二:攻击ui
  • 分布式文件系统04-DataNode海量数据分布式高可靠存储
  • 【LeetCode数据结构】单链表的应用——环形链表问题详解
  • 【PTA数据结构 | C语言版】哈夫曼树的实现
  • UDP中的单播,多播,广播
  • 【RAG Agent】Deep Searcher实现逻辑解析
  • 【Unity3D实例-功能-移动】角色移动-通过WSAD(CharacterController方式)
  • 【STM32实践篇】:串口通信
  • Qwen3-8B 的 TTFT 性能分析:16K 与 32K 输入 Prompt 的推算公式与底层原理详解
  • 吴恩达机器学习笔记(3)—线性代数回顾(可选)
  • 【Django】DRF API版本和解析器
  • HTML Style 对象深度解析:从基础到高级应用
  • 上电复位断言的自动化
  • 【数据结构】双向循环链表的实现
  • 18.TaskExecutor获取ResourceManagerGateway
  • 【MySQL】索引中的页以及索引的分类