Files
youlegames/codes/games/server/docs/guides/development/07-工具模块.md
2026-02-04 23:47:45 +08:00

1520 lines
37 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 工具模块详解
## 📋 文档概述
本文档详细说明进贤麻将的工具模块系统,包括:
- **Logger.js** - 日志管理器
- **ErrorHandler.js** - 错误处理器
- **shared/utils** - 共享工具类
- 工具模块的使用方法和最佳实践
**文档目标**:帮助开发者正确使用日志系统和错误处理机制,提升代码质量和可维护性。
---
## 🎯 工具模块架构
### 系统组成
```
工具模块系统
├── utils/ # 子游戏专用工具
│ ├── Logger.js # 日志管理器
│ └── ErrorHandler.js # 错误处理器
└── shared/utils/ # 前后端共享工具
├── ArrayUtils.js # 数组操作工具
├── CardSourceInfoHelper.js # 牌源信息辅助
├── GameContextHelper.js # 游戏上下文辅助
├── GameStateHelper.js # 游戏状态辅助
├── MahjongCardUniqueId.js # 麻将牌唯一ID
└── RoomConfigUtils.js # 房间配置工具
```
### 设计原则
1. **分离关注点**:日志和错误处理分开管理
2. **ES5兼容**兼容浏览器和Node.js环境
3. **统一接口**提供统一的API调用方式
4. **性能优化**:最小化性能开销
5. **易于使用**简单直观的API设计
---
## 📝 Logger.js - 日志管理器
### 核心功能
**文件位置**`server/games2/jinxianmahjong/utils/Logger.js`
**版本**v1.0.0
**总行数**606行
Logger提供了完整的日志管理功能
- ✅ 6级日志系统TRACE/DEBUG/INFO/WARN/ERROR/FATAL
- ✅ 日志格式化和美化输出
- ✅ 日志历史记录和查询
- ✅ 日志统计分析
- ✅ 性能计时功能
- ✅ 分组日志输出
- ✅ 控制台颜色支持
### 日志级别定义
```javascript
LOG_LEVELS: {
TRACE: 0, // 最详细的信息,用于跟踪程序执行
DEBUG: 1, // 调试信息,开发时使用
INFO: 2, // 一般信息,记录程序运行状态
WARN: 3, // 警告信息,可能的问题
ERROR: 4, // 错误信息,程序运行错误
FATAL: 5 // 致命错误,程序无法继续运行
}
```
**级别控制**:只有 `>= currentLevel` 的日志才会输出
**颜色编码**
| 级别 | 颜色 | 说明 |
|------|------|------|
| TRACE | 白色 | 详细跟踪信息 |
| DEBUG | 青色 | 开发调试信息 |
| INFO | 绿色 | 正常运行信息 |
| WARN | 黄色 | 警告提示信息 |
| ERROR | 红色 | 错误异常信息 |
| FATAL | 紫色 | 致命错误信息 |
### 配置参数
```javascript
config: {
// 当前日志级别
currentLevel: 1, // 默认DEBUG级别
// 输出设置
enableConsole: true, // 启用控制台输出
enableColors: true, // 启用颜色(如果支持)
enableTimestamp: true, // 启用时间戳
enableModuleName: true, // 启用模块名
enableStackTrace: false, // 启用堆栈跟踪ERROR级别以上
// 格式设置
timestampFormat: 'ISO', // 时间戳格式: 'ISO', 'LOCAL', 'TIMESTAMP'
moduleName: 'JinXianMahjong', // 模块名
// 性能设置
maxLogHistory: 1000, // 最大日志历史记录数
flushInterval: 0 // 刷新间隔毫秒0表示立即输出
}
```
### 核心API
#### 1. 设置日志级别
```javascript
/**
* 设置日志级别
* @param {number|string} level - 日志级别(数字或字符串)
*/
Logger.setLevel(level)
// 使用示例
Logger.setLevel('DEBUG'); // 使用字符串
Logger.setLevel(1); // 使用数字
Logger.setLevel('INFO'); // 只输出INFO及以上级别
```
#### 2. 记录日志
**基础日志方法**
```javascript
/**
* 记录日志
* @param {string} tag - 标签(模块/功能名称)
* @param {string} message - 日志消息
* @param {Object} data - 附加数据(可选)
*/
Logger.trace(tag, message, data)
Logger.debug(tag, message, data)
Logger.info(tag, message, data)
Logger.warn(tag, message, data)
Logger.error(tag, message, data)
Logger.fatal(tag, message, data)
```
**使用示例**
```javascript
// 基础日志
Logger.info('GameController', '游戏开始');
// 带附加数据的日志
Logger.debug('MahjongService', '发牌完成', {
playerCount: 4,
tilesPerPlayer: 13
});
// 警告日志
Logger.warn('OperationManager', '玩家操作超时', {
playerId: 'player001',
operation: 'discard',
timeout: 15000
});
// 错误日志
Logger.error('RpcHandler', 'RPC调用失败', {
method: 'player_discard',
errorCode: 2001,
error: error.message
});
// 致命错误
Logger.fatal('System', '系统初始化失败', {
reason: '依赖模块加载失败'
});
```
#### 3. 分组日志
```javascript
/**
* 分组日志(用于组织相关日志)
*/
Logger.group(groupName) // 开始分组
Logger.groupEnd() // 结束分组
Logger.groupCollapsed(groupName) // 开始折叠分组(默认折叠)
// 使用示例
Logger.group('游戏初始化');
Logger.info('Init', '加载配置');
Logger.info('Init', '创建游戏状态');
Logger.info('Init', '初始化牌墙');
Logger.groupEnd();
```
#### 4. 性能计时
```javascript
/**
* 性能计时
*/
Logger.time(label) // 开始计时
Logger.timeEnd(label) // 结束计时并输出
// 使用示例
Logger.time('胡牌检测');
var result = WinDetectionFactory.detectWin(handTiles, jingInfo);
Logger.timeEnd('胡牌检测'); // 输出: 胡牌检测: 3.142ms
```
#### 5. 表格输出
```javascript
/**
* 表格输出(用于输出结构化数据)
* @param {Array|Object} data - 表格数据
*/
Logger.table(data)
// 使用示例
var players = [
{ id: 'p1', name: '玩家1', score: 100 },
{ id: 'p2', name: '玩家2', score: 50 },
{ id: 'p3', name: '玩家3', score: -30 },
{ id: 'p4', name: '玩家4', score: -20 }
];
Logger.table(players);
```
#### 6. 日志历史
```javascript
/**
* 获取日志历史
* @param {number} limit - 返回数量限制(可选)
* @param {number} level - 过滤日志级别(可选)
* @returns {Array} 日志历史数组
*/
Logger.getHistory(limit, level)
// 使用示例
var allLogs = Logger.getHistory(); // 获取所有日志
var last100 = Logger.getHistory(100); // 获取最近100条
var errors = Logger.getHistory(null, Logger.LOG_LEVELS.ERROR); // 只获取错误日志
```
#### 7. 清空历史
```javascript
/**
* 清空日志历史
*/
Logger.clearHistory()
```
#### 8. 日志统计
```javascript
/**
* 获取日志统计信息
* @returns {Object} 统计对象
*/
Logger.getStatistics()
// 返回示例
{
total: 1523, // 总日志数
byLevel: { // 按级别统计
0: 234, // TRACE
1: 567, // DEBUG
2: 456, // INFO
3: 123, // WARN
4: 89, // ERROR
5: 54 // FATAL
},
startTime: 1697376000000, // 统计开始时间
duration: 3600000, // 运行时长(毫秒)
rate: 0.42 // 日志频率(条/秒)
}
```
#### 9. 重置统计
```javascript
/**
* 重置日志统计
*/
Logger.resetStatistics()
```
#### 10. 配置管理
```javascript
/**
* 配置Logger
* @param {Object} options - 配置选项
*/
Logger.configure(options)
// 使用示例
Logger.configure({
currentLevel: Logger.LOG_LEVELS.INFO, // 设置为INFO级别
enableTimestamp: true, // 启用时间戳
enableColors: true, // 启用颜色
timestampFormat: 'ISO', // 使用ISO格式
maxLogHistory: 500 // 最多保存500条历史
});
```
### 日志格式
**标准格式**
```
[时间戳] [级别] [模块名:标签] 消息内容
```
**输出示例**
```
[2025-10-15T10:30:45.123Z] [INFO] [JinXianMahjong:GameController] 游戏开始
[2025-10-15T10:30:45.456Z] [DEBUG] [JinXianMahjong:MahjongService] 发牌完成 { playerCount: 4, tilesPerPlayer: 13 }
[2025-10-15T10:30:46.789Z] [WARN] [JinXianMahjong:OperationManager] 玩家操作超时 { playerId: 'player001', timeout: 15000 }
[2025-10-15T10:30:47.012Z] [ERROR] [JinXianMahjong:RpcHandler] RPC调用失败 { method: 'player_discard', errorCode: 2001 }
```
### 实际使用场景
#### 场景1游戏流程日志
```javascript
// GameController.js
GameController.prototype.startGame = function() {
Logger.info('GameController', '======= 开始新游戏 =======');
Logger.group('游戏初始化');
Logger.debug('GameController', '玩家数量:', this.players.length);
Logger.debug('GameController', '房间配置:', this.roomtype);
try {
// 初始化游戏状态
Logger.time('初始化游戏状态');
this.gameState = GameStateManager.createGameState(
this.roomcode,
this.roomtype,
this.getPlayerIds()
);
Logger.timeEnd('初始化游戏状态');
Logger.info('GameController', '游戏状态创建成功');
// 初始化牌墙
Logger.time('初始化牌墙');
this.mahjongWall = MahjongWall.initialize(this.gameState);
Logger.timeEnd('初始化牌墙');
Logger.info('GameController', '牌墙初始化完成');
// 发牌
Logger.time('发牌');
this.dealTiles();
Logger.timeEnd('发牌');
Logger.info('GameController', '发牌完成');
} catch (error) {
Logger.error('GameController', '游戏初始化失败', {
error: error.message,
stack: error.stack
});
throw error;
}
Logger.groupEnd();
Logger.info('GameController', '======= 游戏初始化完成 =======');
};
```
#### 场景2调试日志
```javascript
// MahjongGameService.js
MahjongGameService.prototype.checkWinCondition = function(playerHand, gameState) {
Logger.debug('MahjongGameService', '开始检查胡牌条件', {
playerId: playerHand.playerId,
handSize: playerHand.tiles.length
});
var rules = gameState.rules;
Logger.trace('MahjongGameService', '规则配置', {
useJing: rules.gameRules.useJing,
minFan: rules.winConditions.minFan
});
// 胡牌检测
Logger.time('胡牌检测');
var winResult = WinDetectionFactory.detectWin(playerHand, gameState.jingInfo);
Logger.timeEnd('胡牌检测');
Logger.debug('MahjongGameService', '胡牌检测结果', {
canWin: winResult.canWin,
patterns: winResult.patterns
});
if (!winResult.canWin) {
Logger.debug('MahjongGameService', '不满足胡牌条件');
return { canWin: false };
}
// 检查分数
var score = ScoreCalculation.calculateWinScore(
winResult.patterns,
gameState.jingInfo,
gameState
);
Logger.debug('MahjongGameService', '计算得分', {
totalScore: score.totalScore,
minFan: rules.winConditions.minFan
});
if (score.totalScore < rules.winConditions.minFan) {
Logger.warn('MahjongGameService', '未达到起胡分数要求', {
currentScore: score.totalScore,
required: rules.winConditions.minFan
});
return {
canWin: false,
reason: '未达到起胡分数要求(需' + rules.winConditions.minFan + '分)'
};
}
Logger.info('MahjongGameService', '胡牌条件满足', {
playerId: playerHand.playerId,
score: score.totalScore
});
return {
canWin: true,
patterns: winResult.patterns,
score: score
};
};
```
#### 场景3错误日志
```javascript
// RpcHandler.js
RpcHandler.prototype.handleRequest = function(pack, room, callback) {
var method = pack.cmd;
Logger.info('RpcHandler', 'RPC请求', {
method: method,
playerId: pack.playerid
});
try {
// 验证玩家
if (!this._validatePlayer(pack.playerid, room)) {
Logger.warn('RpcHandler', '玩家验证失败', {
playerId: pack.playerid,
roomcode: room.roomcode
});
return callback({ error: '玩家不存在' });
}
// 执行RPC方法
var result = this._executeMethod(method, pack, room);
Logger.debug('RpcHandler', 'RPC执行成功', {
method: method,
result: result
});
callback(result);
} catch (error) {
Logger.error('RpcHandler', 'RPC执行失败', {
method: method,
playerId: pack.playerid,
error: error.message,
stack: error.stack
});
callback({ error: '处理请求失败' });
}
};
```
---
## ⚠️ ErrorHandler.js - 错误处理器
### 核心功能
**文件位置**`server/games2/jinxianmahjong/utils/ErrorHandler.js`
**版本**v1.0.0
**总行数**659行
ErrorHandler提供了完整的错误管理功能
- ✅ 标准化错误代码系统
- ✅ 错误分类和级别管理
- ✅ 错误创建和格式化
- ✅ 错误统计和追踪
- ✅ 安全执行包装
- ✅ 错误恢复策略
### 错误代码范围
```javascript
ERROR_CODE_RANGES: {
CLIENT_START: 1000, // 客户端/接口错误
CLIENT_END: 1999,
VALIDATION_START: 2000, // 数据验证错误
VALIDATION_END: 2999,
GAME_LOGIC_START: 3000, // 游戏逻辑错误
GAME_LOGIC_END: 3999,
BUSINESS_START: 4000, // 业务逻辑错误
BUSINESS_END: 4999,
NETWORK_START: 5000, // 网络通信错误
NETWORK_END: 5999,
SYSTEM_START: 9000 // 系统错误
}
```
### 错误代码定义
#### 1. 框架接口错误1000-1999
```javascript
INTERFACE_INVALID_PARAMS: 1001, // 接口参数无效
INTERFACE_ROOM_NOT_FOUND: 1002, // 房间不存在
INTERFACE_PLAYER_NOT_FOUND: 1003, // 玩家不存在
INTERFACE_CALLBACK_MISSING: 1004, // 回调函数缺失
INTERFACE_METHOD_NOT_IMPLEMENTED: 1005, // 接口方法未实现
INTERFACE_TIMEOUT: 1006, // 接口调用超时
INTERFACE_UNAUTHORIZED: 1007, // 接口未授权
INTERFACE_RATE_LIMITED: 1008, // 接口调用频率限制
```
#### 2. 游戏逻辑错误2000-2999
```javascript
GAME_INVALID_ACTION: 2001, // 无效的游戏操作
GAME_WRONG_PHASE: 2002, // 游戏阶段错误
GAME_PLAYER_LIMIT: 2003, // 玩家数量限制
GAME_ROOM_FULL: 2004, // 房间已满
GAME_ALREADY_STARTED: 2005, // 游戏已开始
GAME_NOT_STARTED: 2006, // 游戏未开始
GAME_INVALID_CARD: 2007, // 无效牌张
GAME_INVALID_OPERATION: 2008, // 无效操作
GAME_TIMEOUT: 2009, // 游戏操作超时
GAME_INSUFFICIENT_PLAYERS: 2010, // 玩家不足
```
#### 3. 数据验证错误3000-3999
```javascript
VALIDATION_ROOMTYPE: 3001, // RoomType格式错误
VALIDATION_CARD_DATA: 3002, // 牌张数据无效
VALIDATION_PLAYER_DATA: 3003, // 玩家数据无效
VALIDATION_ROOM_DATA: 3004, // 房间数据无效
VALIDATION_GAME_STATE: 3005, // 游戏状态无效
VALIDATION_CONFIG: 3006, // 配置数据无效
VALIDATION_MISSING_REQUIRED: 3007, // 缺少必需字段
VALIDATION_TYPE_MISMATCH: 3008, // 数据类型不匹配
```
#### 4. 业务逻辑错误4000-4999
```javascript
BUSINESS_ROOM_CLOSED: 4001, // 房间已关闭
BUSINESS_PLAYER_OFFLINE: 4002, // 玩家离线
BUSINESS_INSUFFICIENT_PERMISSION: 4003, // 权限不足
BUSINESS_DUPLICATE_ACTION: 4004, // 重复操作
BUSINESS_INVALID_STATE: 4005, // 无效状态
BUSINESS_RESOURCE_UNAVAILABLE: 4006, // 资源不可用
```
#### 5. 网络通信错误5000-5999
```javascript
NETWORK_CONNECTION_LOST: 5001, // 连接丢失
NETWORK_TIMEOUT: 5002, // 网络超时
NETWORK_INVALID_RESPONSE: 5003, // 无效响应
NETWORK_SERVER_ERROR: 5004, // 服务器错误
```
#### 6. 系统错误9000-9999
```javascript
SYSTEM_UNKNOWN: 9001, // 未知系统错误
SYSTEM_TIMEOUT: 9002, // 系统超时
SYSTEM_MEMORY_ERROR: 9003, // 内存错误
SYSTEM_CONFIGURATION_ERROR: 9004, // 配置错误
SYSTEM_INITIALIZATION_FAILED: 9005, // 初始化失败
SYSTEM_DEPENDENCY_ERROR: 9006 // 依赖错误
```
### 错误严重级别
```javascript
ERROR_LEVELS: {
FATAL: 'FATAL', // 致命错误,系统无法继续运行
ERROR: 'ERROR', // 错误,功能无法正常执行
WARN: 'WARN', // 警告,可能影响功能
INFO: 'INFO' // 信息,仅用于记录
}
```
### 核心API
#### 1. 创建错误对象
```javascript
/**
* 创建错误对象
* @param {number} code - 错误代码
* @param {string} message - 自定义错误消息(可选)
* @param {Object} context - 错误上下文信息(可选)
* @param {string} level - 错误级别(可选)
* @returns {Object} 标准化错误对象
*/
ErrorHandler.createError(code, message, context, level)
// 使用示例
var error = ErrorHandler.createError(
ErrorHandler.ERROR_CODES.GAME_INVALID_ACTION,
'当前阶段不允许出牌',
{
playerId: 'player001',
currentPhase: 'WAITING',
expectedPhase: 'PLAYING'
}
);
// 返回的错误对象
{
code: 2001,
message: '当前阶段不允许出牌',
level: 'ERROR',
timestamp: 1697376000000,
context: {
playerId: 'player001',
currentPhase: 'WAITING',
expectedPhase: 'PLAYING'
},
stack: '...堆栈信息...',
module: 'JinXianMahjong',
version: '1.0.0',
category: 'GAME_LOGIC'
}
```
#### 2. 安全执行函数
```javascript
/**
* 包装函数执行,自动捕获错误
* @param {Function} func - 要执行的函数
* @param {Object} context - 执行上下文
* @param {Array} args - 函数参数
* @returns {Object} 执行结果 {success: boolean, result: any, error: Object}
*/
ErrorHandler.safeExecute(func, context, args)
// 使用示例
var result = ErrorHandler.safeExecute(
function() {
return this.performRiskyOperation();
},
gameController,
[]
);
if (result.success) {
console.log('操作成功:', result.result);
} else {
console.error('操作失败:', result.error);
}
```
#### 3. 格式化错误信息
```javascript
/**
* 格式化错误信息用于显示
* @param {Object} error - 错误对象
* @returns {string} 格式化后的错误信息
*/
ErrorHandler.formatErrorMessage(error)
// 使用示例
var error = ErrorHandler.createError(2001, '无效操作');
var message = ErrorHandler.formatErrorMessage(error);
// 输出: "[ERROR:2001] 无效操作"
```
#### 4. 错误恢复
```javascript
/**
* 尝试从错误中恢复
* @param {Object} error - 错误对象
* @param {Function} recoveryFunc - 恢复函数
* @returns {boolean} 是否成功恢复
*/
ErrorHandler.tryRecover(error, recoveryFunc)
// 使用示例
var recovered = ErrorHandler.tryRecover(error, function() {
// 尝试恢复逻辑
return game.resetToLastValidState();
});
if (recovered) {
Logger.info('ErrorHandler', '已成功从错误中恢复');
} else {
Logger.error('ErrorHandler', '无法从错误中恢复');
}
```
#### 5. 错误统计
```javascript
/**
* 获取错误统计信息
* @returns {Object} 统计对象
*/
ErrorHandler.getStatistics()
// 返回示例
{
total: 45,
byCode: {
2001: 12,
3001: 8,
4001: 5
},
byLevel: {
FATAL: 1,
ERROR: 25,
WARN: 15,
INFO: 4
},
recent: [...] // 最近100条错误
}
```
### 实际使用场景
#### 场景1接口参数验证
```javascript
// export.js - Export.makewar()
export.makewar = function(room, roomtype) {
try {
// 参数验证
if (!room) {
throw ErrorHandler.createError(
ErrorHandler.ERROR_CODES.INTERFACE_INVALID_PARAMS,
'room参数不能为空',
{ functionName: 'makewar' }
);
}
if (!roomtype) {
throw ErrorHandler.createError(
ErrorHandler.ERROR_CODES.INTERFACE_INVALID_PARAMS,
'roomtype参数不能为空',
{ functionName: 'makewar' }
);
}
// 验证roomtype格式
var validation = RoomConfigUtils.validate(roomtype);
if (!validation.isValid) {
throw ErrorHandler.createError(
ErrorHandler.ERROR_CODES.VALIDATION_ROOMTYPE,
'roomtype格式错误: ' + validation.errors.join(', '),
{ roomtype: roomtype, errors: validation.errors }
);
}
// 创建游戏
Logger.info('Export', '开始创建游戏');
var gameState = GameStateManager.createGameState(
room.roomcode,
roomtype,
room.getPlayerIds()
);
return { success: true, gameState: gameState };
} catch (error) {
Logger.error('Export', 'makewar失败', {
error: error.message,
code: error.code
});
return {
success: false,
error: ErrorHandler.formatErrorMessage(error)
};
}
};
```
#### 场景2游戏逻辑错误处理
```javascript
// OperationManager.js
OperationManager.prototype.validateDiscard = function(playerId, tileCode) {
// 检查游戏是否开始
if (this.gameState.phase !== 'PLAYING') {
return ErrorHandler.createError(
ErrorHandler.ERROR_CODES.GAME_WRONG_PHASE,
'游戏未在进行中',
{
currentPhase: this.gameState.phase,
expectedPhase: 'PLAYING'
}
);
}
// 检查是否轮到该玩家
if (this.gameState.currentPlayerIndex !== this._getPlayerIndex(playerId)) {
return ErrorHandler.createError(
ErrorHandler.ERROR_CODES.GAME_INVALID_ACTION,
'不是该玩家的回合',
{
playerId: playerId,
currentPlayer: this.gameState.players[this.gameState.currentPlayerIndex].id
}
);
}
// 检查玩家是否持有该牌
var player = this._getPlayer(playerId);
if (!this._hasCard(player.handTiles, tileCode)) {
return ErrorHandler.createError(
ErrorHandler.ERROR_CODES.GAME_INVALID_CARD,
'玩家手中没有该牌',
{
playerId: playerId,
tileCode: tileCode,
handTiles: player.handTiles.map(function(t) { return t.code; })
}
);
}
// 验证通过
return null;
};
// 使用验证结果
OperationManager.prototype.performDiscard = function(playerId, tileCode) {
var validationError = this.validateDiscard(playerId, tileCode);
if (validationError) {
Logger.warn('OperationManager', '出牌验证失败', {
error: validationError.message,
code: validationError.code
});
return {
success: false,
error: validationError
};
}
// 执行出牌操作
// ...
return { success: true };
};
```
#### 场景3安全执行包装
```javascript
// GameController.js
GameController.prototype.processPlayerAction = function(playerId, action) {
Logger.info('GameController', '处理玩家操作', {
playerId: playerId,
action: action.type
});
// 使用安全执行包装
var result = ErrorHandler.safeExecute(
function() {
// 可能抛出异常的代码
return this._executeAction(playerId, action);
},
this,
[]
);
if (!result.success) {
// 操作失败,记录错误
Logger.error('GameController', '操作执行失败', {
playerId: playerId,
action: action.type,
error: result.error.message
});
// 尝试恢复
var recovered = ErrorHandler.tryRecover(result.error, function() {
// 恢复到上一个有效状态
return this._rollbackToLastState();
}.bind(this));
if (recovered) {
Logger.info('GameController', '已成功从错误中恢复');
}
return {
success: false,
error: ErrorHandler.formatErrorMessage(result.error)
};
}
Logger.debug('GameController', '操作执行成功', {
playerId: playerId,
result: result.result
});
return {
success: true,
result: result.result
};
};
```
---
## 🛠️ shared/utils - 共享工具类
### ArrayUtils.js - 数组操作工具
**功能**:提供数组操作的辅助方法
**常用方法**
```javascript
// 数组去重
ArrayUtils.unique(array)
// 数组打乱(洗牌)
ArrayUtils.shuffle(array)
// 数组查找
ArrayUtils.findIndex(array, predicate)
// 数组分组
ArrayUtils.groupBy(array, keyFunc)
```
### CardSourceInfoHelper.js - 牌源信息辅助
**功能**:管理麻将牌的来源信息
**主要方法**
```javascript
// 创建牌源信息
CardSourceInfoHelper.create(source, round, turn)
// 验证牌源信息
CardSourceInfoHelper.validate(sourceInfo)
```
### GameContextHelper.js - 游戏上下文辅助
**功能**:管理游戏上下文信息
**主要方法**
```javascript
// 创建游戏上下文
GameContextHelper.create(roomcode, round, phase)
// 更新上下文
GameContextHelper.update(context, updates)
```
### GameStateHelper.js - 游戏状态辅助
**功能**:游戏状态的辅助操作
**主要方法**
```javascript
// 验证游戏状态
GameStateHelper.validate(gameState)
// 克隆游戏状态
GameStateHelper.clone(gameState)
// 比较游戏状态
GameStateHelper.compare(state1, state2)
```
### MahjongCardUniqueId.js - 麻将牌唯一ID
**功能**:生成和管理麻将牌的唯一标识
**主要方法**
```javascript
// 生成唯一ID10-145
MahjongCardUniqueId.generate(code, index)
// 从唯一ID获取牌码
MahjongCardUniqueId.getCode(uniqueId)
// 验证唯一ID
MahjongCardUniqueId.validate(uniqueId)
```
### RoomConfigUtils.js - 房间配置工具
**功能**:房间配置的解析和验证(详见 [06-规则配置系统.md](../core/06-规则配置系统.md)
---
## 💡 使用最佳实践
### 1. 日志级别选择
```javascript
// ✓ 推荐:根据环境设置日志级别
if (process.env.NODE_ENV === 'production') {
Logger.setLevel('WARN'); // 生产环境只记录警告和错误
} else {
Logger.setLevel('DEBUG'); // 开发环境记录调试信息
}
// ✓ 推荐:使用合适的日志级别
Logger.trace('Module', '详细跟踪信息'); // 非常详细,仅开发调试用
Logger.debug('Module', '调试信息'); // 开发时使用
Logger.info('Module', '正常运行信息'); // 关键流程节点
Logger.warn('Module', '警告信息'); // 可能的问题
Logger.error('Module', '错误信息'); // 功能异常
Logger.fatal('Module', '致命错误'); // 系统无法继续
// ✗ 不推荐:滥用日志级别
Logger.error('Module', '玩家进入房间'); // 这应该是INFO级别
Logger.info('Module', '系统崩溃'); // 这应该是FATAL级别
```
### 2. 日志标签规范
```javascript
// ✓ 推荐:使用清晰的标签
Logger.info('GameController', '游戏开始');
Logger.debug('MahjongService', '发牌完成');
Logger.warn('OperationManager', '操作超时');
// ✗ 不推荐:标签不清晰
Logger.info('GC', 'start'); // 太简短
Logger.info('游戏', '开始'); // 使用中文不便于过滤
```
### 3. 附加数据使用
```javascript
// ✓ 推荐:提供有用的上下文信息
Logger.error('RpcHandler', 'RPC调用失败', {
method: 'player_discard',
playerId: 'player001',
errorCode: 2001,
timestamp: Date.now()
});
// ✗ 不推荐:数据过多或无用
Logger.info('Module', '操作', {
// 包含整个gameState对象太大
gameState: this.gameState
});
```
### 4. 性能考虑
```javascript
// ✓ 推荐:仅在需要时记录日志
if (Logger.config.currentLevel <= Logger.LOG_LEVELS.DEBUG) {
var expensiveData = this._calculateExpensiveData();
Logger.debug('Module', '详细数据', expensiveData);
}
// ✗ 不推荐:总是计算数据
var expensiveData = this._calculateExpensiveData();
Logger.debug('Module', '详细数据', expensiveData); // DEBUG被禁用时也会计算
```
### 5. 错误处理策略
```javascript
// ✓ 推荐:创建标准化错误
throw ErrorHandler.createError(
ErrorHandler.ERROR_CODES.GAME_INVALID_ACTION,
'具体的错误描述',
{ 有用的上下文信息 }
);
// ✓ 推荐:使用安全执行
var result = ErrorHandler.safeExecute(riskyFunction, context, args);
if (!result.success) {
// 处理错误
}
// ✗ 不推荐:直接抛出字符串
throw '出错了'; // 没有错误码,无法分类
// ✗ 不推荐:忽略错误
try {
riskyOperation();
} catch (error) {
// 什么都不做,错误被吞噬
}
```
### 6. 日志和错误配合使用
```javascript
// ✓ 推荐:记录错误日志
try {
performOperation();
} catch (error) {
var standardError = ErrorHandler.createError(
ErrorHandler.ERROR_CODES.GAME_INVALID_ACTION,
error.message,
{ operation: 'performOperation' }
);
Logger.error('Module', '操作失败', {
error: standardError.message,
code: standardError.code
});
throw standardError;
}
// ✗ 不推荐:只记录日志或只抛出错误
try {
performOperation();
} catch (error) {
Logger.error('Module', error.message); // 只记录,不抛出
// 或
throw error; // 只抛出,不记录
}
```
---
## 🔍 调试技巧
### 1. 启用详细日志
```javascript
// 临时启用TRACE级别查看所有日志
Logger.setLevel('TRACE');
// 执行需要调试的代码
someFunction();
// 恢复原来的级别
Logger.setLevel('INFO');
```
### 2. 使用日志分组
```javascript
// 使用分组整理相关日志
Logger.group('===== 玩家操作处理 =====');
Logger.info('Operation', '验证玩家');
Logger.debug('Operation', '检查游戏状态');
Logger.debug('Operation', '执行操作');
Logger.info('Operation', '操作完成');
Logger.groupEnd();
```
### 3. 性能分析
```javascript
// 使用计时功能分析性能
Logger.time('完整游戏流程');
Logger.time('初始化');
initialize();
Logger.timeEnd('初始化');
Logger.time('发牌');
dealTiles();
Logger.timeEnd('发牌');
Logger.time('游戏进行');
playGame();
Logger.timeEnd('游戏进行');
Logger.timeEnd('完整游戏流程');
```
### 4. 查看错误历史
```javascript
// 获取最近的错误
var errors = ErrorHandler.getStatistics().recent;
Logger.table(errors);
// 分析错误分布
var stats = ErrorHandler.getStatistics();
Logger.info('ErrorStats', '错误统计', {
total: stats.total,
byLevel: stats.byLevel,
topErrors: Object.keys(stats.byCode)
.sort(function(a, b) {
return stats.byCode[b] - stats.byCode[a];
})
.slice(0, 5)
});
```
### 5. 日志过滤
```javascript
// 只查看特定级别的日志
var errorLogs = Logger.getHistory(null, Logger.LOG_LEVELS.ERROR);
var warnLogs = Logger.getHistory(null, Logger.LOG_LEVELS.WARN);
// 只查看最近的日志
var recent = Logger.getHistory(50); // 最近50条
// 自定义过滤
var gameLogs = Logger.getHistory().filter(function(log) {
return log.tag.indexOf('Game') !== -1;
});
```
---
## 📚 完整使用示例
### 示例1模块初始化
```javascript
// 模块入口文件
(function() {
'use strict';
// 配置Logger
Logger.configure({
currentLevel: Logger.LOG_LEVELS.DEBUG,
enableTimestamp: true,
enableColors: true,
moduleName: 'JinXianMahjong'
});
Logger.info('System', '======= 进贤麻将系统启动 =======');
Logger.info('System', '版本:', '1.0.0');
Logger.info('System', '环境:', process.env.NODE_ENV || 'development');
// 初始化系统
try {
Logger.group('系统初始化');
Logger.time('加载配置');
var config = loadConfiguration();
Logger.timeEnd('加载配置');
Logger.debug('System', '配置加载完成', config);
Logger.time('初始化数据库');
initializeDatabase();
Logger.timeEnd('初始化数据库');
Logger.info('System', '数据库初始化完成');
Logger.time('注册游戏模块');
registerGameModule();
Logger.timeEnd('注册游戏模块');
Logger.info('System', '游戏模块注册完成');
Logger.groupEnd();
Logger.info('System', '======= 系统启动完成 =======');
} catch (error) {
var systemError = ErrorHandler.createError(
ErrorHandler.ERROR_CODES.SYSTEM_INITIALIZATION_FAILED,
'系统初始化失败: ' + error.message,
{ error: error.stack },
ErrorHandler.ERROR_LEVELS.FATAL
);
Logger.fatal('System', '系统启动失败', {
error: systemError.message,
code: systemError.code
});
process.exit(1);
}
})();
```
### 示例2完整的操作流程
```javascript
// 玩家出牌操作的完整流程
function handlePlayerDiscard(playerId, tileCode) {
Logger.info('DiscardHandler', '======= 处理玩家出牌 =======', {
playerId: playerId,
tileCode: tileCode
});
try {
Logger.group('出牌验证');
// 1. 参数验证
Logger.debug('Validation', '验证参数');
if (!playerId || !tileCode) {
throw ErrorHandler.createError(
ErrorHandler.ERROR_CODES.INTERFACE_INVALID_PARAMS,
'出牌参数无效',
{ playerId: playerId, tileCode: tileCode }
);
}
// 2. 玩家验证
Logger.debug('Validation', '验证玩家');
var player = getPlayer(playerId);
if (!player) {
throw ErrorHandler.createError(
ErrorHandler.ERROR_CODES.INTERFACE_PLAYER_NOT_FOUND,
'玩家不存在',
{ playerId: playerId }
);
}
// 3. 游戏状态验证
Logger.debug('Validation', '验证游戏状态');
var gameState = getGameState();
if (gameState.phase !== 'PLAYING') {
throw ErrorHandler.createError(
ErrorHandler.ERROR_CODES.GAME_WRONG_PHASE,
'游戏未在进行中',
{ currentPhase: gameState.phase }
);
}
// 4. 回合验证
Logger.debug('Validation', '验证玩家回合');
if (!isPlayerTurn(playerId)) {
throw ErrorHandler.createError(
ErrorHandler.ERROR_CODES.GAME_INVALID_ACTION,
'不是该玩家的回合',
{ playerId: playerId, currentPlayer: getCurrentPlayer() }
);
}
Logger.groupEnd();
Logger.info('Validation', '验证通过');
// 5. 执行出牌
Logger.group('执行出牌');
Logger.time('出牌操作');
var result = ErrorHandler.safeExecute(
function() {
return performDiscard(player, tileCode);
},
this,
[]
);
Logger.timeEnd('出牌操作');
if (!result.success) {
throw result.error;
}
Logger.info('Discard', '出牌成功', {
playerId: playerId,
tileCode: tileCode,
discardedTile: result.result
});
Logger.groupEnd();
// 6. 检查其他玩家的响应
Logger.group('检查玩家响应');
var responses = checkPlayerResponses(tileCode);
Logger.debug('Response', '玩家响应', responses);
Logger.groupEnd();
// 7. 广播消息
Logger.debug('Broadcast', '广播出牌消息');
broadcastDiscard(playerId, tileCode, responses);
Logger.info('DiscardHandler', '======= 出牌处理完成 =======');
return {
success: true,
result: {
playerId: playerId,
tile: tileCode,
responses: responses
}
};
} catch (error) {
Logger.error('DiscardHandler', '出牌处理失败', {
error: error.message,
code: error.code,
playerId: playerId,
tileCode: tileCode
});
// 记录错误统计
var stats = ErrorHandler.getStatistics();
Logger.warn('DiscardHandler', '当前错误统计', {
total: stats.total,
recent: stats.recent.length
});
return {
success: false,
error: ErrorHandler.formatErrorMessage(error)
};
}
}
```
---
## 🔗 相关文档链接
- **上一篇**[06-规则配置系统.md](../core/06-规则配置系统.md) - 规则配置解析
- **下一篇**[08-游戏流程概述.md](../architecture/08-游戏流程概述.md) - 完整游戏流程
- **参考**
- [03-RPC处理机制.md](../framework/03-RPC处理机制.md) - RPC错误处理
- [04-游戏核心服务.md](../core/04-游戏核心服务.md) - 服务层日志使用
---
## 📝 附录
### A. Logger配置选项完整列表
```javascript
{
currentLevel: 0-5, // 当前日志级别
enableConsole: true/false, // 启用控制台输出
enableColors: true/false, // 启用颜色
enableTimestamp: true/false, // 启用时间戳
enableModuleName: true/false, // 启用模块名
enableStackTrace: true/false, // 启用堆栈跟踪
timestampFormat: 'ISO'/'LOCAL'/'TIMESTAMP', // 时间戳格式
moduleName: 'string', // 模块名
maxLogHistory: number, // 最大历史记录数
flushInterval: number // 刷新间隔(毫秒)
}
```
### B. 错误代码快速查询
| 范围 | 类别 | 说明 |
|------|------|------|
| 1000-1999 | 框架接口错误 | Export/Import接口相关 |
| 2000-2999 | 游戏逻辑错误 | 游戏规则和操作相关 |
| 3000-3999 | 数据验证错误 | 参数和数据格式验证 |
| 4000-4999 | 业务逻辑错误 | 业务规则和状态相关 |
| 5000-5999 | 网络通信错误 | 网络连接和通信相关 |
| 9000-9999 | 系统错误 | 系统级别的严重错误 |
### C. 常用日志模式
```javascript
// 1. 方法入口/出口模式
function someMethod(param) {
Logger.debug('Module', 'someMethod 入口', { param: param });
try {
// 方法逻辑
var result = doSomething(param);
Logger.debug('Module', 'someMethod 出口', { result: result });
return result;
} catch (error) {
Logger.error('Module', 'someMethod 异常', { error: error.message });
throw error;
}
}
// 2. 状态变更模式
function changeState(newState) {
Logger.info('StateManager', '状态变更', {
from: this.currentState,
to: newState
});
this.currentState = newState;
}
// 3. 性能监控模式
function performanceMonitor(operation) {
Logger.time(operation.name);
var result = operation.execute();
Logger.timeEnd(operation.name);
return result;
}
```
---
**文档版本**v1.0
**最后更新**2025年10月15日
**维护者**:进贤麻将开发团队