积分系统

扣减积分

import { CreditsService } from "@readystart/api-core/billing/services/credits.service"

constructor(private readonly creditsService: CreditsService) {}

async handleRequest(req: CustomRequest) {
  const result = await this.creditsService.deductCreditsFromTenant({
    tenant_id: req.tenant.id,
    user_id: req.user.user_id,
    credits_to_deduct: 10,
    custom_reason: "API 调用",
  })

  if (!result.success) {
    // result.message: "No available credits" | "Credit limit exceeded"
    throw new ForbiddenException(result.message)
  }
}

扣减流程

1. 检查成员积分限额(tenant_member_credit_limits)
   ↓ 如果设置了限额(!= -1)且 used_credits >= credit_limit → 返回 "Credit limit exceeded"
2. 查询组织可用积分包(按 priority DESC, expires_at ASC 排序)
   ↓ 无积分包 → 返回 "No available credits"
3. 按优先级依次扣减,记录交易到 credit_transactions
4. 如果所有积分包用完仍有剩余,剩余部分进入透支(credit_overdrafts)
5. 更新成员已用量(tenant_member_credit_limits.used_credits += credits_to_deduct)

积分包优先级

积分包按 priority 降序消耗,相同优先级按过期时间升序(先过期的先用)。

可通过前端 Credits 页面调整优先级。

成员积分限额

Owner 可以给每个成员设置积分使用上限:

扣减前检查:credit_limit != -1 && used_credits >= credit_limit → 拒绝
扣减后更新:used_credits += credits_to_deduct

允许单次使用超出限额(因为 token 消耗不可预知),但下次请求会被拒绝。

查询积分

// 查询组织可用积分(按创建时间倒序)
const result = await this.creditsService.getActiveByTenant(tenant_id)
// result.data = { all: CreditPackage[], total: number }