公开字段语义与转换规则
字段目录解决“有哪些字段”的问题,本页解决“字段应该怎样解释”的问题。它只讨论公开客户端可以读取或提交的结构,不把内部状态码当成稳定协议。
三种成功状态
公开接口并不全部使用同一组顶层字段:
| 形式 | 判断方法 | 适用场景 |
|---|---|---|
success | success === true | 大多数读取、写入和文件操作 |
valid | valid === true | Token 验证 |
| HTTP + 业务组合 | HTTP 为 2xx 且业务字段成功 | 二进制文件和兼容客户端 |
只检查 HTTP 200 会把业务失败当成成功;只检查 success 又可能误判验证响应。客户端应按接口页面定义判断,并保留 msg。
可选、空和未知
可选字段
字段名带 ? 表示服务可以不返回它。缺少字段与 null 不必然等价:
- 缺少数组:按空数组展示,但保留“字段缺失”的兼容信息;
- 缺少对象:按空对象或不可用状态展示;
- 缺少数字:显示“暂无”,不要自动填 0,除非页面明确规定 0 的含义;
- 缺少文本:显示空状态,不要拼接成
undefined。
空数组
空数组通常表示当前范围没有数据,例如没有相片、没有评论、没有在线用户或没有地区统计。它不是网络失败,也不应触发无限重试。
未知字段和枚举
客户端必须允许服务增加字段和状态字符串。未知字段应在原始响应中保留,未知枚举可以显示原文或“未知状态”,不要因为不认识就删除整条记录。
数值单位
| 字段类别 | 原始单位/形式 | 页面显示建议 |
|---|---|---|
| 达成率 | 放大整数 | 除以 10000 后显示百分比,并保留原值 |
| 文件大小 | 字节 | 可换算为 KiB/MiB,但下载和校验使用原值 |
| 下载/安装限速 | MB/s | 明确显示单位,不与字节/秒混用 |
| 游玩/会话时长 | 数值和格式化文本并存 | 统计使用数值,展示可用格式化文本 |
| 时间戳 | Unix 秒或时间字符串 | 先确认来源,再指定时区格式化 |
| 统计百分比 | 可能已经是 0–100 | 先看字段说明,避免重复乘 100 |
达成率示例
function formatAchievement(raw: number): string {
return `${(raw / 10000).toFixed(4)}%`;
}显示层可以按需要减少小数位,但导入、导出和二次计算应保留原始整数。
Token 与账号模型
isMachine 是账号类型提示,不应被解释成“权限全部开放”。页面最终可见菜单和服务返回的权限才是有效边界。
推荐保存以下本地状态:
interface LocalSession {
displayName: string;
token: string;
isMachine: boolean;
expiresAt?: string;
}Token 只能在内存或受保护存储中使用。displayName 是本地标签,不能替换服务端账号名称,也不能作为接口身份提交。
配方模型
配方响应是由可见配置名映射到配置值的对象:
interface RuleValue {
enable: boolean;
value: string;
}
type RuleMap = Record<string, RuleValue>;value 统一按文本传输,即使页面看起来是数字、列表或 JSON。客户端应以当前字段的输入提示决定如何校验,而不是把所有字段都强制转换成数字。
修改流程:读取 → 选择已存在的可见键 → 只改变目标项 → 保存 → 重新读取。未知键和未编辑项必须保留。
备份模型
备份响应中的 data 是可演进对象。正确做法是保存完整对象:
interface BackupResult {
success: boolean;
msg: string;
data: unknown;
}不要:
- 只保存当前 UI 展示的四个字段;
- 在恢复前自动重命名未知键;
- 将 Token 写入备份对象;
- 把不同账号的备份合并后再恢复。
相片模型与二进制
相片列表是文件名数组。文件读取成功时可能不是 JSON,而是图片二进制;失败时才可能返回 JSON 错误封装。
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
const error = await response.json();
// 读取 error.msg
} else {
const blob = await response.blob();
// 创建临时 URL,使用完成后 revoke
}文件名必须来自服务返回的列表或用户确认,不应从未校验的路径拼接,以避免本地保存位置和显示名称混淆。
成绩模型
成绩数据通常把一首曲目的多个难度拆成行:
interface UserMusicRow {
musicId: number;
level: number;
playCount: number;
achievement: number;
comboStatus: number;
syncStatus: number;
deluxscoreMax: number;
scoreRank: number;
}level 是难度索引,不应直接当作网页上显示的难度名称;scoreRank、comboStatus 和 syncStatus 也是索引。显示名称和颜色由客户端映射表决定,未来增加索引时要提供未知状态兜底。
成绩互动模型中的 musicId 是关联键,不是评论 ID。评论的 id 只在评论接口中使用,回复使用 parentId 指向父评论。
游玩记录模型
游玩记录可以拆成四层:
- 记录身份:记录编号、用户编号、地点和版本;
- 曲目信息:曲目编号、难度和 Track;
- 成绩:达成率、评级、DX 分数和连击;
- 细节:各种音符判定、Fast/Late、对战、伙伴和模式标记。
判定字段的数字是计数,不是百分比。缺少某个音符类型字段时不要补写 0 到服务端;展示时可以折叠没有内容的区域。
查卡模型
查卡响应同时包含实时近似数据和聚合统计:
| 区域 | 解释 |
|---|---|
statistics | 当前在线和时间窗口概览 |
currentOnlinePlayers | 当前可见在线用户 |
recentPlayRecords | 最近游玩记录 |
sessionStatistics | 会话数量摘要 |
sessionDurationAnalysis | 会话时长分布 |
playerSessionFrequencyAnalysis | 用户频率和回访聚合 |
generatedAt | 这次统计生成时间 |
这些数据不保证在同一瞬间生成。多机台页面必须显示机台来源和生成时间;自动刷新时不要把上一轮数据伪装成实时数据。
订单与健康数据的展示语义
机台健康页面的字段可能包含 CPU、内存、网络、磁盘、订单和上报时间。它们是诊断信号:
success/network_status只能说明一次检查结果;reported_at过旧时,当前值可能已经不代表机台现状;- 下载进度是阶段进度,不等于整个订单安装完成;
- 磁盘剩余空间要与待下载大小一起判断;
- 单个 HTTP/TCP 检查失败不应单独触发重装。
页面应给出“最后更新于”时间,并在数据过旧时显示警告。
统计模型
地区和服务级统计是聚合值:
- 允许为空;
- 只在服务返回的时间点成立;
- 不应与个人账号名称拼接;
- 不应从总数倒推出单个用户;
- 可以缓存短时间,但必须标记生成时间。
时间处理
时间字符串可能带 Z、明确偏移或不带时区。客户端应:
- 保存原始字符串;
- 解析时显式指定默认时区;
- 展示给用户时显示本地时区或注明 UTC;
- 排序时按解析后的时间值,不按字符串字典序;
- 记录错误时同时保留本地时间和原始时间。
脱敏策略
公开日志、截图和故障报告至少遮盖:
- Token 和密码;
- 二维码/访问码;
- 机台标识和机台凭据;
- 完整用户编号、地点和对战名称;
- 备份文件内容;
- Hosts 文本、下载地址和镜像密码。
保留接口名、HTTP 状态、业务消息、时间、版本和脱敏后的记录编号,通常足够管理员定位问题。
