首页 文章 精选 留言 我的

精选列表

搜索[uniapp],共459篇文章
优秀的个人博客,低调大师

从零用 Java 实现小红书 SpringBoot Vue UniApp(18)音视频聊天室完整实现

>## [**移动端演示 http://8.146.211.120:8081/#/**](https://gitee.com/link?target=http%3A%2F%2F8.146.211.120%3A8081%2F%23%2F) ## [**管理端演示 http://8.146.211.120:8088/#/**](https://gitee.com/link?target=http%3A%2F%2F8.146.211.120%3A8088%2F%23%2F) ## [**项目整体介绍及演示**](https://gitee.com/link?target=https%3A%2F%2Fblog.csdn.net%2Fqq_35238367%2Farticle%2Fdetails%2F145126222) **前言** 直播是「主播中心」:一个人讲,很多人听。聊天室则是「麦位中心」:多人轮流上麦,更接近 Clubhouse / 语聊房形态。 本章与(17)共用腾讯云 TRTC 与 WebSocket 基础设施,但业务模型完全不同: - 房间号进出 - 1 个房主位 + 8 个麦位 - 语音房 / 视频房双模式 - 锁麦、抱下、强制闭麦、踢人、公屏禁言 - 体验时长倒计时 ---- ![聊天室入口:创建 / 加入](https://developer.qcloudimg.com/http-save/yehe-12101878/4ad9383393acf4f5828e7dce9994131a.png) ![聊天室麦位舞台与体验倒计时](https://developer.qcloudimg.com/http-save/yehe-12101878/1729717436741c389925327cd34f886e.png) ![消息中可收到聊天室邀请卡片](https://developer.qcloudimg.com/http-save/yehe-12101878/0b7226ac312eb83af45519430b4b1ad6.png) ![线上演示:消息列表](https://developer.qcloudimg.com/http-save/yehe-12101878/b23533443f2bdbc7f951e7ff88055bec.png) ![管理端仪表盘(线上截图)](https://developer.qcloudimg.com/http-save/yehe-12101878/38c1b65a61823df3e5ea04b2f93df784.png) ### 相对直播,聊天室解决什么问题 |维度|直播|聊天室| |:-:|:-:|:-:| |角色|主播 / 观众|房主 / 麦上用户 / 听众| |推流人数|通常 1 路主播|最多房主 + 8 麦| |治理|禁言、关播|锁麦、抱下、踢人、禁言| |入口|笔记 / 创作者中心|房间号创建或加入| |分享|直播卡片|聊天室卡片| ---- ### 一、业务架构 ``` 创建房间(mediaMode=0语音 / 1视频) ↓ 返回 roomNo + TRTC 凭证 ↓ 房主默认在「房主位」推流;听众点空麦位上麦 ↓ 麦位状态 JSON 存库 + WebSocket 全员同步 ↓ 结束 / 踢出 / 体验到期 → 推送 ended / kicked ``` 麦位槽位字段:`userId` / `muted` / `locked` / `cameraOff`。改麦位不必拆 8 张子表,状态变更后 `pushRoomState` 推给在房用户。 ---- ### 二、创建房间(核心代码) `MemberVoiceRoomServiceImpl#createRoom`: ``` public AppMemberVoiceRoomRespVO createRoom(Long loginUserId, AppMemberVoiceRoomCreateReqVO reqVO) { MemberVoiceRoomDO living = voiceRoomMapper.selectLivingByOwner(loginUserId); if (living != null) { throw exception(VOICE_ROOM_NO_CREATE); } String roomNo = nextRoomNo(); Integer mediaMode = MemberVoiceRoomMediaModeEnum.of(reqVO.getMediaMode()).getMode(); String defaultTitle = MemberVoiceRoomMediaModeEnum.isVideo(mediaMode) ? "视频聊天室" : "语音聊天室"; MemberVoiceRoomDO room = MemberVoiceRoomDO.builder() .roomNo(roomNo) .trtcRoomId("vr_" + roomNo) .title(StrUtil.blankToDefault(StrUtil.trim(reqVO.getTitle()), defaultTitle)) .ownerUserId(loginUserId) .status(MemberVoiceRoomStatusEnum.LIVING.getStatus()) .mediaMode(mediaMode) .seatJson(emptySeats()) // 8 个空麦位 .ownerMuted(false) .ownerCameraOff(false) .memberCount(1) .ownerHeartbeatTime(LocalDateTime.now()) .chatJson(new ArrayList<>()) .build(); voiceRoomMapper.insert(room); // ... return buildResp(room, loginUserId, true); } ``` 加入房间:校验进行中 + 体验是否到期,登记 Redis 在线,推送系统公屏「进入了房间」。 ---- ### 三、上麦 / 下麦 ``` public AppMemberVoiceRoomRespVO takeSeat(Long loginUserId, AppMemberVoiceRoomTakeSeatReqVO reqVO) { MemberVoiceRoomDO room = requireLiving(reqVO.getRoomNo()); if (Objects.equals(room.getOwnerUserId(), loginUserId)) { throw exception(VOICE_ROOM_ALREADY_ON_SEAT); // 房主已在房主位 } List> seats = normalizeSeats(room.getSeatJson()); Integer seatIndex = reqVO.getSeatIndex(); // 校验:已在麦上 / 座位有人 / 锁麦 Map target = seats.get(seatIndex); if (target != null && toBool(target.get("locked"))) { throw exception(VOICE_ROOM_SEAT_LOCKED); } Map seat = new HashMap<>(); seat.put("userId", loginUserId); seat.put("muted", false); seat.put("locked", false); seat.put("cameraOff", false); seats.set(seatIndex, seat); MemberVoiceRoomDO update = new MemberVoiceRoomDO(); update.setId(room.getId()); update.setSeatJson(seats); voiceRoomMapper.updateById(update); pushRoomState(room); // WebSocket 全员同步麦位 return buildResp(room, loginUserId, false); } ``` 前端:上麦成功后切换 TRTC 角色为主播并 `startLocalAudio/Video`;下麦切回观众并停采。 房主治理:锁麦 / 强制闭麦 / 抱下 / 禁言 / 踢人。视频模式下「点击放大预览、长按出管理菜单」,避免误触。 ---- ### 四、接口一览 `AppMemberVoiceRoomController`(`/member/voice-room`): ``` @PostMapping("/create") // 创建 @PostMapping("/join") // 加入 @GetMapping("/get") // 状态 @PostMapping("/seat/take") // 上麦 @PostMapping("/seat/leave") // 下麦 @PostMapping("/mute") // 静音 @PostMapping("/camera") // 摄像头 @PostMapping("/chat") // 公屏 @PostMapping("/leave") // 离开 @PostMapping("/end") // 结束 @PostMapping("/heartbeat") // 房主心跳 @PostMapping("/seat/lock") // 锁麦 @PostMapping("/seat/force-leave") // 抱下 @PostMapping("/kick") // 踢人 @PostMapping("/chat/mute") // 禁言 @PostMapping("/force-mute") // 强制闭麦 ``` WebSocket 类型: ``` String MEMBER_VOICE_ROOM_SUBSCRIBE = "member_voice_room_subscribe_type"; String MEMBER_VOICE_ROOM_CHAT = "member_voice_room_chat_type"; String MEMBER_VOICE_ROOM_STATE = "member_voice_room_state_type"; String MEMBER_VOICE_ROOM_ENDED = "member_voice_room_ended_type"; String MEMBER_VOICE_ROOM_KICKED = "member_voice_room_kicked_type"; ``` ---- ### 五、前端页面结构 **入口页** `pages/voice-room/index.vue` - 创建语音房 / 创建视频房 - 输入房间号加入 **房间页** `pages/voice-room/room.vue` |区域|内容| |:-:|:-:| |顶栏|房主信息、人数、分享、离开| |舞台|房主位 + 8 麦位网格| |中部|体验倒计时浮层| |公屏|系统消息 + 用户消息| |底部|输入、表情、更多(麦、摄像头、耳返、扬声器、下麦)| App 端通过 `hybrid/html/trtc-voice-room.html` 承载 TRTC。体验配置:`member.voice-room.max-minutes`,倒计时与直播共用同一套工具。 ---- ### 关键代码位置 - 后端:`AppMemberVoiceRoomController.java`、`MemberVoiceRoomServiceImpl.java` - 表:`member_voice_room`(见 `xhs-init.sql`) - 移动端:`pages/voice-room/index.vue`、`room.vue` - API:`sheep/api/member/voiceRoom.js` ---- ### 总结 1. 麦位 JSON + 全量状态推送,实现成本低、联调快 2. 与直播共用 TRTC / 倒计时 / 敏感词,模块边界清晰 3. 「点击放大、长按治理」提升视频房可用性 #### 下一章预告 下一章专门讲 **分享卡片**:把房间一键发到私信/群聊,以及体验倒计时与部署 SQL 清单。 ---- >**演示地址** 移动端:[http://8.146.211.120:8081/#/](https://gitee.com/link?target=http%3A%2F%2F8.146.211.120%3A8081%2F%23%2F) 管理端:[http://8.146.211.120:8088/#/](https://gitee.com/link?target=http%3A%2F%2F8.146.211.120%3A8088%2F%23%2F) **代码地址** https://gitee.com/ddeatrr/springboot_vue_xhs

优秀的个人博客,低调大师

从零用 Java 实现小红书 SpringBoot Vue UniApp(23)笔记话题多对多:一条笔记进多个话题池

## [移动端演示](http://8.146.211.120:8081/) ## [管理端演示](http://8.146.211.120:8088/) **前言** 早期实现里,一篇笔记只挂一个 `topic_id`:运营在后台改话题、用户在发现页按话题筛,都够用。但真实社区里常见 **同一篇笔记同时出现在「穿搭」和「好物分享」** 两个话题池——单字段就卡住了。 在不破坏旧数据的前提下,增加关联表 `member_note_topic_rel`,主话题仍写 `member_note.topic_id`(取多选列表第一个),其余话题走关联表;管理端笔记表单支持 **多选话题**,发现流按关联表过滤。 ![移动端:发现页](https://developer.qcloudimg.com/http-save/yehe-12101878/286e43f7a692fc2263cfa195454191e8.png) ![移动端:市集频道](https://developer.qcloudimg.com/http-save/yehe-12101878/e5dc07478f570c1581e2ac92eb013790.png) ![管理端:运营看板](https://developer.qcloudimg.com/http-save/yehe-12101878/b1db43f380cdad495ed9b9b70d2f0bf9.png) ---- ### 单话题与多对多对比 |维度|单话题字段|多对多| |:-:|:-:|:-:| |数据模型|`member_note.topic_id`|`topic_id` + `member_note_topic_rel`| |后台编辑|单选下拉|`el-select` 多选 `topicIds`| |发现页筛选|`WHERE topic_id = ?`|`note_id IN (SELECT … FROM rel WHERE topic_id = ?)`| |兼容历史|—|无 rel 时回退读 `topic_id`| ---- ### 一、表结构(可重复执行) 脚本:`sql/mysql/member_note_topic_rel.sql` - `note_id` + `topic_id` 唯一(含软删) - `sort`:0 表示主话题,与 `member_note.topic_id` 对齐 - 初始化 `INSERT … SELECT`:把已有笔记的主话题灌进 rel,避免上线后旧笔记「消失」 ---- ### 二、后端:保存时先删后插 管理端保存、App 发布带话题时,统一走 `replaceNoteTopics`: ``` private void replaceNoteTopics(Long noteId, List topicIds) { memberNoteTopicRelMapper.deleteByNoteId(noteId); if (CollUtil.isEmpty(topicIds)) { return; } int sort = 0; for (Long topicId : topicIds) { memberNoteTopicRelMapper.insert(MemberNoteTopicRelDO.builder() .noteId(noteId) .topicId(topicId) .sort(sort++) .build()); } } ``` `resolveAdminTopicIds` 校验每个话题属于当前频道后,**第一个 topicId 写回 `member_note.topic_id`**,保证旧接口、旧报表仍有一个「主话题」字段可读。 读详情时 `getNoteTopicIds`:**优先 rel 列表**,没有 rel 再降级 `note.topicId`,H5 不必一次性全量洗数据。 ---- ### 三、发现流 SQL:关联表子查询 `MemberNoteMapper` 在按话题筛选时,条件从单表字段改为: ``` note_id IN (SELECT note_id FROM member_note_topic_rel WHERE deleted = 0 AND topic_id = #{topicId}) ``` 同一套逻辑用于关注频道、同城等多场景列表,避免「后台挂了 B 话题、前台只在 A 话题能看到」的割裂。 ---- ### 四、管理端表单:多选 + 校验 管理端 `views/member/note/NoteForm.vue` - 表单项 `topicIds`:`el-select` `multiple` - 提交:`topicId: topicIds[0]`,同时传 `topicIds` 数组 - 回显:`data.topicIds?.length ? data.topicIds : [data.topicId]` 运营同学一次勾选多个话题即可,不必复制笔记。 ![管理端:笔记话题与站点配置](https://developer.qcloudimg.com/http-save/yehe-12101878/268394db07545a119337e52e5cadf14b.png) ---- ### 五、App 侧 用户发布笔记时,正文 `#话题名` 解析仍会自动挂话题;多对多主要在 **运营纠偏、活动合集** 场景由后台补挂。C 端若只选一个话题,行为与旧版一致。 ---- ### 关键代码位置 |模块|路径| |:-:|:-:| |DO / Mapper|member 模块 `MemberNoteTopicRelDO`、`MemberNoteTopicRelMapper`| |业务|`MemberNoteServiceImpl#replaceNoteTopics`、`getNoteTopicIds`| |管理端 VO|`MemberNoteSaveReqVO.topicIds`、`MemberNoteRespVO.topicIds`| |管理端 UI|`views/member/note/NoteForm.vue`| ---- ### 总结 多对多不是「再加一个下拉」,而是 **关联表 + 主话题兼容 + 列表 SQL 统一走 rel**。这样一条爆款笔记可以同时进多个话题池,后台也不用复制内容。 >源码:https://gitee.com/ddeatrr/springboot_vue_xhs

优秀的个人博客,低调大师

三角洲代练跑刀代肝护航陪玩下单系统源码:TP6+UniApp 多端私域搭建全解析

## **一、 产品定位与适用场景** 本系统采用**多端适配架构**,覆盖微信小程序、H5 及 APP 等全终端形态,具备**开源可定制**特性,可根据不同业务需求灵活扩展。 随着《三角洲行动》等战术竞技游戏爆火,市场对“跑刀、代肝、护航”的需求呈指数级增长。本系统广泛适用于: - **传统陪玩工会**、代练公司、游戏工会 - **打手工作室**、电竞俱乐部 - **游戏陪玩代打及周边服务**(代练、陪玩、代肝、护航、跑刀、养号等) 它是构建游戏圈层、企业搭建**私域流量池**并实现高效商业转化的利器。 ## **二、 技术架构与核心优势** 系统基于 **ThinkPHP6 (TP6) + Uni-app** 框架原生开发,打造全端代练/陪玩/陪练管理平台。 - **全端账号同步**:完美支持微信公众号、小程序、H5、PC 后台及 APP,用户数据无缝互通。 - **零基础安装**:核心优势在于**无需开发经验**,跟随标准化部署文档与教程即可完成源码安装。 - **工作室入驻模式**:提供灵活的“工作室/工会入驻”机制,支持多层级管理,轻松规模化运营。 ![](https://developer.qcloudimg.com/http-save/yehe-12580925/a2921cd0262eb76133b95b08d8b30e50.png) ## **三、 上线前必备:搭建环境与资质清单** 为确保系统合规稳定运行,在源码安装前需准备以下基础设施与主体资质: **序号准备项详细说明与要求** **1、域名**国内服务器必须完成 ICP 备案,用于系统访问及微信配置。 **2、服务器**推荐腾讯云 / 阿里云等主流厂商,保证 IM 通讯与数据自主可控。 **3、营业执照**企业或个体工商户均可,用于申请支付及微信类目认证。 **4、支付商户号**微信商户号、支付宝商户号(均需使用营业执照申请,打通收款)。 **5、微信生态矩阵**公众号 / 服务号(需认证)、小程序(需认证且备案)、APP 软著(如需上架)。 ### 核心代码片段(打手接单逻辑) ![](https://developer.qcloudimg.com/http-save/yehe-12580925/adc3da51a8badc56dfe99ed886551979.png)

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册