无限流跑团平台技术实现:从规则引擎到实时通信系统

📅 2026/7/22 11:04:25
无限流跑团平台技术实现:从规则引擎到实时通信系统
在游戏开发和规则设计领域无限流跑团作为一种结合角色扮演、策略决策和故事叙述的玩法近年来受到越来越多开发者和玩家的关注。这类项目不仅考验规则设计的严谨性还需要处理玩家交互、状态管理和剧情分支等复杂逻辑。对于想要深入理解游戏机制或自行搭建类似平台的开发者来说掌握其核心架构和实现路径至关重要。本文将以一个无限流跑团平台的技术实现为主线逐步拆解如何构建支持多规则、多房间、实时聊天的跑团系统。我们将从核心概念入手逐步完成环境准备、基础架构搭建、关键功能实现、运行验证以及生产级优化最终形成一个可运行、可扩展的参考实现。无论你是游戏后端开发者、独立游戏制作人还是对分布式系统设计感兴趣的工程师都能从中获得可直接复用的技术方案。1. 理解无限流跑团平台的核心机制无限流跑团平台本质上是一个多人在线角色扮演游戏MRPG系统但与传统游戏相比它更强调规则引擎的灵活性和叙事的开放性。理解其核心机制是设计技术方案的前提。1.1 无限流跑团的基本工作流程典型的无限流跑团会话遵循“准备-行动-裁决-推进”的循环模式。游戏主持人GM首先设定场景和规则玩家根据角色属性和当前情境做出行动选择规则引擎对行动结果进行裁决系统根据裁决结果推进剧情。这个过程涉及几个关键技术点规则解析需要将自然语言描述的行动转换为可计算的规则判定。状态同步所有玩家需要实时看到相同的游戏状态和剧情进展。分支管理玩家的不同选择会导致剧情走向不同的分支路径。会话持久化跑团活动可能持续数小时甚至数天需要可靠的存档机制。1.2 FX规则系统的技术特点FX规则作为一种流行的跑团规则体系其技术实现上有几个显著特征属性系统角色属性通常包括力量、敏捷、智力等基础属性以及技能、特长等衍生属性。骰子机制使用不同面数的骰子如D20、D100进行随机判定。难度等级行动的成功与否取决于属性值与难度等级的对比关系。奖惩机制优势、劣势等状态会影响骰子结果的计算方式。在技术实现上规则引擎需要能够解析规则描述、管理角色状态、执行骰子运算并返回可读的结果描述。1.3 聊天记录作为游戏载体的特殊性将聊天记录作为游戏主要交互界面这种设计带来了独特的技术挑战消息时序必须保证所有玩家看到的消息顺序完全一致。状态嵌入游戏状态变化需要与聊天消息自然融合。指令识别玩家输入的行动指令需要与普通聊天内容区分处理。历史回溯玩家需要能够查看完整的游戏历史记录。2. 环境准备与技术选型构建一个稳定可靠的无限流跑团平台需要谨慎选择技术栈和开发环境。下面是我们推荐的技术方案和配置步骤。2.1 基础开发环境配置首先确保本地开发环境满足以下要求Node.js 16推荐使用LTS版本这是后端服务的基础运行环境。Python 3.8用于规则引擎和脚本扩展的开发。Redis 6作为会话存储和消息队列使用。MongoDB 4.4存储游戏状态、角色数据和聊天历史。安装完成后通过以下命令验证环境版本# 检查Node.js版本 node --version # 检查Python版本 python --version # 检查Redis是否运行 redis-cli ping # 检查MongoDB连接 mongo --eval db.version()2.2 后端技术栈选择基于性能、开发效率和生态成熟度考虑我们选择以下技术栈Express.js轻量级Web框架适合实时应用开发。Socket.IO处理WebSocket连接实现实时通信。MongooseMongoDB对象建模工具提供数据验证和业务逻辑钩子。Joi数据验证库确保输入数据的完整性。Winston日志记录库便于问题排查和监控。创建项目并安装依赖# 初始化项目 mkdir infinite-flow-platform cd infinite-flow-platform npm init -y # 安装核心依赖 npm install express socket.io mongoose joi winston npm install --save-dev nodemon eslint2.3 前端技术方案虽然前端实现不是本文重点但为了完整性我们提供基础的前端配置!DOCTYPE html html head title无限流跑团平台/title script src/socket.io/socket.io.js/script style .chat-container { max-height: 400px; overflow-y: auto; } .message { margin: 10px 0; padding: 8px; border-radius: 4px; } .system-message { background-color: #f0f0f0; } .player-message { background-color: #e3f2fd; } /style /head body div idgame-container div idchat-history classchat-container/div input typetext idmessage-input placeholder输入行动或聊天内容 button onclicksendMessage()发送/button /div script const socket io(); // 前端逻辑将在后续章节实现 /script /body /html2.4 项目结构设计合理的项目结构是维护性的基础建议采用以下目录组织infinite-flow-platform/ ├── src/ │ ├── models/ # 数据模型 │ │ ├── Room.js # 房间模型 │ │ ├── Player.js # 玩家模型 │ │ └── Message.js # 消息模型 │ ├── services/ # 业务服务 │ │ ├── ruleEngine/ # 规则引擎 │ │ ├── chatService/ # 聊天服务 │ │ └── gameService/ # 游戏服务 │ ├── routes/ # API路由 │ ├── middleware/ # 中间件 │ └── app.js # 应用入口 ├── public/ # 静态文件 ├── tests/ # 测试文件 ├── package.json └── README.md3. 核心数据模型设计数据模型是系统稳定性的基石。无限流跑团平台需要精心设计的数据结构来支持复杂的游戏逻辑。3.1 房间Room模型设计房间是跑团活动的基本单位每个房间包含完整的游戏会话状态// models/Room.js const mongoose require(mongoose); const roomSchema new mongoose.Schema({ name: { type: String, required: true, trim: true }, description: { type: String, default: }, ruleSystem: { type: String, enum: [FX, DND, CUSTOM], default: FX }, maxPlayers: { type: Number, default: 6, min: 2, max: 20 }, currentPlayers: [{ type: mongoose.Schema.Types.ObjectId, ref: Player }], gameMaster: { type: mongoose.Schema.Types.ObjectId, ref: Player, required: true }, status: { type: String, enum: [WAITING, ACTIVE, PAUSED, COMPLETED], default: WAITING }, currentScene: { title: String, description: String, choices: [{ text: String, nextScene: String, conditions: mongoose.Schema.Types.Mixed }] }, createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now } }); // 更新时间戳自动更新 roomSchema.pre(save, function(next) { this.updatedAt Date.now(); next(); }); module.exports mongoose.model(Room, roomSchema);3.2 玩家Player模型设计玩家模型需要存储角色属性、游戏状态和会话信息// models/Player.js const mongoose require(mongoose); const playerSchema new mongoose.Schema({ username: { type: String, required: true, unique: true, trim: true }, currentRoom: { type: mongoose.Schema.Types.ObjectId, ref: Room }, character: { name: String, attributes: { strength: { type: Number, default: 10 }, agility: { type: Number, default: 10 }, intelligence: { type: Number, default: 10 }, // FX规则特有属性 magic: { type: Number, default: 0 }, luck: { type: Number, default: 10 } }, skills: mongoose.Schema.Types.Mixed, inventory: [{ name: String, quantity: Number, description: String }], health: { current: { type: Number, default: 100 }, max: { type: Number, default: 100 } } }, isOnline: { type: Boolean, default: false }, lastActive: { type: Date, default: Date.now }, role: { type: String, enum: [PLAYER, GAME_MASTER], default: PLAYER } }); // 索引优化查询性能 playerSchema.index({ username: 1 }); playerSchema.index({ currentRoom: 1 }); playerSchema.index({ isOnline: 1 }); module.exports mongoose.model(Player, playerSchema);3.3 消息Message模型设计聊天消息需要支持丰富的类型和元数据// models/Message.js const mongoose require(mongoose); const messageSchema new mongoose.Schema({ room: { type: mongoose.Schema.Types.ObjectId, ref: Room, required: true }, sender: { type: mongoose.Schema.Types.ObjectId, ref: Player, required: true }, content: { type: String, required: true, trim: true }, type: { type: String, enum: [CHAT, ACTION, SYSTEM, DICE_ROLL, GAME_EVENT], default: CHAT }, metadata: { diceResult: { formula: String, result: Number, details: [Number] }, action: { type: String, target: String, success: Boolean, modifiers: [String] }, gameEvent: { eventType: String, parameters: mongoose.Schema.Types.Mixed } }, timestamp: { type: Date, default: Date.now }, isHidden: { type: Boolean, default: false } }); // 复合索引优化消息查询 messageSchema.index({ room: 1, timestamp: 1 }); messageSchema.index({ type: 1, timestamp: 1 }); module.exports mongoose.model(Message, messageSchema);4. 规则引擎的实现规则引擎是跑团平台的核心负责将玩家行动转换为游戏结果。FX规则系统的实现需要处理属性判定、骰子运算和难度计算。4.1 骰子运算器基础实现首先实现一个支持多种骰子类型的运算器// services/ruleEngine/diceRoller.js class DiceRoller { /** * 解析骰子表达式如 2d63 或 d20 * param {string} expression 骰子表达式 * returns {Object} 解析结果 */ static parseDiceExpression(expression) { const regex /^(\d*)d(\d)([-]\d)?$/; const match expression.toLowerCase().match(regex); if (!match) { throw new Error(无效的骰子表达式: ${expression}); } const count match[1] ? parseInt(match[1]) : 1; const sides parseInt(match[2]); const modifier match[3] ? parseInt(match[3]) : 0; return { count, sides, modifier }; } /** * 执行骰子投掷 * param {string} expression 骰子表达式 * returns {Object} 投掷结果 */ static roll(expression) { const { count, sides, modifier } this.parseDiceExpression(expression); const rolls []; let total 0; for (let i 0; i count; i) { const roll Math.floor(Math.random() * sides) 1; rolls.push(roll); total roll; } total modifier; return { expression, rolls, modifier, total, details: ${count}d${sides}${modifier 0 ? : }${modifier ! 0 ? modifier : } }; } /** * FX规则专用的属性检定 * param {number} attributeValue 属性值 * param {number} difficulty 难度等级 * returns {Object} 检定结果 */ static fxAttributeCheck(attributeValue, difficulty 10) { const rollResult this.roll(d20); const success rollResult.total attributeValue; const margin Math.abs(attributeValue - rollResult.total); // FX规则的特殊成功/失败判定 let degree 普通; if (rollResult.total 1) degree 大成功; else if (rollResult.total 20) degree 大失败; else if (margin 10) degree success ? 极成功 : 极失败; return { success, degree, roll: rollResult.total, attributeValue, difficulty, margin }; } } module.exports DiceRoller;4.2 规则解析器实现规则解析器负责处理自然语言描述的行动并将其转换为可执行的规则判定// services/ruleEngine/ruleParser.js class RuleParser { /** * 解析玩家行动描述 * param {string} actionText 行动描述 * param {Object} character 角色数据 * returns {Object} 解析后的行动对象 */ static parseAction(actionText, character) { const action { type: UNKNOWN, skill: null, target: null, difficulty: 10, modifiers: [] }; // 技能检定识别 const skillMatch actionText.match(/(侦查|聆听|潜行|说服|战斗|魔法)检定?/); if (skillMatch) { action.type SKILL_CHECK; action.skill skillMatch[1]; action.difficulty this.determineDifficulty(actionText); } // 攻击行动识别 if (actionText.includes(攻击) || actionText.includes(打击)) { action.type ATTACK; action.skill 战斗; } // 使用物品识别 const itemMatch actionText.match(/使用\s*(\S)/); if (itemMatch) { action.type USE_ITEM; action.target itemMatch[1]; } return action; } /** * 根据描述文本确定难度等级 * param {string} text 描述文本 * returns {number} 难度值 */ static determineDifficulty(text) { if (text.includes(极其困难) || text.includes(几乎不可能)) return 18; if (text.includes(困难) || text.includes(有挑战)) return 15; if (text.includes(中等) || text.includes(一般)) return 12; return 10; // 简单难度 } } module.exports RuleParser;4.3 完整的规则引擎服务将各个组件组合成完整的规则引擎服务// services/ruleEngine/index.js const DiceRoller require(./diceRoller); const RuleParser require(./ruleParser); class RuleEngine { constructor(ruleSystem FX) { this.ruleSystem ruleSystem; } /** * 处理玩家行动 * param {Object} player 玩家对象 * param {string} actionText 行动描述 * returns {Object} 行动结果 */ processAction(player, actionText) { const action RuleParser.parseAction(actionText, player.character); let result { success: false, message: }; switch (action.type) { case SKILL_CHECK: result this.handleSkillCheck(player, action); break; case ATTACK: result this.handleAttack(player, action); break; case USE_ITEM: result this.handleItemUse(player, action); break; default: result.message 无法识别的行动类型; } return { action, result, timestamp: new Date(), playerId: player._id }; } /** * 处理技能检定 * param {Object} player 玩家对象 * param {Object} action 行动对象 * returns {Object} 检定结果 */ handleSkillCheck(player, action) { const skillValue player.character.attributes[this.mapSkillToAttribute(action.skill)] || 10; const checkResult DiceRoller.fxAttributeCheck(skillValue, action.difficulty); return { success: checkResult.success, message: ${player.character.name}进行${action.skill}检定: ${checkResult.roll}/${skillValue} ${checkResult.degree}, details: checkResult }; } /** * 技能到属性的映射 * param {string} skill 技能名称 * returns {string} 属性名称 */ mapSkillToAttribute(skill) { const mapping { 侦查: intelligence, 聆听: intelligence, 潜行: agility, 说服: intelligence, 战斗: strength, 魔法: magic }; return mapping[skill] || intelligence; } } module.exports RuleEngine;5. 实时通信与聊天系统跑团平台的实时性要求很高需要稳定的双向通信机制。我们使用Socket.IO来实现实时聊天和游戏状态同步。5.1 Socket事件处理基础架构首先建立Socket事件处理的基础架构// services/chatService/socketHandler.js const RuleEngine require(../ruleEngine); const Message require(../../models/Message); class SocketHandler { constructor(io) { this.io io; this.ruleEngine new RuleEngine(); this.setupEventHandlers(); } setupEventHandlers() { this.io.on(connection, (socket) { console.log(用户连接: ${socket.id}); // 加入房间 socket.on(join_room, (data) this.handleJoinRoom(socket, data)); // 发送消息 socket.on(send_message, (data) this.handleSendMessage(socket, data)); // 执行行动 socket.on(player_action, (data) this.handlePlayerAction(socket, data)); // 断开连接 socket.on(disconnect, () this.handleDisconnect(socket)); }); } /** * 处理玩家加入房间 */ async handleJoinRoom(socket, data) { const { roomId, playerId } data; try { socket.join(roomId); socket.currentRoom roomId; socket.playerId playerId; // 通知房间内其他玩家 socket.to(roomId).emit(player_joined, { playerId, timestamp: new Date() }); // 发送房间当前状态 const roomState await this.getRoomState(roomId); socket.emit(room_state, roomState); } catch (error) { socket.emit(error, { message: 加入房间失败 }); } } /** * 处理消息发送 */ async handleSendMessage(socket, data) { const { content, type CHAT } data; const roomId socket.currentRoom; const playerId socket.playerId; if (!roomId || !playerId) { socket.emit(error, { message: 未加入房间或玩家信息缺失 }); return; } try { // 保存消息到数据库 const message new Message({ room: roomId, sender: playerId, content, type }); await message.save(); // 广播消息到房间 this.io.to(roomId).emit(new_message, { id: message._id, sender: playerId, content, type, timestamp: message.timestamp }); } catch (error) { socket.emit(error, { message: 发送消息失败 }); } } } module.exports SocketHandler;5.2 游戏行动处理逻辑处理玩家发起的游戏行动并应用规则引擎// 在SocketHandler类中继续添加方法 /** * 处理玩家行动 */ async handlePlayerAction(socket, data) { const { actionText } data; const roomId socket.currentRoom; const playerId socket.playerId; if (!roomId || !playerId) { socket.emit(error, { message: 未加入房间或玩家信息缺失 }); return; } try { // 获取玩家完整信息 const player await this.getPlayerInfo(playerId); // 使用规则引擎处理行动 const actionResult this.ruleEngine.processAction(player, actionText); // 保存行动结果消息 const message new Message({ room: roomId, sender: playerId, content: actionResult.result.message, type: ACTION, metadata: { action: actionResult.action, diceResult: actionResult.result.details } }); await message.save(); // 广播行动结果 this.io.to(roomId).emit(action_result, { player: player.character.name, action: actionResult.action, result: actionResult.result, timestamp: new Date() }); } catch (error) { socket.emit(error, { message: 执行行动失败 }); } } /** * 获取玩家信息 */ async getPlayerInfo(playerId) { // 这里应该从数据库查询玩家信息 // 简化示例返回模拟数据 return { _id: playerId, character: { name: 测试玩家, attributes: { strength: 12, agility: 14, intelligence: 16, magic: 8, luck: 10 } } }; } /** * 获取房间状态 */ async getRoomState(roomId) { // 从数据库查询房间的当前状态 // 包括玩家列表、当前场景、消息历史等 return { roomId, players: [], currentScene: {}, messageHistory: [] }; }5.3 前端通信实现完善前端JavaScript代码实现完整的通信逻辑// 前端JavaScript代码 class GameClient { constructor() { this.socket io(); this.currentRoom null; this.playerId null; this.setupEventListeners(); } setupEventListeners() { // 连接事件 this.socket.on(connect, () { console.log(已连接到服务器); }); // 新消息事件 this.socket.on(new_message, (data) { this.displayMessage(data); }); // 行动结果事件 this.socket.on(action_result, (data) { this.displayActionResult(data); }); // 错误处理 this.socket.on(error, (data) { alert(错误: ${data.message}); }); } /** * 加入房间 */ joinRoom(roomId, playerId) { this.currentRoom roomId; this.playerId playerId; this.socket.emit(join_room, { roomId, playerId }); } /** * 发送消息 */ sendMessage(content, type CHAT) { if (!this.currentRoom) { alert(请先加入房间); return; } this.socket.emit(send_message, { content, type }); document.getElementById(message-input).value ; } /** * 执行游戏行动 */ performAction(actionText) { this.socket.emit(player_action, { actionText }); } /** * 显示消息 */ displayMessage(data) { const chatHistory document.getElementById(chat-history); const messageElement document.createElement(div); messageElement.className message ${data.type.toLowerCase()}-message; messageElement.innerHTML strong${data.sender}:/strong ${data.content} small${new Date(data.timestamp).toLocaleTimeString()}/small ; chatHistory.appendChild(messageElement); chatHistory.scrollTop chatHistory.scrollHeight; } } // 初始化客户端 const gameClient new GameClient(); // 发送消息函数 function sendMessage() { const input document.getElementById(message-input); const content input.value.trim(); if (content) { if (content.startsWith(/action )) { // 处理游戏行动 const actionText content.substring(8); gameClient.performAction(actionText); } else { // 普通聊天消息 gameClient.sendMessage(content); } } } // 回车发送消息 document.getElementById(message-input).addEventListener(keypress, (e) { if (e.key Enter) { sendMessage(); } });6. 系统集成与运行验证将各个模块整合成完整的应用并进行端到端的测试验证。6.1 应用入口文件配置创建主应用文件整合所有服务// app.js const express require(express); const http require(http); const socketIo require(socket.io); const mongoose require(mongoose); const winston require(winston); const SocketHandler require(./services/chatService/socketHandler); // 日志配置 const logger winston.createLogger({ level: info, format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.File({ filename: error.log, level: error }), new winston.transports.File({ filename: combined.log }) ] }); // 数据库连接 mongoose.connect(mongodb://localhost:27017/infinite_flow, { useNewUrlParser: true, useUnifiedTopology: true }).then(() { logger.info(MongoDB连接成功); }).catch(err { logger.error(MongoDB连接失败, err); }); const app express(); const server http.createServer(app); const io socketIo(server, { cors: { origin: *, methods: [GET, POST] } }); // 静态文件服务 app.use(express.static(public)); // API路由 app.use(express.json()); app.use(/api/rooms, require(./routes/rooms)); app.use(/api/players, require(./routes/players)); // Socket处理 new SocketHandler(io); // 错误处理中间件 app.use((err, req, res, next) { logger.error(服务器错误, err); res.status(500).json({ error: 内部服务器错误 }); }); const PORT process.env.PORT || 3000; server.listen(PORT, () { logger.info(服务器运行在端口 ${PORT}); console.log(无限流跑团平台已启动: http://localhost:${PORT}); });6.2 基础API路由实现实现房间和玩家管理的REST API// routes/rooms.js const express require(express); const router express.Router(); const Room require(../models/Room); // 获取房间列表 router.get(/, async (req, res) { try { const rooms await Room.find({ status: WAITING }) .populate(gameMaster, username) .select(name description maxPlayers currentPlayers.length); res.json(rooms); } catch (error) { res.status(500).json({ error: 获取房间列表失败 }); } }); // 创建新房间 router.post(/, async (req, res) { try { const { name, description, ruleSystem, maxPlayers, gameMasterId } req.body; const room new Room({ name, description, ruleSystem, maxPlayers, gameMaster: gameMasterId, currentPlayers: [gameMasterId] }); await room.save(); res.status(201).json(room); } catch (error) { res.status(400).json({ error: 创建房间失败 }); } }); module.exports router;6.3 运行验证测试创建测试脚本来验证系统功能// tests/integration.test.js const request require(supertest); const app require(../app); const Room require(../models/Room); const Player require(../models/Player); describe(无限流跑团平台集成测试, () { beforeAll(async () { // 测试前清理数据 await Room.deleteMany({}); await Player.deleteMany({}); }); test(创建房间和玩家, async () { // 创建测试玩家 const playerResponse await request(app) .post(/api/players) .send({ username: test_gm }); expect(playerResponse.status).toBe(201); // 使用创建的玩家作为GM创建房间 const roomResponse await request(app) .post(/api/rooms) .send({ name: 测试房间, description: 集成测试房间, ruleSystem: FX, maxPlayers: 4, gameMasterId: playerResponse.body._id }); expect(roomResponse.status).toBe(201); expect(roomResponse.body.name).toBe(测试房间); }); test(房间列表查询, async () { const response await request(app).get(/api/rooms); expect(response.status).toBe(200); expect(Array.isArray(response.body)).toBe(true); expect(response.body.length).toBeGreaterThan(0); }); });运行测试验证系统功能# 安装测试依赖 npm install --save-dev jest supertest # 运行测试 npx jest tests/integration.test.js6.4 端到端功能验证手动测试完整流程启动服务node app.js访问应用打开浏览器访问http://localhost:3000创建房间通过API或界面创建测试房间加入房间多个浏览器标签模拟不同玩家加入测试聊天发送普通消息验证实时通信测试行动使用/action 侦查检定格式测试规则引擎验证持久化刷新页面验证状态恢复预期结果所有玩家能看到一致的聊天记录行动能正确触发规则判定数据能持久化保存。7. 常见问题排查与优化在实际部署和运行过程中可能会遇到各种问题。下面提供常见问题的排查路径和解决方案。7.1 连接与通信问题问题现象可能原因检查方式解决方案无法连接到服务器端口被占用、防火墙阻止检查端口状态、防火墙规则更换端口或配置防火墙例外Socket连接频繁断开网络不稳定、心跳配置不当检查网络延迟、Socket配置调整心跳间隔、增加重连机制消息延迟或丢失服务器负载高、网络拥堵监控服务器性能、网络质量优化代码、增加消息确认机制心跳配置优化示例// Socket.IO配置优化 const io socketIo(server, { pingTimeout: 60000, pingInterval: 25000, cors: { origin: *, methods: [GET, POST] } });7.2 规则引擎问题问题现象可能原因检查方式解决方案行动解析错误自然语言处理不准确检查行动描述日志完善规则解析器、增加训练数据骰子结果异常随机数生成问题验证骰子算法使用更安全的随机数生成器属性计算错误映射关系配置错误检查属性映射表完善技能-属性映射逻辑增强的规则解析器// 改进的行动识别 static parseAction(actionText, character) { // 增加更多的模式匹配 const patterns [ { regex: /(?:进行|做)(\S)检定/, type: SKILL_CHECK }, { regex: /攻击\s*(\S)/, type: ATTACK }, { regex: /使用\s*(\S)/, type: USE_ITEM } ]; for (let pattern of patterns) { const match actionText.match(pattern.regex); if (match) { return { type: pattern.type, skill: match[1] }; } } return { type: UNKNOWN }; }7.3 性能与扩展性问题数据库优化建议为常用查询字段建立索引使用连接池管理数据库连接对大量消息记录进行分页查询代码层面的性能优化// 使用批量操作提高性能 async function saveMessages(messages) { try { await Message.insertMany(messages, { ordered: false }); } catch (error) { logger.error(批量保存消息失败, error); } } // 使用Redis缓存热点数据 const redis require(redis); const client redis.createClient(); async function getRoomStateWithCache(roomId) { const cacheKey room:${roomId}:state; const cached await client.get(cacheKey); if (cached) { return JSON.parse(cached); } const state await getRoomState(roomId); await client.setex(cacheKey, 300, JSON.stringify(state)); // 缓存5分钟 return state; }