24小时监控玩家在线状态,每小时记录一次活跃数据。每个账户、每个游戏、每个角色每小时最多生成一条记录。
玩家A从1点在线到3点,实际产生3条数据(1点、2点、3点各一条)。
| 对比项 | 客户端每小时上报 | 心跳 + 服务端聚合 |
|---|---|---|
| iOS 可靠性 | ⚠️ 后台定时器不可靠 | ⚠️ 同样依赖前台定时器 |
| H5 可靠性 | ⚠️ 切后台暂停JS | ⚠️ 同样依赖页面可见 |
| 安卓兼容性 | ⚠️ 部分厂商杀后台 | ⚠️ 同样存在此问题 |
| 实现复杂度 | 低 | 中(多一层Redis + Cron) |
| 接口调用量 | 低(1次/小时) | 中(12次/小时) |
| 扩展性 | 仅统计活跃 | 可扩展实时在线、掉线检测 |
两种方案都依赖客户端定时器,切后台/被杀后定时器都会暂停,回到前台都会重新启动。核心区别在于漏报窗口:心跳5分钟 vs 每小时上报60分钟。
对于"每小时记录一次活跃"的需求,客户端每小时上报简单直接,够用。
采用方案:客户端每小时上报。
POST /api/v2/User/playerActive| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| member_id | int | 是 | 玩家账号ID |
| game_id | int | 是 | 游戏ID |
| channel_id | int | 是 | 渠道ID(推广员) |
| server_id | int | 是 | 区服ID |
| server_name | string | 是 | 区服名 |
| role_id | int | 是 | 角色ID |
| role_name | string | 是 | 角色名 |
{
"code": 200,
"msg": "上报成功",
"data": []
}
cy_members)获取 member_create_timecy_role_info)获取 role_create_timechannel_id(推广员,level=3)查询渠道层级关系,获取子会长、公会账号、商务账号game_id 查询游戏名member_id + game_id + role_id + 小时 是否已存在记录cy_player_active_logCREATE TABLE `cy_player_active_log` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`member_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '玩家账号ID',
`subaccount_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '子账户ID',
`game_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '游戏ID',
`game_name` varchar(100) NOT NULL DEFAULT '' COMMENT '游戏名',
`channel_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '渠道ID(推广员)',
`server_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '区服ID',
`server_name` varchar(100) NOT NULL DEFAULT '' COMMENT '区服名',
`role_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '角色ID',
`role_name` varchar(100) NOT NULL DEFAULT '' COMMENT '角色名',
`role_create_time` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '角色创建时间(时间戳)',
`member_create_time` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '账户注册时间(时间戳)',
`promoter_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '推广员ID',
`sub_president_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '子会长ID',
`guild_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '公会账号ID(会长)',
`business_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '商务账号ID',
`create_time` datetime NOT NULL COMMENT '活跃时间(例如:2026-06-30 01:00:00)',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_active` (`member_id`, `game_id`, `role_id`, `create_time`),
KEY `idx_game` (`game_id`, `create_time`),
KEY `idx_channel` (`channel_id`, `create_time`),
KEY `idx_guild` (`guild_id`, `create_time`),
KEY `idx_business` (`business_id`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='玩家活跃记录';
| 字段 | 说明 |
|---|---|
member_id |
来自请求参数,关联 cy_members 表 |
subaccount_id |
来自请求参数或玩家信息 |
game_id |
来自请求参数 |
game_name |
通过 game_id 查询 cy_game 表获取 |
channel_id |
来自请求参数,即推广员ID(level=3) |
promoter_id |
等于 channel_id |
sub_president_id |
通过 channel.parent_id 向上查 level=2 的渠道 |
guild_id |
通过 channel.parent_id 向上查 level=1 的渠道 |
business_id |
通过 channel.parent_id 向上查 level=0 的渠道 |
create_time |
服务端当前时间,取整到小时(如 2026-06-30 01:00:00) |
渠道表 nw_channel 通过 id_path 或 parent_id 维护四级树形结构:
商务(level=0) → 公会/会长(level=1) → 子会长(level=2) → 推广员(level=3)
id_path 示例:,10,20,300,500,,一次查询即可拿到整条链路。
客户端(每小时定时器)
│
▼
POST /api/v2/User/playerActive
│
├─ 查询 cy_members → member_create_time
├─ 查询 cy_role_info → role_create_time
├─ 查询 cy_game → game_name
├─ 查询 nw_channel(向上查层级) → 子会长、公会、商务
├─ 去重检查(uk_active唯一索引)
│
▼
写入 cy_player_active_log
uk_active 唯一索引保证同一玩家+游戏+角色+小时只有一条记录,重复上报用 INSERT IGNORE 或 ON DUPLICATE KEYcreate_time 使用服务端时间,不依赖客户端时间nw_channel 表guild_id 索引直接查,无需再关联渠道表