Files
mini-zmc/docs/TECHNICAL_DESIGN.md
2026-07-29 10:50:33 +08:00

230 lines
5.5 KiB
Markdown
Raw 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.

# 技术设计
## 1. 当前原型架构
当前版本采用微信小程序原生 JavaScript不依赖 npm 包和远程服务。
```text
页面层 pages/*
本地游戏服务 services/game-service.js
wx Storage房间、玩家、事件和结果
```
`game-service.js` 将页面与数据源隔离。接入真实后端时,页面调用接口可以尽量保持不变,只需将服务实现替换为网络请求和实时订阅。
## 2. 推荐生产架构
初期建议采用微信云开发,减少登录、部署和运维成本;验证规模后可迁移为自有服务。
```text
微信小程序
├─ HTTPS API创建、加入、抓捕、技能、结算
├─ WebSocket房间状态和事件推送
└─ 定位模块:低频位置上报与边界判断
应用服务
├─ 身份与权限
├─ 房间状态机
├─ 游戏规则引擎
├─ 位置模糊化
├─ 口令与防作弊
└─ 安全事件
数据层
├─ 持久数据库:用户、房间、结果
├─ Redis在线状态、口令、房间实时状态
└─ 短期位置存储:设置自动过期时间
```
## 3. 核心实体
### User
```js
{
id,
openId,
displayName,
avatarUrl,
safetySettings,
createdAt
}
```
### Room
```js
{
id,
code,
hostId,
name,
mode,
status,
settings: {
maxPlayers,
durationMinutes,
prepareMinutes,
clueIntervalMinutes,
seekerCount,
radiusMeters,
skillsEnabled
},
center,
phaseEndsAt,
version,
createdAt
}
```
### Player
```js
{
id,
roomId,
userId,
displayName,
role,
status,
ready,
caughtBy,
caughtAt,
joinedAt,
lastSeenAt
}
```
### GameEvent
```js
{
id,
roomId,
type,
actorId,
targetId,
visibility,
payload,
createdAt
}
```
### LocationPing
```js
{
roomId,
playerId,
latitude,
longitude,
accuracy,
capturedAt,
expiresAt
}
```
生产环境中 `LocationPing` 不应作为永久战绩保存。
## 4. API 草案
- `POST /rooms`:创建房间。
- `GET /rooms/:code`:读取可公开的房间摘要。
- `POST /rooms/:code/join`:加入房间。
- `POST /rooms/:id/ready`:切换准备状态。
- `POST /rooms/:id/start`:房主开始游戏。
- `POST /rooms/:id/location`:上报位置。
- `POST /rooms/:id/skills/:skill`:使用技能。
- `POST /rooms/:id/captures`:提交抓捕凭证。
- `POST /rooms/:id/pause`:暂停游戏。
- `POST /rooms/:id/assemble`:发起全员集合。
- `POST /rooms/:id/finish`:结束游戏。
- `GET /rooms/:id/result`:读取结算结果。
所有改变游戏状态的接口都应:
- 校验用户身份和房间成员关系。
- 校验当前房间状态和角色权限。
- 使用服务端时间。
- 支持幂等键,避免弱网重试造成重复事件。
- 更新房间版本号并广播事件。
## 5. 实时同步
WebSocket 消息建议仅发送必要状态变化:
```js
{
eventId,
roomId,
roomVersion,
type: "PLAYER_CAUGHT",
payload: {},
serverTime
}
```
客户端策略:
1. 进入候场或游戏页后建立连接。
2.`eventId` 去重。
3. 检测版本跳跃时重新拉取房间快照。
4. 退到后台后按微信能力选择保活或恢复时重连。
5. UI 倒计时基于 `phaseEndsAt - serverTimeOffset` 计算。
## 6. 位置和地理围栏
- 客户端获取坐标并附带定位精度和采集时间。
- 服务端校验坐标新鲜度、速度异常和精度阈值。
- 使用服务端保存的场地中心和半径判断越界。
- 向寻找者发送的线索由服务端完成网格化或随机偏移。
- 不将躲藏者原始坐标发送给其他普通玩家。
- 位置记录设置短期 TTL游戏结束后触发删除任务。
线索模糊化可采用:
- H3/Geohash 网格降级。
- 以真实点为中心生成受边界约束的随机圆。
- 仅返回目标与玩家的距离档位,如“很近、附近、较远”。
## 7. 抓捕防作弊
动态口令建议由服务端生成:
```text
token = HMAC(roomSecret, targetPlayerId + timeWindow + nonce)
```
- 口令窗口建议 30 秒。
- 提交时校验寻找者与目标的最近位置距离。
- 校验双方都在线、角色正确且目标尚未被抓。
- 使用事务写入抓捕结果,防止两个寻找者同时抓到同一目标。
- 口令只作为相遇证明之一,不能替代安全规则。
## 8. 安全设计
- 房主开始前必须完成场地和规则确认。
- 服务端保留暂停、集合和取消的高优先级事件通道。
- 客户端进入后台、定位失效、低电量或离线时产生状态提示。
- 精确位置访问纳入服务端审计,不允许普通运营后台随意查看。
- 公开组局上线前必须具备举报、封禁、内容安全和年龄策略。
## 9. 测试策略
- 单元测试:房间状态机、身份分配、倒计时、抓捕和结算。
- 属性测试:不同人数与寻找者数量下角色数量始终合法。
- 集成测试:多人并发加入、重复抓捕、断线重连和版本冲突。
- 真机测试:前后台切换、弱网、定位精度、耗电和不同微信版本。
- 户外测试:边界提醒时机、线索节奏、屏幕使用时间和安全流程。
## 10. 迁移顺序
1. 保留页面与服务接口,先把本地房间读写替换为云函数。
2. 接入微信登录并使每个玩家拥有稳定服务端 ID。
3. 引入实时房间事件和服务端倒计时。
4. 接入地图选区、定位上报和模糊线索。
5. 将抓捕、技能和结算全部迁到服务端裁决。
6. 增加日志、监控、隐私删除和运营安全能力。