16 KiB
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_limit 或 t_config.user_call_limit 调整)。V2.0 接口不返用户级数据 → 前端拿 canCreate=true 进创作页 → 真正的 POST /creation/image-story 被 QUOTA_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 对象 UserQuotaVO(V2.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=true,V2.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. 注意事项
- 不要带 HMAC 签名头:服务端
HmacAuthenticationFilter已对此路径加白名单,带签名头不会报错但浪费计算。 - 额度仅含 A1/A2/A3:A6 角色提取、A7 角色重建、视频生成有各自的额度池,本接口不反映这些。
- 三层兜底语义:机构不存在 / 机构未授权 / 机构空 / 用户空 任一不过即
canCreate=false。 - phone 必须是真实登录用户:它是用户级 Redis key 的一部分,传错号码 = 查到错的人的额度。
- 没有真正的鉴权:任何人知道 orgId+phone 即可查到额度数字。如果未来要收紧,可改成需要 Bearer token,接口语义不变。
- 不修改任何状态:纯查询接口,重复调用幂等。
- 创作提交时仍会强校验:本接口返回
canCreate=true不代表 100% 能创建成功,A1/A2/A3 提交时会再次原子扣减额度(机构 + 用户两层都校验)。前端不应依赖此接口做"扣减预占"。 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/userCallRemaining(V2.0 误删),canCreate 升级双闸,对齐后端真实校验链路 |