# 七日签到后端接口文档 ## 1. 基本约定 - 基础地址:`https://.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. 符合领取条件时,服务端保存领取前的展示快照,再异步执行并等待领取落库。 4. 如果 `data.todayClaim` 非空,前端检查其 `claimId` 是否已处理,未处理才发放 `todayClaim.rewards` 并保存处理标记。 5. 按照领取前的 `status`、`canClaim`、`todayClaimed` 和 `rewards` 渲染活动。本次请求触发自动领取时 `canClaim: true`,当档奖励的 `claimed: false`,便于播放领取展示;前端完成发奖后更新本地状态,不再需要调用 `signInClaim`。当天再次请求会返回已领取状态。 当天重复请求会返回同一个 `todayClaim`,用于首次响应丢失后的重试;它不表示本次新增领取。必须按 `claimId` 去重,不能每次收到就发奖。此接口具有写入行为,不应被预加载或轮询当作纯查询使用。`todayClaim` 只返回当天记录,不包含跨日未处理奖励的补领机制。 活动只在 `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": "未达到签到活动开启关卡" } ``` ### 达到目标关卡 用户没有活动状态时,本次请求会创建活动;已有活动状态时不会重置开始和结束时间。 活动进行中且当天未领取时,本次请求会自动领取下一档奖励。当天已领、活动完成或过期时不新增领取记录。`status`、`claimedCount`、`todayClaimed`、`canClaim`、`nextRewardDay` 和 `rewards[].claimed` 均为本次自动领取前的快照;`todayClaim` 为落库后的当天领取凭据。数据库写入失败时返回失败或抛出服务端错误,不返回虚假的领取成功。 例如已有 Day 1、2 记录,本次自动领取 Day 3:返回 `claimedCount: 2`、`todayClaimed: false`、`canClaim: true`、`nextRewardDay: 3`,Day 1、2 的 `claimed` 为 `true`,Day 3~7 为 `false`,同时 `todayClaim.rewardDay: 3`。当天再次请求则返回 `claimedCount: 3`、`todayClaimed: true`、`canClaim: false` 和 Day 3 的 `claimed: true`。第 7 次领取的首次响应同样保留 `status: active`,再次请求才显示 `completed`。并发请求可能均读到领取前快照,前端发奖始终按 `claimId` 去重,不能只依赖 `canClaim`。 ```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, "todayClaim": { "claimId": "71b4...9a2f", "rewardDay": 1, "rewards": [{ "type": "infinite_health", "count": 900 }] }, "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 自然日是否已经领取 | | `todayClaim` | object \| null | 当天领取凭据:`claimId: string`、`rewardDay: number`、`rewards: RewardItem[]`;当天无记录为 `null`,重复请求返回相同凭据 | | `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`;禁止在正式环境发布。