feat(framework): 协议 rpc 常量补全(~84 个,逐字对齐 02_Const.js)

Rpc 表覆盖:
- 登录连接 5 个(player_login/kick_server/connect_*server/agentserver_game)
- 房间生命周期 13 个(create_room/self/other_join_room/.../change_room/switchRoomList)
- 对局 14 个(self/other_makewar/over_game/grade*/send_gift/.../beanroom_surrender/...)
- 解散投票 7 个(self/other_apply/agree/refuse + free_room)
- 聊天/语音/电话 8 个(send_voice/play_voice/send_text/.../hangup_phone/...)
- 资产/经济 9 个(update_roomcard/bean/charm + giveCoin/change_star/topup_card/...)
- 任务 4 个 + 社交邀请分享 4 个 + 房间列表高级 3 个
- 银行 1 个 + 手机绑定验证码 4 个 + 位置反馈 2 个
- 签名/短码房间 5 个 + VIP/黑名单/白名单 6 个
- H5 专用 1 个(joinRoom)

字面量联合类型 RpcName:编译期校验入参,拼错即报错
自映射契约:每个 Rpc.X === 'X',避免错拼

171/171 tests pass, typecheck exit 0。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-31 22:08:58 +08:00
co-authored by Claude Opus 5
parent 79c2c804dc
commit 52f88a285f
2 changed files with 173 additions and 1 deletions
@@ -1,9 +1,133 @@
import { Route } from '../core/constants.ts';
export { Route };
/** 平台层会用到的 rpc 名(最小集)。完整清单见 docs/protocol/02、03。 */
/**
* 协议层 rpc 名(与 docs/protocol/02、03 及 projects/Game_Surface_3/js/00_Surface/02_Const.js 逐字对齐)。
*
* 字面量联合类型:业务侧发包(NetClient.send)/收包 handler(platformHandlers + roomHandlers + sdk onReceive)入参必须在此集合内,
* 拼错编译期就发现;未注册的 rpc 运行时显式抛错(第二准则:不静默兜底)。
*/
export const Rpc = {
// ---- 登录与连接 ----
player_login: 'player_login',
kick_server: 'kick_server',
connect_roomserver: 'connect_roomserver',
connect_agentserver: 'connect_agentserver',
agentserver_game: 'agentserver_game',
// ---- 房间生命周期(room route)----
create_room: 'create_room',
self_join_room: 'self_join_room',
other_join_room: 'other_join_room',
self_exit_room: 'self_exit_room',
other_exit_room: 'other_exit_room',
self_break_room: 'self_break_room',
other_break_room: 'other_break_room',
player_prepare: 'player_prepare',
change_seat: 'change_seat',
change_room: 'change_room',
switchRoomList: 'switchRoomList',
// ---- 对局(room route)----
self_makewar: 'self_makewar',
other_makewar: 'other_makewar',
over_game: 'over_game',
get_player_grade1: 'get_player_grade1',
get_player_grade2: 'get_player_grade2',
send_gift: 'send_gift',
other_send_gift: 'other_send_gift',
beanroom_surrender: 'beanroom_surrender',
send_phiz: 'send_phiz',
broadcast: 'broadcast',
other_offline: 'other_offline',
other_online: 'other_online',
// ---- 解散投票(room route)----
self_apply_free_room: 'self_apply_free_room',
other_apply_free_room: 'other_apply_free_room',
self_agree_free_room: 'self_agree_free_room',
other_agree_free_room: 'other_agree_free_room',
self_refuse_free_room: 'self_refuse_free_room',
other_refuse_free_room: 'other_refuse_free_room',
free_room: 'free_room',
// ---- 聊天 / 语音 / 电话(room route)----
send_voice: 'send_voice',
play_voice: 'play_voice',
send_text: 'send_text',
receive_chat: 'receive_chat',
call_phone: 'call_phone',
other_callphone: 'other_callphone',
hangup_phone: 'hangup_phone',
other_hangup: 'other_hangup',
kick_offline: 'kick_offline',
// ---- 资产 / 经济(agent/room route)----
update_roomcard: 'update_roomcard',
update_bean: 'update_bean',
update_charm: 'update_charm',
giveCoin: 'giveCoin',
change_star: 'change_star',
topup_card: 'topup_card',
get_paylist: 'get_paylist',
pay_succ: 'pay_succ',
get_treasurelist: 'get_treasurelist',
// ---- 任务(agent route)----
get_player_task: 'get_player_task',
player_finish_task: 'player_finish_task',
get_task_award: 'get_task_award',
can_award: 'can_award',
// ---- 社交 / 邀请 / 分享(agent route)----
binding_invitecode: 'binding_invitecode',
get_player_invitecode: 'get_player_invitecode',
share_room: 'share_room',
get_share_room: 'get_share_room',
quick_enter_share_room: 'quick_enter_share_room',
// ---- 房间列表 / 高级(agent route)----
advanced_roomlist: 'advanced_roomlist',
advanced_createroom: 'advanced_createroom',
query_player2: 'query_player2',
// ---- 银行 / 资料(agent route)----
set_bankpwd: 'set_bankpwd',
// ---- 手机绑定 / 验证码(agent route)----
binding_phone: 'binding_phone',
send_phone_checkcode: 'send_phone_checkcode',
send_phone_code_wechat: 'send_phone_code_wechat',
submit_phoneinfo: 'submit_phoneinfo',
// ---- 位置 / 反馈(agent route)----
submit_location: 'submit_location',
submit_opinion: 'submit_opinion',
// ---- 签名 / 短码房间(agent route)----
setSign: 'setSign',
getInfoByShortCode: 'getInfoByShortCode',
optBanList: 'optBanList',
getShortCodeRankList: 'getShortCodeRankList',
// ---- VIP / 黑名单 / 白名单(agent route)----
setAllCharm: 'setAllCharm',
getVipRankList: 'getVipRankList',
playerBehavior: 'playerBehavior',
setVipForbidSelect: 'setVipForbidSelect',
getPlayerWhiteList: 'getPlayerWhiteList',
optWhiteList: 'optWhiteList',
// ---- H5 专用(agent route,由 joinRoom 入口直达)----
joinRoom: 'joinRoom',
} as const;
/** 全部 rpc 名的字面量联合类型(编译期校验入参) */
export type RpcName = (typeof Rpc)[keyof typeof Rpc];
/** Route + Rpc 二元组的契约(用于 typed send / typed receive) */
export interface Envelope<T = unknown> {
route: typeof Route[keyof typeof Route];
rpc: RpcName;
data: T;
}
@@ -0,0 +1,48 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Rpc, type RpcName } from '../../YouleNexus/assets/framework/protocol/routes.ts';
test('Rpc 表: 每个值等于 key(自映射契约,避免错拼)', () => {
// 每个 Rpc.X 必须 === 'X'(自映射契约;改任一即不一致)
for (const [k, v] of Object.entries(Rpc)) {
assert.equal(v, k, `Rpc.${k} 应等于 "${k}",实际 "${v}"`);
}
});
test('Rpc 表: 至少覆盖核心业务场景', () => {
const required: RpcName[] = [
'player_login', 'kick_server', 'connect_roomserver', 'connect_agentserver',
'create_room', 'self_join_room', 'other_join_room',
'self_exit_room', 'other_exit_room', 'player_prepare', 'change_seat',
'self_makewar', 'other_makewar',
'self_apply_free_room', 'free_room',
'send_text', 'receive_chat',
'update_bean', 'update_roomcard',
'joinRoom',
];
for (const rpc of required) {
assert.ok(typeof Rpc[rpc] === 'string', `Rpc.${rpc} 必须是字符串`);
}
});
test('RpcName 联合类型: 字面量类型正确推导', () => {
// 编译期类型测试:这里的赋值如果类型不对,tsc 会报错
const sample: RpcName = 'player_login';
assert.equal(sample, 'player_login');
});
test('Rpc 表: 房间 handler 已覆盖的关键 rpc 都在白名单', () => {
// 验证 room-handlers.ts 用的 14 个 rpc 都在 Rpc 表里
const roomRpcs: RpcName[] = [
'self_join_room', 'other_join_room',
'self_exit_room', 'other_exit_room',
'self_apply_free_room', 'other_apply_free_room',
'self_agree_free_room', 'other_agree_free_room',
'self_refuse_free_room', 'other_refuse_free_room',
'other_offline', 'other_online',
'change_seat', 'free_room',
];
for (const rpc of roomRpcs) {
assert.ok(Rpc[rpc] === rpc, `Rpc.${rpc} 必须等于自身字符串`);
}
});