feat(server): 免费版账户级分钟卡控 + 累加式看广告加时(#21 后端)

免费版此前形同虚设:ConnectNode 每次连接发固定 daily_minutes×1min TTL、
从不扣减已用,重连即崭新 10 分钟,日额度从未强制。改为账户级(全设备共享)
真卡控 + 累加式看广告加时:

- migration 000020: usage_daily 加 ad_bonus_minutes(sqlite+mysql)。
  当日额度 = plans.daily_minutes + ad_bonus_minutes;剩余 = 额度 − minutes_used。
- usage.store.AddAdBonusMinutes: 事务内累加封顶(替代布尔 MarkAdUnlocked),
  返回新总额+本次实际加时;GetDay/GetUsageRange 补读 ad_bonus_minutes。
- usage.service.UnlockAd: verify+nonce 后 +adBonusPerAd(10) 封顶 adDailyBonusCeiling(120),
  返回 granted+remaining;TodaySummary 加 MinutesCap/AdBonusMinutes。
- usage.ads DevVerifier: 放行式占位校验(nonce 防重放仍生效),接真 AdMob 前
  让看广告加时流程可端到端跑通;main.go 按 ADS_DEV_MODE/默认装配。
- ads/unlock: 204 → 200 返回 {granted_minutes, minutes_remaining}。
- ConnectNode 免费门: 读 AccountDayMinutes(used,bonus) → remaining≤0 拒 QUOTA_EXHAUSTED,
  否则 TTL=remaining(凭证到点硬切断兜底)。nodes.store 加 AccountDayMinutes。
- /me: 补 ad_bonus_minutes,quota_today_min 分母改 daily+bonus,加 quota_cap_min。
- quota.CheckFreeConnect 同步累加语义(免费基础 10 分钟不再需先看广告)。
- openapi + 契约/迁移版本/集成测试同步;新增 SQLite AddAdBonusMinutes 单测。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-01 23:44:10 +08:00
parent 0bedd1c4d1
commit e023fb6579
19 changed files with 367 additions and 101 deletions
+28 -6
View File
@@ -294,10 +294,11 @@ paths:
/ads/unlock:
post:
operationId: adsUnlock
summary: 激励视频广告解锁当日时长
summary: 激励视频广告加时(累加式)
description: |
免费版用户完成激励视频广告后调用。服务端向广告平台(AdMob/Unity)校验 ad_token 真伪,
通过后记录 `usage_daily.ad_unlocked_at`,当日 connect 接口方可放行。
通过后 `usage_daily.ad_bonus_minutes` 累加固定分钟(每日封顶),当日免费额度随之上浮。
额度为**全账户共享**(非每设备)。响应返回本次实际加时与最新剩余分钟。
tags: [Commerce]
requestBody:
required: true
@@ -315,8 +316,20 @@ paths:
type: string
description: 广告 SDK 签发的服务端回执 token
responses:
"204":
description: 广告解锁成功(无响应体)
"200":
description: 广告加时成功
content:
application/json:
schema:
type: object
required: [granted_minutes, minutes_remaining]
properties:
granted_minutes:
type: integer
description: 本次广告实际加时分钟(已达每日封顶时为 0)
minutes_remaining:
type: integer
description: 加时后账户当日剩余分钟(全账户共享)
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -715,14 +728,23 @@ components:
type: integer
description: 今日已使用分钟数
example: 3
minutes_cap:
type: integer
nullable: true
description: 今日额度 = 套餐每日分钟 + 看广告累加分钟;付费无限制时为 null
example: 20
minutes_remaining:
type: integer
nullable: true
description: 今日剩余分钟数,付费无限制时为 null
description: 今日剩余分钟数(全账户共享),付费无限制时为 null
example: 7
ad_bonus_minutes:
type: integer
description: 今日已通过看广告累加的分钟数
example: 10
ad_unlocked:
type: boolean
description: 免费版今日是否已完成激励广告解锁
description: 免费版今日是否已通过看广告加过时(ad_bonus_minutes > 0,历史字段)
example: false
Device: