# 02.基于iCloud构建游戏内排行榜

## 前言

昨天晚上打开我的扫雷游戏，发现排行榜出Bug了：无尽榜单显示不出来了。

虽然很快改好发版了。觉得有必要整理一下我的排行榜的实现思路。以为后续优化提供方向。

> 推荐阅读：[基于iCloud构建用户体系](https://ryukiedev.gitbook.io/wiki/du-li-kai-fa/icloud/ji-yu-icloud-gou-jian-yong-hu-ti-xi)

![Elic](https://github.com/RyukieSama/RyukieDevGitBook/tree/a42f52af6ee4b3f8a9b03ec02ac1ec992f35942a/%E7%8B%AC%E7%AB%8B%E5%BC%80%E5%8F%91/iCloud/Images/CloudKit09.png)

## 一、 榜单基础内容

排序逻辑：

* 游戏等级：越高越靠前
* 完成时间：越短越靠前

显示元素：

* 等级
* 时间
* 用户昵称
* 头像

## 二、 数据流

![数据流](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2Febb21143f42ac02048e6f1953aa95f756ee7eb5d.png?generation=1627466480479603\&alt=media)

基础数据中除了 `游戏等级` `完成时间` 是在 `Rank record` 中，`Nickname` `Avatar` 都是要二次查找的。由于不存在服务端，所以这些数据处理都要客户端自行包装。

### 2.1 Rank Table 无尽游戏记录表

![ElicRankTable](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2Fd1c0f7942e50a341e67ae37f98fadd8440471aec.png?generation=1627466474983225\&alt=media)

### 2.2 User Table 用户表

![ElicUserTable](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F84d821468a592ced28918bb5861da42ecb427f62.png?generation=1627466474624594\&alt=media)

### 2.3 数据逻辑

下面描述一下榜单的数据逻辑吧。

* 玩家成功完成一场无尽模式游戏
  * 前提是 `CloudKit` 可用
* 如果 `Rank table` 中已有该用户创建的记录
  * 如果成绩更好：更新最新游戏记录
  * 否则丢弃本次游戏记录
* 如果 `Rank table` 中没有该用户创建的记录
  * 创建一条记录，保存到 `Rank table` 中
* 用户进入榜单列表拉取数据 `Rank record` 数组
  * 由于并不存在后端，所以数据关联的逻辑需要自己处理
* 遍历榜单数据获取用户ID数组
  * 遍历用户ID数组，从UserTable中批量查询出对应的 `User record` 数组。
* 合并 `Rank record` & `User record` 为 `Rank item data`
  * `Rank item data` 为 `Cell` 展示的数据
* `Cell` 展示数据

## 三、 用户头像

### 3.1 Avatar Table

![ElicAvatarTable](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2Fd0abe961b14903004cef9c300e4bc2c62236eea9.png?generation=1627466475316803\&alt=media)

### 3.2 图片资源加载

虽然 `CloudKit` 提供了保存资源的能力，但我们去请求资源的时候，它就是直接完整的返回整个数据了。

所以如果将头像数据直接保存在 `User record` 中的话，列表的请求速度就很恶心了。（虽然一开始我就是这么做的😂）

在上一步的图中可以看到，`Avatar` 是独立有一张表来存的。这样就为我的 `异步加载图片` 创造了可能。

参考主流图片缓存框架的思路简单封装了一下。在 `Cell` 复用的时候异步加载图片资源。

由于不存在URL这种唯一标识，我采用了 `User record id + Avatar modificationDate` 为图片的唯一标识。

![异步加载图片](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F76d8ecb9608c870ba1d7becae3e1ff6d495ea211.png?generation=1627466475201841\&alt=media)

> 当然用图床URL的形式更方便。但是曾经用七牛做博客图床，然后现在图全挂了，还要自己绑域名（虽然我有）。想想算了还是苹果靠谱点（不要钱）。

## 四、 违规控制

由于榜单内的内容包含了用户自定义的部分如：`Nickname` `Avatar`。而且有人的地方就有人搞事。

如果有不好的用户用了一些不好的文案或者头像，对于项目来说是有风险的。平时可能还好，你要是审核的时候被看到那不是妥妥要把你拒掉。

但作为独立开发者（想省钱），我又不可能去接什么审核服务。况且场景本身就把违规的范围控制的很小了，最多两个榜单200条数据。我没事刷一刷看看就行了。

### 4.1 管理员

为了能快速获得违规用户的ID，我设置了用户角色的字段 `manager`。拥有该权限的用户直接点击榜单 `Cell` 就可以复制 `User record id`。然后进行下一步处理。

### 4.2 违规内容处理

* 昵称违规
  * 直接置空
* 头像违规
  * 删除头像

> 最开始这里是直接删除 `Avatar Table` 中由该用户创建的记录，但是有问题的。思考一下欢迎在评论中回复你想到的问题。其实挺简单的。

### 4.3 屏蔽用户

在 `User Table` 中有个 `blocked` 字段，用以屏蔽用户。

这种用户可以正常用 `App`，游戏记录也会正常记录，但是在榜单列表中会被过滤掉。

> 到现在为止，只屏蔽过一个用户，用💛图片，我给他删除一次，还继续上传，然后我就把他屏蔽了🤷‍♂️

## 五、 优化空间

### 5.1 违规处理

现在违规处理是复制了 `User record id` 去后台处理。感觉可以直接把处理的动作直接放在App里，处理起来也更高效。

### 5.2 异步加载图片

现在的封装还挺简单，有空了完善一下开源出来，也方便自己后续项目使用。

### 5.3 CloudKit 请求

现在在请求方面做了简单的封装，但主要是基于业务的。难以直接套用，还是抽空封装一下把。
