Logo花火漫画开发文档
RPC 函数详情

高清配额管理

免费用户每日高清资源访问配额的查询、检查与消耗

概述

高清(HD)漫画资源默认仅对 VIP 用户开放。为了让免费用户也能体验高清内容,系统提供每日有限的免费高清配额。三个 RPC 函数配合管理这一机制:

函数职责调用者
get_premium_quota统一入口:VIP 返回无限额度,免费用户返回真实配额客户端(通过 JWT 自动识别用户)
check_free_user_premium_quota查询免费用户的今日配额详情get_premium_quota 内部调用,也可直接调用
record_free_premium_view消耗一次配额并记录浏览客户端在免费用户访问高清章节时调用

三个函数均为 SECURITY DEFINER。每日限额从 app_settings 表的 FREE_DAILY_LIMIT 键读取,默认兜底值为 10。

涉及的表

用途
free_user_daily_premium_views记录免费用户每日高清浏览,(user_id, chapter_id, view_date) 联合唯一
app_settings存储 FREE_DAILY_LIMIT 配置值
profiles查询 vip_expiration_date 判断 VIP 状态

get_premium_quota

统一的配额查询入口。通过 auth.uid() 自动识别当前用户,VIP 用户直接返回无限额度,免费用户内部调用 check_free_user_premium_quota 获取真实配额。

参数

无参数。通过 JWT 中的 auth.uid() 自动获取用户身份。

未登录时(auth.uid() = NULL)函数直接返回空结果集。

返回字段

字段类型说明
usedTodayinteger今日已使用次数(VIP 固定为 0)
dailyLimitinteger每日上限(VIP 为 2147483647 即 Max Int)
remaininginteger剩余次数(VIP 为 2147483647
isVipboolean是否为 VIP 用户

调用示例

val quota = supabase.postgrest
    .rpc("get_premium_quota")
    .decodeSingle<PremiumQuota>()

if (quota.isVip || quota.remaining > 0) {
    // 允许访问高清资源
}
const { data } = await supabase.rpc('get_premium_quota')
// data: { usedToday, dailyLimit, remaining, isVip }

内部逻辑

auth.uid()

  ├── NULL → 返回空

  ├── VIP(vip_expiration_date >= now())
  │     → { usedToday: 0, dailyLimit: MaxInt, remaining: MaxInt, isVip: true }

  └── 免费用户
        → 调用 check_free_user_premium_quota(uid)
        → { usedToday, dailyLimit, remaining, isVip: false }

check_free_user_premium_quota

查询免费用户的今日高清配额详情。通常由 get_premium_quota 内部调用,也可直接调用以跳过 VIP 判断。

参数

Prop

Type

返回字段

字段类型说明
used_todayinteger今日已使用次数
remaininginteger剩余次数(≥ 0
can_accessboolean是否还能访问(used < limit
daily_limitinteger每日上限(来自 app_settings

调用示例

val quota = supabase.postgrest
    .rpc("check_free_user_premium_quota") {
        parameter("p_user_id", userId)
    }
    .decodeSingle<FreeQuotaStatus>()
const { data } = await supabase
  .rpc('check_free_user_premium_quota', {
    p_user_id: userId,
  })

record_free_premium_view

消耗一次免费高清配额。在免费用户打开高清章节时调用,函数内部处理去重(同一章节当日重复访问不消耗配额)和超额检查。

参数

Prop

Type

返回字段

字段类型说明
successboolean是否允许访问
used_todayinteger今日已使用次数(含本次)
remaininginteger剩余次数
daily_limitinteger每日上限
statustext状态码,见下表

status 值

含义success
success记录成功,配额 -1true
already_viewed_today今日已看过该章节,不重复扣除true
quota_exceeded今日配额已用完false

调用示例

val result = supabase.postgrest
    .rpc("record_free_premium_view") {
        parameter("p_user_id", userId)
        parameter("p_chapter_id", chapterId)
    }
    .decodeSingle<PremiumViewResult>()

when (result.status) {
    "success" -> // 正常消耗一次配额,加载高清
    "already_viewed_today" -> // 今日已看过,免费放行
    "quota_exceeded" -> // 提示用户升级 VIP
}
const { data } = await supabase
  .rpc('record_free_premium_view', {
    p_user_id: userId,
    p_chapter_id: chapterId,
  })

if (!data.success) {
  // 配额已用完,引导升级
}

执行流程

record_free_premium_view(user, chapter)

  ├── 今天已看过该章节?
  │     → 是:返回 { success: true, status: "already_viewed_today" }

  ├── 今日用量 ≥ 每日上限?
  │     → 是:返回 { success: false, status: "quota_exceeded" }

  └── 插入 free_user_daily_premium_views(ON CONFLICT DO NOTHING)
        → 返回 { success: true, status: "success" }

同一章节当日重复访问返回 already_viewed_todaysuccess = true),不消耗额外配额。这意味着用户可以反复阅读已解锁的章节。

On this page