server/laf-cloud/functions/goldMiner/README.md
2026-09-24 17:59:52 +08:00

278 lines
22 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.

# 黄金矿工后端接口 V1.5
前端接入请阅读 [前端接口文档 V1.5](FRONTEND-API.md),其中按接口说明请求参数、返回字段、支付与领奖恢复流程。
更新时间:2026-09-18。本文件描述本分支实际实现;需求基线见 [PRD](../../../docs/goldMiner-PRD.md) 和 [后端设计](../../../docs/goldMiner-backend-design.md)。尚未部署云函数、配置真实商品或进行真实支付联调。
当前版本的 [Postman Collection、环境模板和使用说明](postman/gold-miner.README.md) 已使用 `/goldMiner/index` 和 `/goldMiner/admin`,可直接导入后填写测试环境变量。旧文件的 `/goldMiner` 和 `/goldMinerAdmin` 需更新;`userLevel`、`userCoin` 和 `wx/*` 地址不变。
## 目录与函数名称
黄金矿工专属云函数、配置、触发器、说明和测试统一位于 `laf-cloud/functions/goldMiner/`:
```text
goldMiner/
index.ts / .yaml 玩家接口:goldMiner/index
admin.ts / .yaml 管理接口:goldMiner/admin
config.ts / .yaml 内部配置规则:goldMiner/config
service.ts / .yaml 内部活动业务:goldMiner/service
payment.ts / .yaml 内部支付业务:goldMiner/payment
merchant.ts / .yaml 内部微信商户签名、验签、查单
legacyPayment.ts / .yaml 内部旧客服渠道订单业务
merchantNotify.ts / .yaml 商户支付通知:goldMiner/merchantNotify
jobs.ts / .yaml 内部定时任务:goldMiner/jobs
web/order3.html 外部支付页部署副本
LEGACY-PAYMENT.md 旧渠道接入与部署说明
config.draft.json
trigger.json
tests/gold-miner.test.mjs
tests/gold-miner-postman.test.mjs
postman/gold-miner.postman_collection.json
postman/gold-miner.postman_environment.json
postman/gold-miner.README.md
README.md
```
函数名和 YAML 的 `name` 均与相对 `functions/` 的路径一致,内部引用使用 `@/goldMiner/...`。通用登录、关卡和微信支付入口仍位于原目录,通过导入活动模块接入。目录迁移不改变订单字段、数据库集合、活动期 ID、请求参数或奖励交付标识 `goldMiner.claim`。
本分支已完成目录整理和旧渠道扩展,未部署云函数或删除云端旧入口。发布时需要同步更新客户端调用地址与定时触发器 target;旧平铺函数不会因本地文件移动自动从云端移除。
## 1. 本次实现
- 北京时间周四开始、周一截止;通过第 41 关后获得资格,该关本身不计数。
- 主线和无尽胜利统一增加 1;去重和进度一起写入玩家期记录,计数封顶,不建事件恢复集合。
- 每期完整配置与任务快照,未付费也能累计,付费后任意领取已达标任务。
- 订单仅新增原生对象字段 `goldMiner`,商品价格与数量由服务端确定。
- 原生 Android/iOS 直购,以及旧 iOS 客服渠道的鉴权预下单、支付链接、转单、商户通知和主动查单恢复。
- 延迟支付有达标奖励时生成原期待发清单;无奖励时顺延紧接下一期,开期自动解锁,仅一次。
- 后端授权 → 前端发货 → 现有接口保存余额 → 后端确认领取。后台从不修改金币余额。
- 到期冻结、待发清单、顺延激活、已确认订单的履约重试、管理员配置和索引入口。
## 2. 公共约定
客户端入口使用 POST,活动接口名称为 `goldMiner/index`。示例中的 uid/token 均为占位值,必须使用真实登录结果。所有活动请求要求有效用户和非空有效 token,`gameName=iaa` 不支持。
成功:`{ code: 1, data, msg }`。失败:`{ code: 0, data: null, errorCode, msg }`。时间为 UTC 毫秒;金额为分;金币为整数。请求超时应重试原 ID,不能生成新的胜利事件或新发货凭证。
`periodId` 格式为 `goldMiner:2026-09-17`,日期必须是北京时间周四;该期时间为 `2026-09-17 00:00:00+08:00` 至 `2026-09-21 00:00:00+08:00`,截止端点不包含在内。
## 3. 状态查询
```json
{ "action": "info", "uid": "player-id", "token": "login-token" }
```
返回 `serverTime, status, periodId, startsAt, endsAt, configVersion, priceFen, productId, progressWins, maxTarget, tasks`。时间为毫秒时间戳;价格为人民币分。任务按目标关数排列,前端使用 taskId 发起操作,无需 sequence。
活动 `status` 替代原来的 availability、qualified、purchasable 和 entitlement:
| status | 含义 |
| --- | --- |
| unavailable | 休息期或没有有效配置;返回 serverTime、periodId、startsAt、endsAt、空 tasks,不提供商品/进度字段 |
| locked | 未达到参与门槛;返回 serverTime、periodId、空 tasks,不提供商品/进度字段 |
| purchasable | 本期未解锁,允许购买 |
| purchase_disabled | 本期未解锁,后端暂停购买;仍可累计进度 |
| unlocked | 本期已解锁,包括延迟支付顺延;关闭购买开关不影响此状态和已有领取资格 |
每档仅返回 `taskId, targetWins, itemsSnapshot, claimStatus, progress, completed`。`progress=min(progressWins,targetWins)`;`completed` 只表示达标,不代表可领取。
| claimStatus | 含义 |
| --- | --- |
| locked | 未达标 |
| pending_unlock | 已达标,尚未付费解锁 |
| claimable | 当前满足全部条件,可以申请领取 |
| issuing | 已授权发货,等待客户端保存和确认;重试同一 taskId 的 claim 获取原 grantId 并恢复流程,不重复加币 |
| claimed | 已完成客户端保存确认 |
`info` 不再返回 nextStartsAt、nextPeriodId、currency、claimedThrough,以及任务 sequence、claimed、issuing、claimable、blockReason;领取后也不会附带 grantId、发货时间等内部存档字段。configVersion、原有商品/进度/奖励信息继续保留。
这是查询响应的不兼容调整,前端应以 `status === "purchasable"` 显示购买按钮,以 `task.claimStatus === "claimable"` 显示可领取,并单独处理 issuing。数据库中的权益来源、任务顺序、claimedThrough 和原来的领取状态保持不变;`claim`、`confirm_delivery` 的凭证响应不变,仍包含 grantId 和 claimedThrough。
旧期补发通过 `settlements` 单独读取。建议每次登录检查,不能只在当前期开放时查询。顺延目标期开期查询自动激活后返回 unlocked,不再向前端暴露权益来源。
## 4. 通关上报
扩展现有 `userLevel` 的 `action=save`,保留原有参数,新增 `goldMiner` 对象。表单提交时该字段可使用 JSON 字符串。
主线示例:
```json
{
"action": "save", "uid": "player-id", "token": "login-token",
"levelAmount": 42,
"goldMiner": {
"periodId": "goldMiner:2026-09-17", "eventId": "win-deviceA-1001",
"outcome": "win", "mode": "main", "clearedMainLevel": 42
}
}
```
无尽示例(L 表示当前已存档的主线上限,需替换成实际数值):`levelAmount=L, isWuXian="true"`,goldMiner 内容为:
```json
{
"periodId": "goldMiner:2026-09-17", "eventId": "win-deviceA-1002",
"outcome": "win", "mode": "endless", "endlessSequence": 123
}
```
同一次胜利在重试中固定 eventId、模式、序号和 periodId。活动期绑定发生在首次胜利上报,不能在重试时改成下一期,也不能在开局时固定为尚未开放的旧期。无尽序号须唯一且持久化,不能用会重复抽取的关卡模板号代替。
普通存档返回外壳不变,额外在 `data.goldMiner` 返回活动处理结果。例如 `code=1, activityCounted=true, duplicate=false, progressWins=1`。合法重复请求返回 `duplicate=true`;封顶不继续增长。
活动处理失败时普通关卡存档仍成功,活动子结果返回 `code=0,errorCode`。前端必须分别检查两个结果;只有活动内成功写入的进度保留,过期后不回补写入失败的进度。无 goldMiner 参数的旧请求完全不参与活动计数。
## 5. 购买、支付和查单
使用现有 `wx/orderPaySig`(Android)或 `wx/iosorderPaySig`(原生 iOS):
```json
{
"uid": "player-id", "token": "login-token",
"itemid": "gold_miner", "createRequestId": "purchase-deviceA-1001"
}
```
商品 ID 必须为配置中的 `gold_miner` 或 `gold_miner_` 前缀商品。该前缀为本活动保留,不得用于普通金币包。服务端忽略客户端的 itemPrice/itemCount,固定数量 1,按当期快照定价。
同账号同一期使用唯一订单号,重复请求/并发设备均复用此单;签名返回结构沿用现有协议。即使 createRequestId 改变,也不再创建另一笔同期待付订单。
`wx/payCallBack` 对黄金矿工订单执行正式/测试环境校验、原始消息签名、用户、商品、数量和原价匹配。只有可信回调会写入 `goldMiner.confirmedAt`。之后回调、查单、登录补单和定时任务均可继续同一履约路线。
`wx/getPayInfo`、`wx/iosgetPayInfo`、`wx/getOrderReward` 查询黄金矿工订单时必须携带 uid/token;返回:
```text
code: 1
data: {
pay_state: 2,
goldMiner: { fulfillmentStatus, fulfillmentRoute, targetPeriodId, ... },
rewardDelivery: "goldMiner.claim"
}
```
这里表示活动权益已交付,不表示所有任务金币已发放。通用领取接口不能在支付未确认时将黄金矿工订单直接改为已完成,客户端也不能套用普通商品补发金币。
旧 iOS 客服渠道现通过 `goldMiner/index` 的 `action=create_order, channel=legacy_ios` 创建鉴权订单;详细请求、配置与支付页见 [旧渠道接入说明](LEGACY-PAYMENT.md)。客服入口使用服务端签发的短期票据,转单保留完整活动快照;商户通知和主动查单确认付款后沿用活动履约。登录排除黄金矿工的旧通用补发和过期清单删除,使用专属查单恢复。两个渠道共用每玩家每期唯一订单,已有订单时切换渠道返回 `PAYMENT_CHANNEL_CONFLICT`。
### 支付边界
- 正常期内权益解锁:原期自行领取,期末剩余达标奖励进入待发清单。
- 结束后首次履约:冻结原期进度;有奖励则留原期补发,零奖励则预留紧接下一期。
- 已选路线持久化,回调重试不能改道;已在期内正常解锁的零达标玩家不顺延。
- 顺延目标期开期后,查询、通关和下单均会确保资格生效;不重置该期已有进度。
- 未支付的废弃旧订单不会永久阻止下一期正常购买。若该旧单之后才确认付款,并与目标期已有购买订单冲突,则标记 `manual_review`,不重复授予资格或自动转到第三期。
- 原生渠道查单仍读取本地已确认订单并恢复履约。旧客服渠道新增微信商户 API 主动查单,由查单接口、登录和定时任务触发;结果必须验签并核对商户、用户、金额、订单和环境。超时/验签失败不确认付款、不删除订单,退避重试。
## 6. 领取和前端保存确认
领取授权:
```json
{
"action": "claim", "uid": "player-id", "token": "login-token",
"periodId": "goldMiner:2026-09-17", "taskId": "task_1", "requestId": "claim-1001"
}
```
返回 `grantId, taskId, items, claimStatus, claimedThrough, requiresClientDelivery`。奖励仅从当期快照读取。到期后所有 claim 返回 PERIOD_SETTLEMENT_REQUIRED(服务器正在结算奖励),不再创建或恢复普通领奖授权;数据库写入也限制在结束前。已有授权仍可通过 confirm_delivery 确认,与补发共用发货状态。
前端按 grantId 做本地去重并发货,使用原资源接口(例如 `userCoin.save`)保存发放后的总余额。保存成功后确认:
```json
{
"action": "confirm_delivery", "uid": "player-id", "token": "login-token",
"periodId": "goldMiner:2026-09-17", "taskId": "task_1", "grantId": "claim返回的稳定凭证"
}
```
可任意领取和确认已达标任务;claimedThrough 仅保留从第一档开始连续已确认的数量,不限制领奖。保存失败只重试保存;确认失败只重试确认;已完成任务的重试返回原结果,不重新加币。
`confirm_delivery` 是鉴权后的客户端保存确认,后端不会据此加金币,也没有独立证明本地发货成功。继续保留清档、多设备并发、发货和保存之间中断等既有风险,没有实施通用金币版本协议。
## 7. 服务器到期结算与补发
后台 jobs 和延迟支付履约调用 closePlayer,在玩家期同一文档内冻结进度并给已付费达标的未确认任务统一创建授权。已存在的 grantId 保留不变;新增 settlementPreparedAt 标记结算已准备,旧数据由后台重扫补齐。用户查询不再触发结算,必须部署并启用每分钟触发器。
```json
{"action":"settlements","uid":"player-id","token":"login-token"}
```
返回 `{status,items,nextCursor}`,status 为 pending_delivery / settling / no_pending_rewards。扫描玩家所有已结束期,非仅最后参与期;每个 items 元素为 `{settlementId,periodId,rewards:[{taskId,grantId,items}]}`,只包含尚未确认奖励。已全部确认或没有达标奖励的期不返回。各期奖励按档位顺序排列,金币不由后端增加。
前端不传 limit,后端固定每页扫描 20 条历史玩家期记录,兼容忽略旧 limit。已准备好的期优先返回 pending_delivery 和奖励清单,其他期尚未结算不会隐藏这些奖励。无论 pending_delivery 还是 settling,有 nextCursor 就继续翻页;扫描结束后从第一页复查,仍在结算时稍后再查。no_pending_rewards 表示后端已检查该玩家当前环境下所有已结束期,无待发或待结算记录,items=[]、nextCursor=null;不能仅凭某页空列表结束检查。
前端直接用清单授权发奖并保存资源,不再调用 claim。金币确定保存成功后确认:
```json
{"action":"confirm_settlement_delivery","uid":"player-id","token":"login-token","settlementId":"清单ID","grantIds":["原稳定授权ID"]}
```
返回 `{settlementId,confirmedGrantIds,deliveryStatus}`,deliveryStatus 为 partial / client_saved。支持同一结算单批量确认 1~100 个不重复 grantId,原子校验归属及授权状态,允许非连续任务,重复确认不重复推进。原 confirm_delivery 与新确认共用任务状态。已确认奖励在后续查询自动消失。
旧 ack_settlement 保留为可选展示确认,未全部发完仍返回 DELIVERY_PENDING;新补发流程无需调用它。清单仍可能包含“金币已保存、确认响应丢失”的奖励,前端必须用 grantId 恢复交付记录,不能重新加币。详细字段及恢复流程见 [前端文档](FRONTEND-API.md)。
## 8. 管理、配置与部署
测试服可通过 `publish_test_period` 指定任意开始和结束时间;需同时开启 `PAYMENT_APP_ENV=test` 与 `GOLD_MINER_TEST_PERIODS_ENABLED=true`。详见 [临时期操作说明](TEST-PERIODS.md) 和 [完整请求示例](test-period.publish.example.json)。正式周历保持不变。
`goldMiner/admin` 使用独立环境变量 `GOLD_MINER_ADMIN_TOKEN`,无默认密码。HTTP POST 的 body 传 adminToken,只允许服务端运维使用。
- `action=setup_indexes`:创建新增集合索引,仅黄金矿工订单使用受限的订单唯一索引,不重写旧商品订单。
- `action=publish, config={...}`:校验并发布不可变新版本,publishedAt 由后端设置;effectiveFromPeriodId 必须是未来期。
- `action=set_purchasable, periodId, purchaseEnabled=false`:停止已建立活动期的新购买;不改变配置和已购权益。
[草稿配置](config.draft.json) 的 priceFen/effectiveFromPeriodId 为 null、tasks 为空且 status=draft,不会启用活动。运营必须提供实际数值,填入递增的 targetWins 与正整数金币,发布后才可生成活动期。本期门槛 41。实现保护上限为 100 档任务、最终目标 1000 次,避免单文档去重无界增长。
配置在周四开期冻结;懒创建活动期只选择开期前已发布且已生效的版本。配置版本更新不会改变已冻结期或玩家快照。
活动存储现只使用 `activityConfigs` 与 `goldMinerPlayerPeriods`,支付仍复用 order。配置与百人赛通过 activityId 区分;运行控制以独立 control 记录保存。玩家期记录内嵌 entitlement、原任务发货状态和整期结算状态,不再维护 goldMinerPeriods/Entitlements/RewardGrants/Settlements/JobCursors 等独立集合。
详细结构、配置选取及发布流程见 [公共配置说明](../activityConfig/README.md)。已有数据必须在维护窗口按 [迁移说明](../activityConfig/MIGRATION.md) 转换;新代码不再回读旧集合。原 grantId、settlementId 和玩家期快照保留。
完整管理请求示例:[黄金矿工配置](config.publish.example.json)(2026-09-24 起,门槛 41、1 元、目标 3/5/7/9/10,奖励 100/200/300/400/500 金币)。日期过期后改为未来周四再发布。示例数值不代表自动开启活动。
部署顺序:
1. 先发布公共内部模块 activityConfig/store,再发布完整共享函数及 YAML:goldMiner/config → goldMiner/service → goldMiner/payment → goldMiner/merchant → goldMiner/legacyPayment → goldMiner/jobs。
2. 发布 goldMiner/index、goldMiner/admin 与 goldMiner/merchantNotify;共享模块及 jobs 的 HTTP methods 为空,不能暴露为玩家入口。
3. 发布改动的 userLevel、login、wx/orderPaySig、wx/iosorderPaySig、wx/payCallBack、wx/getPayInfo、wx/getOrderReward、wx/iosgetPayInfo、wx/KeFuInfo、wx/checkIos。
4. 配置管理员口令、原有支付环境/签名密钥及 [旧渠道环境变量](LEGACY-PAYMENT.md),运行 setup_indexes;生产和测试环境独立操作。旧渠道默认关闭;发布 web/order3.html 到实际支付页地址并完成客户端预下单接入后再开启。
5. 按 [触发器配置](trigger.json) 绑定每分钟运行的 goldMiner/jobs。仅上传 TS/YAML 不等于定时触发器已绑定。
6. 接入兼容客户端并验证,再发布完整的未来期商业配置。
所有共享引用沿用仓库静态 `@/` 导入方式,需先部署依赖。goldMiner/jobs 每次商户查单最多处理 5 条,其他每类最多处理 100 条,不再保存扫描游标,成功记录按业务状态退出队列,失败记录设置 nextRetryAt 退避;并发执行可重复处理但业务幂等。配置故障不阻断旧期关闭扫描。日志中的 job retry/manual_review 需要运维检查。
该分支从后端基线 e9a4d34 创建,没有包含主工作区未提交的登录/小程序福利改动。合并时注意这些改动与 login.ts 的补单接入位置。
## 9. 验证结果
以下为历史版本验证记录;本次集合精简的测试结果以提交说明为准。
前次查询协议调整后,黄金矿工行为测试 58 项、Postman 脚本测试 19 项、支付页测试 4 项通过,共 81 项。新增覆盖暂停购买、发货恢复、任务顺序以及响应字段白名单。支付来源与外部 SDK、Mongo 条件写使用模拟,未连接线上支付或数据库。上一版支付扩展另跑支付路由、新手礼包、限时礼包、月卡和登录补单回归通过;这些是历史验证记录。
上一版完整相关回归共 128 项:120 通过,8 项失败已在未改动的 e9a4d34 基线复现(以下为历史记录,不代表本次全量重跑):
- Jungle 旧测试 6 项:规划表、领取资格、回调状态、价格及 usersAd 等旧断言与当前代码不一致。
- 登录 1 项:ordinary usersAd login does not create migration fields。
- 前端源码检查 1 项:login-wucai-source 从后端目录引用不存在的相对前端路径。
```powershell
$env:TEST_WX_PAY_NOTIFY_URL='https://sor779u2w8.sealoshzh.site/wx/payCallBack'
node --test laf-cloud/functions/goldMiner/tests/gold-miner.test.mjs laf-cloud/tests/payment-routing.test.mjs laf-cloud/tests/rookie-gift.test.mjs laf-cloud/tests/starter-pack.test.mjs laf-cloud/tests/monthly-card-renewal.test.mjs laf-cloud/tests/login-wucai-state.test.mjs laf-cloud/tests/login-wucai-source.test.mjs laf-cloud/tests/login-cat-arr.test.mjs laf-cloud/tests/jungle-treasure.test.mjs
```
黄金矿工 9 个 TypeScript 模块通过定向类型检查(外部 Laf/Node 使用最小声明,保持项目非 strict 设置)。额外启用 noUnusedLocals/noUnusedParameters 时,共享依赖 Utils.ts 的既有 db 未使用报错,已确认 HEAD 中同样存在,未改动无关文件;这不代表旧仓库全量类型检查或云端 SDK 编译已通过。发布前还需验证实际 Mongo 的单文档条件更新、`$expr/$$NOW` 截止边界、原生支付通知及定时任务绑定。
## 公共活动参与开关
activityConfigs 中 recordType=version 的外层 enabled 控制是否允许新玩家参与。publish 请求顶层接受布尔值 enabled(缺省 true);旧记录缺失时为开启。最新适用版本关闭后不会回退旧版本。
关闭后,新玩家 info 返回 unavailable,不能新增进度或下单;已经初始化任务的本期玩家仍可查询、推进和购买,purchaseEnabled 仍单独控制购买。已支付并分配到目标期的权益继续恢复,支付回调、领奖、到期结算不受影响。活动列表携带玩家身份后保留已有玩家的入口,匿名和新玩家不返回关闭期。下一期按该期配置重新检查,不沿用上一期参与资格。
版本仍按期开始时间选择;发布未来版本不会提前改变当前期。运营需要立即停止新参与,可修改当前适用 version 的外层 enabled=false。临时测试期在排期条目外层保存 enabled,语义一致。公共接口及新活动接入约定见 [公共配置说明](../activityConfig/README.md)。
## VIP 六档礼包与任意档位领取
完整发布示例见 [vip-tiers.example.json](vip-tiers.example.json),配置、快照锁定及兼容规则见 [VIP-TIERS.md](VIP-TIERS.md)。前端新增任务状态 pending_unlock(已达标待付费),付费后所有已达标任务均可任意领取和确认。普通周周期及未付费累计规则保持原实现。