docs: add signup bonus requirement

This commit is contained in:
QiuSW
2026-07-08 14:19:00 +08:00
parent 1394cded49
commit 02aa12e8b9
18 changed files with 104 additions and 45 deletions
+4 -1
View File
@@ -15,6 +15,7 @@
- 例外:客户端下载版本检查接口只返回公开发布元数据,设计为匿名只读接口,不需要 API Key,不读取用户、不扣点。
- 图片生成为**同步**接口,可能耗时较长,调用方与网关需设置足够超时(≥ 300s)。
- 用户端注册使用同一个 `User` 账本主体;注册邮箱**必填且唯一**(`ACCOUNT_EMAIL_VERIFICATION="none"`,**不做邮箱验证**、注册即可用),唯一约束避免同邮箱对应多个点数账户。
- T-608 起新用户注册成功一次性赠送 **100 点**试用点数;赠点必须经计费层写入钱包和 `PointsLedger(change_type=signup_bonus)`,不得直接改余额字段。历史用户是否补发不属于默认注册流程。
- API Key 库内只存 `key_hash`(SHA-256)与 `key_prefix`,明文只在创建时返回一次,不在 admin、日志或调用记录中回显。
- 每次生成调用写 `CallRecord`;只允许保存 `result_ref` / `result_summary` 这类引用或摘要,不保存 provider `raw`、base64 图片或敏感上游字段。
@@ -34,7 +35,7 @@ T-306 已实现对外 API 安全加固:`image_url` 下载只允许 `http` / `h
T-604 目标口径:生成接口在 serializer 基础校验后先执行 prompt 本地敏感词检查;命中返回 `400 content_blocked`,且不得下载 `image_url`、不得预扣点、不得写 `CallRecord` / `PointsLedger`、不得调用上游。T-604 不启用输出审核和图片审核;详细规则见 [`moderation.md`](moderation.md)。
T-501 已实现用户端注册 / 登录基线:`/signup` `/login` `/logout` 走 django-allauth + Django session + CSRF;**免邮箱验证、注册即可用(`ACCOUNT_EMAIL_VERIFICATION="none"`,邮箱仍必填且唯一)**,注册成功创建 0 点 `UserWallet`,不创建赠点流水。对外 API 仍只认 API Key,不接受 Web session。(策略见 T-605 与 2026-07-06 决策)
T-501 已实现用户端注册 / 登录基线:`/signup` `/login` `/logout` 走 django-allauth + Django session + CSRF;**免邮箱验证、注册即可用(`ACCOUNT_EMAIL_VERIFICATION="none"`,邮箱仍必填且唯一)**。T-608 需把注册成功后的 0 点钱包初始化改为一次性发放 100 点注册试用额度,并创建 `signup_bonus` 流水。对外 API 仍只认 API Key,不接受 Web session。(免验证策略见 T-605 与 2026-07-06 决策)
T-502 已实现用户端 API Key 自助管理基线:`/apikeys` 走 Django session + CSRF;登录用户可生成和删除自己的 Key,生成后的明文只在重定向后的首个页面显示一次,库内只保存 `key_hash` 与 `key_prefix`。用户端“删除”落库为 `revoked`,保留历史记录关联;吊销后的 Key 调用生成 / 余额接口返回 `403 account_disabled`,缺失、无效或不存在的 Key 仍返回 `401 unauthorized`。
@@ -243,6 +244,7 @@ calculate_points_cost(operation_type: str, alias: str, resolution: str | None =
quote_recharge_points(amount, currency: str = "CNY", at=None) -> RechargeQuote
calculate_points_granted(amount, currency: str = "CNY", at=None) -> int
create_recharge_order(..., user, amount, pay_method: str, currency: str = "CNY") -> RechargeOrder
grant_signup_bonus(user, points: int = 100) -> SignupBonusGrantResult
precharge_call(..., user, points_cost: int, operation_type: str, alias: str, ...) -> CallCharge
mark_call_success(call_record: CallRecord, ...) -> CallRecord
refund_call_points(call_record: CallRecord, ...) -> RefundResult
@@ -257,6 +259,7 @@ query_and_apply_recharge_payment(order_no: str, query_func) -> RechargeResult
- 缺计费规则抛 `NoPricingRuleError(code="no_pricing_rule")`,API 层应翻译为上方同名错误码。
- `ExchangeRate` 按 `currency + effective_from` 取当前 active 汇率;充值下单时应锁定当时的 `exchange_rate` / `points_granted` 到订单,回调入账不得按新汇率重算。
- 金额换点数采用 `floor(amount * points_per_unit)`,点数为整数。
- `grant_signup_bonus()` 是注册赠点唯一入口:只对当前用户首次成功发放 100 点,锁定 / 创建 `UserWallet`,写 `PointsLedger(change_type=signup_bonus, points_delta=+100)`,并用 MySQL 兼容的唯一幂等标记防重复发放。
- `create_recharge_order()` 创建 `RechargeOrder(status=pending)` 并绑定发起用户;写入订单创建时的金额、币种、汇率、预计点数,再调用支付网关下单取二维码票据并回填 `code_url` / `expires_at`;支付平台下单失败时订单标记 `failed`。
- `precharge_call()` 使用事务 + `select_for_update()` 锁 `UserWallet` 行;余额不足抛 `InsufficientPointsError(code="insufficient_points")`,不创建 `CallRecord`、不写 `PointsLedger`、不调上游。
- 预扣成功后写 `CallRecord(status=pending)` 与 `PointsLedger(change_type=consume, points_delta=-N)`;上游成功只更新调用记录,余额不再变化。