Skip to content

公开字段语义与转换规则 ​

字段目录解决“有哪些字段”的问题,本页解决“字段应该怎样解释”的问题。它只讨论公开客户端可以读取或提交的结构,不把内部状态码当成稳定协议。

三种成功状态 ​

公开接口并不全部使用同一组顶层字段:

形式判断方法适用场景
successsuccess === true大多数读取、写入和文件操作
validvalid === trueToken 验证
HTTP + 业务组合HTTP 为 2xx 且业务字段成功二进制文件和兼容客户端

只检查 HTTP 200 会把业务失败当成成功;只检查 success 又可能误判验证响应。客户端应按接口页面定义判断,并保留 msg。

可选、空和未知 ​

可选字段 ​

字段名带 ? 表示服务可以不返回它。缺少字段与 null 不必然等价:

  • 缺少数组:按空数组展示,但保留“字段缺失”的兼容信息;
  • 缺少对象:按空对象或不可用状态展示;
  • 缺少数字:显示“暂无”,不要自动填 0,除非页面明确规定 0 的含义;
  • 缺少文本:显示空状态,不要拼接成 undefined。

空数组 ​

空数组通常表示当前范围没有数据,例如没有相片、没有评论、没有在线用户或没有地区统计。它不是网络失败,也不应触发无限重试。

未知字段和枚举 ​

客户端必须允许服务增加字段和状态字符串。未知字段应在原始响应中保留,未知枚举可以显示原文或“未知状态”,不要因为不认识就删除整条记录。

数值单位 ​

字段类别原始单位/形式页面显示建议
达成率放大整数除以 10000 后显示百分比,并保留原值
文件大小字节可换算为 KiB/MiB,但下载和校验使用原值
下载/安装限速MB/s明确显示单位,不与字节/秒混用
游玩/会话时长数值和格式化文本并存统计使用数值,展示可用格式化文本
时间戳Unix 秒或时间字符串先确认来源,再指定时区格式化
统计百分比可能已经是 0–100先看字段说明,避免重复乘 100

达成率示例 ​

ts
function formatAchievement(raw: number): string {
  return `${(raw / 10000).toFixed(4)}%`;
}

显示层可以按需要减少小数位,但导入、导出和二次计算应保留原始整数。

Token 与账号模型 ​

isMachine 是账号类型提示,不应被解释成“权限全部开放”。页面最终可见菜单和服务返回的权限才是有效边界。

推荐保存以下本地状态:

ts
interface LocalSession {
  displayName: string;
  token: string;
  isMachine: boolean;
  expiresAt?: string;
}

Token 只能在内存或受保护存储中使用。displayName 是本地标签,不能替换服务端账号名称,也不能作为接口身份提交。

配方模型 ​

配方响应是由可见配置名映射到配置值的对象:

ts
interface RuleValue {
  enable: boolean;
  value: string;
}

type RuleMap = Record<string, RuleValue>;

value 统一按文本传输,即使页面看起来是数字、列表或 JSON。客户端应以当前字段的输入提示决定如何校验,而不是把所有字段都强制转换成数字。

修改流程:读取 → 选择已存在的可见键 → 只改变目标项 → 保存 → 重新读取。未知键和未编辑项必须保留。

备份模型 ​

备份响应中的 data 是可演进对象。正确做法是保存完整对象:

ts
interface BackupResult {
  success: boolean;
  msg: string;
  data: unknown;
}

不要:

  • 只保存当前 UI 展示的四个字段;
  • 在恢复前自动重命名未知键;
  • 将 Token 写入备份对象;
  • 把不同账号的备份合并后再恢复。

相片模型与二进制 ​

相片列表是文件名数组。文件读取成功时可能不是 JSON,而是图片二进制;失败时才可能返回 JSON 错误封装。

ts
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
}

文件名必须来自服务返回的列表或用户确认,不应从未校验的路径拼接,以避免本地保存位置和显示名称混淆。

成绩模型 ​

成绩数据通常把一首曲目的多个难度拆成行:

ts
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 指向父评论。

游玩记录模型 ​

游玩记录可以拆成四层:

  1. 记录身份:记录编号、用户编号、地点和版本;
  2. 曲目信息:曲目编号、难度和 Track;
  3. 成绩:达成率、评级、DX 分数和连击;
  4. 细节:各种音符判定、Fast/Late、对战、伙伴和模式标记。

判定字段的数字是计数,不是百分比。缺少某个音符类型字段时不要补写 0 到服务端;展示时可以折叠没有内容的区域。

查卡模型 ​

查卡响应同时包含实时近似数据和聚合统计:

区域解释
statistics当前在线和时间窗口概览
currentOnlinePlayers当前可见在线用户
recentPlayRecords最近游玩记录
sessionStatistics会话数量摘要
sessionDurationAnalysis会话时长分布
playerSessionFrequencyAnalysis用户频率和回访聚合
generatedAt这次统计生成时间

这些数据不保证在同一瞬间生成。多机台页面必须显示机台来源和生成时间;自动刷新时不要把上一轮数据伪装成实时数据。

订单与健康数据的展示语义 ​

机台健康页面的字段可能包含 CPU、内存、网络、磁盘、订单和上报时间。它们是诊断信号:

  • success/network_status 只能说明一次检查结果;
  • reported_at 过旧时,当前值可能已经不代表机台现状;
  • 下载进度是阶段进度,不等于整个订单安装完成;
  • 磁盘剩余空间要与待下载大小一起判断;
  • 单个 HTTP/TCP 检查失败不应单独触发重装。

页面应给出“最后更新于”时间,并在数据过旧时显示警告。

统计模型 ​

地区和服务级统计是聚合值:

  • 允许为空;
  • 只在服务返回的时间点成立;
  • 不应与个人账号名称拼接;
  • 不应从总数倒推出单个用户;
  • 可以缓存短时间,但必须标记生成时间。

时间处理 ​

时间字符串可能带 Z、明确偏移或不带时区。客户端应:

  1. 保存原始字符串;
  2. 解析时显式指定默认时区;
  3. 展示给用户时显示本地时区或注明 UTC;
  4. 排序时按解析后的时间值,不按字符串字典序;
  5. 记录错误时同时保留本地时间和原始时间。

脱敏策略 ​

公开日志、截图和故障报告至少遮盖:

  • Token 和密码;
  • 二维码/访问码;
  • 机台标识和机台凭据;
  • 完整用户编号、地点和对战名称;
  • 备份文件内容;
  • Hosts 文本、下载地址和镜像密码。

保留接口名、HTTP 状态、业务消息、时间、版本和脱敏后的记录编号,通常足够管理员定位问题。

Released under the CC BY-NC-SA 4.0 License.