library-picturebook-activity/docs/design/create/18_C端额度查询接口对接文档_V1.0.md

16 KiB
Raw Blame History

C 端用户额度查询接口对接文档 V2.1

接口路径GET /api/v1/quota/user 适用端H5 / Android / 小程序 / iOS 签名方式 无需 HMAC 签名(服务端已对此路径加白名单) 更新时间2026-04-18 变更说明V2.1 用户级闸门版——补回 V2.0 误删的 userCallLimit/userCallRemaining 两字段,对齐后端真实校验链路,避免「前端绿灯进入 → 创作时被拒」的体验断崖


0. 为什么有 V2.1(必读)

V2.0 把字段从 12 个砍到 6 个,初衷是"机构没额度=用户没额度"够用了。但遗漏了反命题

机构有额度 ≠ 用户有额度

实际系统里,QuotaService.checkUserCallLimit(orgId, phone, periodId) 会按 (机构+手机+周期) 三元组撞 Redis 用户级上限(默认 5 次/用户/周期,可在 t_org.user_call_limitt_config.user_call_limit 调整。V2.0 接口不返用户级数据 → 前端拿 canCreate=true 进创作页 → 真正的 POST /creation/image-storyQUOTA_EXCEEDED 拒掉 → 用户懵 → 客服压力。

V2.1 修复:接口返回 userCallLimit/userCallRemaining + canCreate 决策升级为四闸门


1. 用途

让 C 端用户在进入创作前一次拿到三件事:能不能创作 + 还剩多少 + 不能为什么

canCreate 字段一键决定是否放行创作入口;summary 字段是服务端拼好的友好文案,前端直接 Toast 即可,无需自己拼字符串。

典型触发场景

  • 首页 / 创作中心点击"+"号开始新作品
  • 进入画一画页面前的额度预检
  • 创作历史页"重新创作"按钮的可用性判断

额度范围:仅 A1/A2/A3 主创作绘本生成。A6/A7 角色提取重建有独立 a5_user_call_limit 池,不在此接口范围。


2. 请求

2.1 端点

GET http://121.40.20.224:8267/api/v1/quota/user

2.2 请求头

Header 必填 说明
Content-Type GET 无 body可省略
X-App-Key / X-Signature / X-Timestamp / X-Nonce 此接口不校验 HMAC 签名,无需带任何签名头
Authorization: Bearer xxx 不需要 Bearer token

2.3 Query 参数

参数 类型 必填 说明 示例
orgId string 机构 ID LESINGLE888888888
phone string 登录用户手机号(参与用户级额度计算 + 日志追溯) 13800138000

⚠️ V2.1 变更phone 从"仅日志追溯"升级为"参与额度计算"——它是用户级 Redis 计数器的 key 一部分,必须传真实登录用户手机号,不能瞎传。

2.4 完整请求示例

GET /api/v1/quota/user?orgId=LESINGLE888888888&phone=13800138000 HTTP/1.1

curl

curl 'http://121.40.20.224:8267/api/v1/quota/user?orgId=LESINGLE888888888&phone=13800138000'

3. 响应

3.1 统一外层结构(与现有 ApiResponse 对齐)

{
  "code": 200,
  "msg": "success",
  "data": { ... },     // 见下方 UserQuotaVO
  "success": true
}

3.2 data 对象 UserQuotaVOV2.1 用户级闸门版8 字段)

字段 类型 说明
orgName string 机构名称用于显示「XX 幼儿园 · 剩余 N 次」)
quotaLimit int 机构总创作配额A1/A2/A3 共享池)
quotaRemaining int 机构剩余创作次数
userCallLimit int 本用户每周期创作上限0 = 未启用上限(不限制单用户)
userCallRemaining int 本用户本周期剩余次数-1 = 不限制;>=0 = 实际剩余
canCreate boolean 决策位:机构剩 > 0 (用户不限 用户剩 > 0) 才 true
reason string|null 不可创作时的实际情况;canCreate=true 时为 null
summary string 服务端拼好的用户友好文案,前端直接 Toast 即可

⚠️ 范围说明:本接口额度仅反映 A1/A2/A3 主创作. A6/A7 提取重建额度(a5_user_call_limit)、视频生成额度等不在此接口范围.

3.3 canCreate=false 的四类原因(按闸门顺序)

闸门 reason 示例 含义
① 机构存在 "机构不存在" orgId 在数据库中查不到
② 机构授权 "机构「XX 幼儿园」未授权" 机构记录存在但未开通授权
③ 机构总额度 "机构创作额度已耗尽(共 1000 次)" quotaA - quotaAUsed ≤ 0
④ 用户级额度 "本用户本周期创作额度已用完(共 5 次/周期)" user_call_limit > 0 且本用户已达上限

3.4 响应示例

通过场景 A用户级有上限

{
  "code": 200,
  "msg": "success",
  "data": {
    "orgName": "XX 幼儿园",
    "quotaLimit": 1000,
    "quotaRemaining": 832,
    "userCallLimit": 5,
    "userCallRemaining": 3,
    "canCreate": true,
    "reason": null,
    "summary": "本人本周期剩余 3 次(机构总剩 832 次)"
  },
  "success": true
}

通过场景 B用户级不限制userCallLimit=0

{
  "data": {
    "orgName": "XX 幼儿园",
    "quotaLimit": 1000,
    "quotaRemaining": 832,
    "userCallLimit": 0,
    "userCallRemaining": -1,
    "canCreate": true,
    "reason": null,
    "summary": "可继续创作,剩余 832 次"
  }
}

拒绝:机构不存在

{
  "data": {
    "orgName": null,
    "quotaLimit": 0,
    "quotaRemaining": 0,
    "userCallLimit": 0,
    "userCallRemaining": -1,
    "canCreate": false,
    "reason": "机构不存在",
    "summary": "机构「LESINGLE999999999」不存在请联系管理员核对机构 ID"
  }
}

拒绝:机构未授权

{
  "data": {
    "orgName": "XX 幼儿园",
    "quotaLimit": 0,
    "quotaRemaining": 0,
    "userCallLimit": 0,
    "userCallRemaining": -1,
    "canCreate": false,
    "reason": "机构「XX 幼儿园」未授权",
    "summary": "机构尚未授权,请联系管理员开通服务"
  }
}

拒绝:机构额度已耗尽

{
  "data": {
    "orgName": "XX 幼儿园",
    "quotaLimit": 1000,
    "quotaRemaining": 0,
    "userCallLimit": 5,
    "userCallRemaining": 2,
    "canCreate": false,
    "reason": "机构创作额度已耗尽(共 1000 次)",
    "summary": "机构创作额度已用完,请联系管理员补充额度"
  }
}

拒绝本用户本周期额度已用完V2.1 新增场景)

{
  "data": {
    "orgName": "XX 幼儿园",
    "quotaLimit": 1000,
    "quotaRemaining": 832,
    "userCallLimit": 5,
    "userCallRemaining": 0,
    "canCreate": false,
    "reason": "本用户本周期创作额度已用完(共 5 次/周期)",
    "summary": "您本周期创作次数已用完(共 5 次),请等待下个周期或联系管理员"
  }
}

这正是 V2.0 不返用户级字段时会被坑的场景:机构剩 832 次,用户却已用完个人 5 次额度。V2.0 错给 canCreate=trueV2.1 正确给 false


4. 前端使用建议(推荐流程)

🟢 核心原则:通过 / 失败 两种场景都必须把 vo.summary 主动展示给用户

summary 是服务端拼好的唯一信息源,前端不要自己拼词、不要丢弃。

  • 失败Toast / Dialog 拦截,不放行
  • 通过:同样 Toast / 顶部条幅 / 入口角标提示"还剩 X 次",让用户心里有数

不要把通过场景的 summary 默默吞掉——用户点"+"号是个心智决策点,必须告诉他"你还剩 3 次",不然下次进来才发现没了,体验断崖。

点击 "+" 创作入口
    ↓
弹"额度查询中..."转圈对话框(防止用户连点)
    ↓
GET /api/v1/quota/user?orgId=xxx&phone=登录手机号
    ↓
设置 3s 超时兜底(超时直接放行,后端创作提交时还会强校验)
    ↓
┌─ canCreate=true  → 关闭转圈, Toast(vo.summary) 提示"剩余 N 次", 进画画页
├─ canCreate=false → 关闭转圈, Dialog(vo.summary) 拦截, 不进入
├─ HTTP 异常       → 关闭转圈, 放行(兜底体验, 创作提交时再次校验)
└─ 超时 3s         → 关闭转圈, 放行(同上)

4.1 通过场景 — summary 直接 Toast推荐

// 不管通过还是失败, summary 都展示给用户
// 通过场景的 summary 长这样: "本人本周期剩余 3 次(机构总剩 832 次)"
//                          或 "可继续创作,剩余 832 次"(用户级不限时)
if (vo.canCreate) {
  Toast.show(vo.summary, { duration: 2000 })  // 2s 轻提示, 不阻塞进入创作页
  navigateToSketchBook()
} else {
  Dialog.alert(vo.summary, { title: '无法创作', confirmText: '我知道了' })
  // 不进入创作页
}

4.2 主入口角标 — 持续提示剩余次数(推荐 UI 增强)

// "+号" 创作入口旁边挂一个角标 / 副标题, 让用户随时看到额度
if (vo.canCreate) {
  if (vo.userCallLimit > 0 && vo.userCallRemaining >= 0) {
    // 有用户级上限:显示双数字, 让用户清楚个人剩余
    showBadge(`本人剩 ${vo.userCallRemaining}/${vo.userCallLimit}`)
    showSubtitle(`${vo.orgName} · 机构剩 ${vo.quotaRemaining} 次`)
  } else if (vo.quotaLimit > 0) {
    showBadge(`剩余 ${vo.quotaRemaining} 次`)
    showSubtitle(vo.orgName)
  } else {
    showSubtitle(vo.orgName)  // 没启用 quotaLimit 时不显示数字
  }
}

// 进度条颜色阈值(用户级优先, 因为用户感知最强)
let ratio
if (vo.userCallLimit > 0) {
  ratio = vo.userCallRemaining / vo.userCallLimit
} else if (vo.quotaLimit > 0) {
  ratio = vo.quotaRemaining / vo.quotaLimit
} else {
  ratio = 1
}
if (ratio > 0.3) color = 'green'
else if (ratio > 0.1) color = 'orange'
else color = 'red'

4.3 反例 — 不要这么做

// ❌ 反例 1通过场景默默放行, 用户不知道还剩多少
if (vo.canCreate) {
  navigateToSketchBook()  // 没提示, 体验断崖
}

// ❌ 反例 2自己拼词, 丢掉服务端 summary
if (vo.canCreate) {
  Toast.show(`还剩 ${vo.quotaRemaining} 次`)  // 漏掉用户级数字, 信息不全
}

// ❌ 反例 3只在 canCreate=false 时提示, 通过场景静默
// → 用户连续创作 4 次后, 第 5 次突然被拒, 完全没有预警

// ❌ 反例 4拿 reason 字段当用户文案展示
if (!vo.canCreate) {
  Dialog.alert(vo.reason)  // 错reason 是系统视角("本用户...")
                           // 应该用 summary("您的本周期创作次数已用完...")
}

4.4 reason vs summary 分工速查表(前端必读)

底层逻辑:服务端给两个字段是故意分工前端必须用对

  • reason = 系统视角,给运维 / 客服 / 日志 / 埋点用,不要给用户看
  • summary = 用户视角"您"开头的人称话术),直接展示给用户,前端不要拼词、不要加工
场景 canCreate reason(系统视角,日志用) summary(用户视角,直接展示) 前端展示方式
通过 + 用户级有上限 true null "本人本周期剩余 3 次(机构总剩 832 次)" Toast 2s + 入口角标
通过 + 用户级不限 true null "可继续创作,剩余 832 次" Toast 2s + 入口角标
机构不存在 false "机构不存在" "机构「LESINGLE999」不存在请联系管理员核对机构 ID" Dialog 拦截
机构未授权 false "机构「XX 幼儿园」未授权" "机构尚未授权,请联系管理员开通服务" Dialog 拦截
机构额度耗尽 false "机构创作额度已耗尽(共 1000 次)" "机构创作额度已用完,请联系管理员补充额度" Dialog 拦截
本用户额度用完 false "本用户本周期创作额度已用完(共 5 次/周期)" "您本周期创作次数已用完(共 5 次),请等待下个周期或联系管理员" Dialog 拦截

关键差异举例V2.1 新增的用户级耗尽场景):

reason  : "本用户本周期创作额度已用完(共 5 次/周期)"      ← 系统视角, 给客服查问题用
summary : "您本周期创作次数已用完(共 5 次),请等待下个周期或联系管理员"  ← 用户视角, 直接 Dialog

Owner 意识summary 文案、用词、人称、引导动作("请联系管理员"/"请等待下个周期"全部在服务端拼好,前端只负责"原文展示"。如果你想改文案 → 提需求让后端改 → 后端在 QuotaQueryController.buildPassSummary / 各闸门分支统一改 → 多端自动同步,单一信息源原则


5. 注意事项

  1. 不要带 HMAC 签名头:服务端 HmacAuthenticationFilter 已对此路径加白名单,带签名头不会报错但浪费计算。
  2. 额度仅含 A1/A2/A3A6 角色提取、A7 角色重建、视频生成有各自的额度池,本接口不反映这些。
  3. 三层兜底语义:机构不存在 / 机构未授权 / 机构空 / 用户空 任一不过即 canCreate=false
  4. phone 必须是真实登录用户:它是用户级 Redis key 的一部分,传错号码 = 查到错的人的额度。
  5. 没有真正的鉴权:任何人知道 orgId+phone 即可查到额度数字。如果未来要收紧,可改成需要 Bearer token接口语义不变
  6. 不修改任何状态:纯查询接口,重复调用幂等。
  7. 创作提交时仍会强校验:本接口返回 canCreate=true 不代表 100% 能创建成功A1/A2/A3 提交时会再次原子扣减额度(机构 + 用户两层都校验)。前端不应依赖此接口做"扣减预占"。
  8. userCallLimit=0 含义:服务端兜底未启用单用户上限——即同机构不限制单用户次数(早期未配置时默认值)。

6. 错误码

HTTP code 说明
200 200 业务正常(即使 canCreate=false 也是 200
400 40001 orgId 或 phone 缺失
500 50000 服务端异常(数据库不可达)

7. V2.0 → V2.1 迁移说明(前端适配)

V2.0 字段 V2.1 字段 处理
orgName orgName 不变 ✓
quotaLimit quotaLimit 不变 ✓
quotaRemaining quotaRemaining 不变 ✓
userCallLimit 新增本用户每周期创作上限0=不限)
userCallRemaining 新增:本用户本周期剩余次数(-1=不限)
canCreate canCreate 决策位语义升级(双闸:机构 ✓ + 用户 ✓)
reason reason 新增 1 类原因「本用户本周期创作额度已用完」
summary summary 通过场景拼词升级,含本人剩余次数

Android 端代码免改条件:原代码只用 isCanCreate() + getSummary() 两字段,可平滑兼容。 Android 端推荐适配:补显示 getUserCallRemaining(),让用户清楚个人剩余次数。


8. 服务端校验链路(运维参考)

GET /api/v1/quota/user?orgId=X&phone=P
    ↓
QuotaQueryController.getUserQuota
    ├─ 闸 1: orgMapper.selectOne(orgId)  → 机构存在?
    ├─ 闸 2: org.authorized == 1         → 已授权?
    ├─ 闸 3: org.quotaA - quotaAUsed > 0 → 机构有额度?
    └─ 闸 4: QuotaService.getUserCallRemaining(orgId, phone, periodId)
            ├─ resolveUserCallLimit(orgId)  // org.user_call_limit 优先, 兜底 t_config
            └─ Redis GET aicreate:quota:user:{orgId}:{phone}:{periodId}
                          → limit - used = remaining

用户级 Redis Key 格式aicreate:quota:user:{orgId}:{phone}:{periodId} TTL:跟随活动周期,不会无限增长


9. 变更历史

版本 日期 变更
V1.0 2026-04-18 首次发布12 字段)
V2.0 2026-04-18 简化为 6 字段,只保留 A1/A2/A3 主创作额度reason 改自然语言
V2.1 2026-04-18 补回 userCallLimit/userCallRemainingV2.0 误删canCreate 升级双闸,对齐后端真实校验链路