server/laf-cloud/signInActivity.API.md

268 lines
8.9 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. 基本约定
- 基础地址:`https://<APPID>.laf.run`
- 数据格式:`application/json`
- 时区:UTC+8
- 活动周期:触发当天为第 1 个自然日,第 15 天 00:00 结束
- 签到规则:14 个自然日内累计领取 7 次,允许断签,每个自然日最多领取一次
- 适用用户库:仅主游戏 `users`;`gameName: "iaa"` 不支持签到活动
- 身份校验:请求携带登录接口返回的 `uid` 和 `token`
- 通用成功码:`code: 1`
- 通用失败码:`code: 0`
签到奖励由后端校验并记录,实际金币、道具、无限体力和猫皮肤仍由前端根据领取接口返回的 `rewards` 发放。前端必须使用 `claimId` 做发奖幂等。
## 2. 推荐调用流程
1. 玩家进入主界面后调用 `signInActivityInfo`。
2. 如果返回 `data: false`,表示玩家尚未达到目标关卡,不展示活动。
3. 如果返回活动对象,按照 `status`、`canClaim`、`todayClaimed` 和 `rewards` 渲染活动。
4. 玩家点击领取时调用 `signInClaim`。
5. 领取成功后,前端先检查本地是否处理过 `claimId`,未处理过才发放 `rewards`。
6. 发奖完成后保存 `claimId`,并重新请求 `signInActivityInfo` 刷新页面状态。
活动只在 `users.levelAmount` 达到目标关卡且没有活动状态的用户首次调用 `signInActivityInfo` 时创建。`login` 和 `setUserLevel` 均不读取签到配置、不判断签到资格,也不创建签到活动。
## 3. 查询签到活动信息
### `POST /signInActivityInfo`
云函数同时配置了 GET 和 POST;客户端统一使用 POST。
### 请求参数
```ts
interface SignInActivityInfoRequest {
uid: string;
token: string;
gameName?: string;
}
```
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `uid` | string | 是 | 用户 `_id` |
| `token` | string | 是 | 登录令牌 |
| `gameName` | string | 否 | 不传表示主游戏;传 `iaa` 会被拒绝 |
### 未达到目标关卡
```json
{
"code": 1,
"data": false,
"msg": "未达到签到活动开启关卡"
}
```
### 达到目标关卡
用户没有活动状态时,本次请求会创建活动;已有活动状态时不会重置开始和结束时间。
```json
{
"code": 1,
"data": {
"serverNow": 1788310800000,
"activityId": "seven_day_sign_in_v1",
"status": "active",
"triggerLevel": 23,
"triggerReached": true,
"startAt": 1788278400000,
"endAt": 1789488000000,
"claimedCount": 0,
"todayClaimed": false,
"canClaim": true,
"nextRewardDay": 1,
"rewards": [
{
"day": 1,
"items": [
{ "type": "infinite_health", "count": 900 }
],
"claimed": false
}
]
},
"msg": "成功"
}
```
### 返回字段
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `serverNow` | number | 服务器当前毫秒时间戳 |
| `activityId` | string | 用户参与的活动配置 ID |
| `status` | string | `active`、`completed` 或 `expired` |
| `triggerLevel` | number | 活动开启目标关卡,当前配置为 23 |
| `triggerReached` | boolean | 服务端根据 `users.levelAmount` 确认的关卡是否达标 |
| `startAt` | number | 活动第 1 天 UTC+8 零点时间戳 |
| `endAt` | number | 活动结束时间戳,不包含该时刻 |
| `claimedCount` | number | 已领取奖励天数,范围 0~7 |
| `todayClaimed` | boolean | 当前 UTC+8 自然日是否已经领取 |
| `canClaim` | boolean | 当前是否可以领取下一档奖励 |
| `nextRewardDay` | number \| null | 下一档奖励序号;当前不可领取时为 `null` |
| `rewards` | RewardDay[] | 完整七天奖励及每档领取状态 |
状态说明:
| 状态 | 说明 |
| --- | --- |
| `active` | 活动期内且尚未领取满 7 天 |
| `completed` | 已领取全部 7 天奖励 |
| `expired` | 已到第 15 天 00:00 且未领取满 7 天 |
## 4. 领取签到奖励
### `POST /signInClaim`
客户端不能指定领取第几天。后端按照当前累计领取次数自动发放下一档配置,并使用用户、活动和自然日生成唯一 `claimId`。
### 请求参数
```ts
interface SignInClaimRequest {
uid: string;
token: string;
gameName?: string;
}
```
### 成功响应
```json
{
"code": 1,
"data": {
"claimId": "71b4...9a2f",
"rewardDay": 1,
"rewards": [
{ "type": "infinite_health", "count": 900 }
],
"claimedCount": 1,
"todayClaimed": true,
"completed": false
},
"msg": "领取成功"
}
```
### 返回字段
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `claimId` | string | 本次领取唯一标识,前端发奖幂等键 |
| `rewardDay` | number | 本次领取的奖励序号,范围 1~7 |
| `rewards` | RewardItem[] | 本次需要由前端发放的奖励 |
| `claimedCount` | number | 领取成功后的累计领取天数 |
| `todayClaimed` | boolean | 成功时固定为 `true` |
| `completed` | boolean | 本次领取后是否已经领满 7 天 |
## 5. 奖励数据结构
```ts
interface RewardDay {
day: number;
items: RewardItem[];
claimed: boolean;
}
interface RewardItem {
type: "coin" | "freeze" | "hammer" | "magic_wand" | "infinite_health" | "cat_skin";
count: number;
itemId?: number;
}
```
| `type` | 含义 | `count` 单位 | 额外字段 |
| --- | --- | --- | --- |
| `coin` | 金币 | 个 | 无 |
| `hammer` | 锤子 | 个 | 无 |
| `freeze` | 冻结道具 | 个 | 无 |
| `magic_wand` | 魔法棒 | 个 | 无 |
| `infinite_health` | 无限体力 | 秒 | 无 |
| `cat_skin` | 猫皮肤 | 个 | 必须提供正整数 `itemId` |
当前七天配置:
| Day | 奖励数据 | 展示含义 |
| --- | --- | --- |
| 1 | `infinite_health: 900` | 无限体力 15 分钟 |
| 2 | `hammer: 1` | 锤子 ×1 |
| 3 | `infinite_health: 1800` | 无限体力 30 分钟 |
| 4 | `freeze: 1`、`magic_wand: 1` | 冻结 ×1、魔法棒 ×1 |
| 5 | `coin: 600` | 金币 ×600 |
| 6 | `hammer: 1`、`freeze: 1`、`magic_wand: 1` | 三种道具各 ×1 |
| 7 | `infinite_health: 3600`、`cat_skin: 1, itemId: 12` | 无限体力 1 小时、专属猫 12 |
## 6. 错误响应
两个签到接口共用的错误:
| `code` | `data` | `msg` | 触发条件 |
| --- | --- | --- | --- |
| 0 | null | `未获取到uid` | 未传 `uid` |
| 0 | null | `七日签到仅支持主游戏` | `gameName` 为 `iaa` |
| 0 | null | `未获取到用户信息` | 用户不存在 |
| 0 | null | `token校验失败` | token 不匹配 |
| 0 | null | `签到活动配置不存在或配置无效` | 找不到配置、配置非法或同时启用多条配置 |
`signInActivityInfo` 特有错误:
| `code` | `data` | `msg` |
| --- | --- | --- |
| 0 | null | `签到活动开启失败` |
`signInClaim` 特有错误:
| `code` | `data` | `msg` |
| --- | --- | --- |
| 0 | null | `签到活动尚未开启` |
| 0 | null | `七日签到奖励已全部领取` |
| 0 | null | `七日签到活动已结束` |
| 0 | null | `今天已经领取` |
| 0 | null | `签到奖励配置异常` |
## 7. 数据库配置
配置集合:`sign_in_activity_config`
- 必须且只能存在一条 `enabled: true` 的有效配置。
- 当前可导入配置文件:`laf-cloud/sign-in-activity-config.json`。
- 用户开始活动后通过 `activityId` 继续读取配置,因此旧配置可以禁用,但在参与用户全部结束前不能删除。
用户活动状态写入 `users.signInActivity`。数据库中使用 JSON 字符串保存,读取时由服务端解析为下述结构;这样可以兼容字段原值为 `null` 的用户,并避免 Laf 将对象更新展开成点路径后触发 MongoDB 错误:
```ts
interface SignInActivityState {
activityId: string;
startAt: number;
endAt: number;
activatedAt: number;
completedAt: number | null;
}
```
领取记录集合:`sign_in_activity_claims`
- 每次领取保存奖励序号、奖励快照和领取时间。
- 记录 `_id` 与 `claimId` 相同,由 `uid + activityId + UTC+8自然日` 计算 SHA-256。
- 同一用户、同一活动、同一自然日的并发请求只能成功写入一条记录。
## 8. 前端接入注意事项
- 不要根据客户端本地关卡自行判定资格;服务端使用 `users.levelAmount` 判断,以 `signInActivityInfo` 返回值为准。
- `data: false` 是正常成功响应,不应作为网络或服务器错误处理。
- 领取成功不代表后端已经增加资产;前端负责发奖。
- 前端必须保存已处理的 `claimId`,避免重复回调造成重复发奖。
- `infinite_health.count` 单位是秒。
- Day 7 的 `cat_skin.itemId` 当前为 12,客户端必须先支持猫 12 的资源、展示和幂等写入 `catArr`。
- 倒计时使用 `serverNow` 和 `endAt`,不要完全依赖客户端本地时间。
## 9. 单账号测试接口
测试环境可发布 `POST /signInTestAdmin`,用一个账号快速准备未达标、待领取 Day 1~7、当天已领、过期和完成状态。该接口的密钥配置、参数和测试顺序见 `laf-cloud/signInActivity.TESTING.md`;禁止在正式环境发布。