把活层退役旧品牌「造梦AI」统一改为现行「绘境AI」,brand-invariant 门红线归零。构成: ~412 Java @author 署名 + game-studio/index.html 浏览器标题 + game-runtime/package.json 描述 + game-admin/.env VITE_APP_TITLE 运营后台标题 + contracts API title/@Schema 用户协议示例/events 描述 + docs-design mockup 品牌位 + 各模块注释。全是注释/显示/元数据,零逻辑改动。 zaomeng 拼音持久化标识符按 2026-06-24 评审 GR-05 不动(347 处保留)。 白名单(docs/ip 法律备案 / _archive / 带日期留痕)与 AGENTS.md 门定义自身不动。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
557 lines
36 KiB
YAML
557 lines
36 KiB
YAML
openapi: 3.0.3
|
||
# 契约 #1 API | 模块:trade(game-module-trade,Wave2 变现域「分账/结算/提现」)| owner:WS5 主笔,全员 review
|
||
# 职责(架构 Doc B):把多源收入按规则分账、归集、结算、对账;钱包打款/提现门槛与打款执行属 trade(不属 pay)。不做支付收单(归 pay)。
|
||
# MVP 钱财闭环(决策 D3):广告收入 → trade 分账(创作者得 80%)→ 创作者收益账户 → 提现(满 5 元)。会员订阅/支付收单=P1,本轮不做。
|
||
# 配置化(决策 D3,全配置走 Nacos/DB,严禁硬编码):
|
||
# - trade.creator-share = 0.80(MVP 全档统一 80% 分账;分层结构 creator_share_tier 保留、值后期填,见 V7.0.0 注释)
|
||
# - trade.withdraw-min = 500(单位:分,满 5 元可提现)
|
||
# 上述为 Nacos 配置项,本契约不建死值;接口内 ≥withdraw-min 校验读 Nacos。
|
||
# 上游 Feign 依赖:消费 ad 模块 AdRevenueApi(com.wanxiang.huijing.game.module.ad.api.AdRevenueApi)——
|
||
# getUnsettledRevenue(statDate) 拉未结算广告收入、markSettled(revenueIds) 回标已结算(seam 契约详见 ad.yaml x-feign-contracts)。
|
||
# trade 侧 T+1 SettlementJob 据此分账入账;跨模块只依赖 ad 的 -api,禁依赖 ad 的 -server。
|
||
# 端:/app-api(产品端 game-studio,创作者,用户 Token + DataPermission 只见自己) /admin-api(管理端 game-admin,RBAC);前缀由 huijing 框架按 controller.app/admin 包名自动添加
|
||
# 错误码段:trade = 1-106-***-***(本模块独占,禁止与他模块重叠:100 project/101 aigc/102 runtime/103 feed/104 telemetry/111 ad)
|
||
# 响应统一 Huijing CommonResult 信封:{ code, data, msg };code=0 成功
|
||
# 资金严谨:金额一律用「分」(BIGINT),禁浮点;比例用 DECIMAL;所有资金写入有幂等键(biz_no/source_ref + uk);状态机用 tinyint,非法流转服务端拒绝。
|
||
# 与契约#5 对齐:不重造 income_settled 事件(那是 telemetry 分析埋点 props:amount/source);本模块落账走 game_trade_income,income_settled 仅为埋点镜像。
|
||
info:
|
||
title: 绘境AI trade 模块 API
|
||
version: 1.0.0
|
||
description: >-
|
||
分账/结算/提现域。消费上游 ad 未结算广告收入 → 按 creator_share(Nacos 0.80) T+1 分账入创作者收益账户 →
|
||
创作者发起提现(满 withdraw-min 分)→ 管理端审核 → mock 打款。
|
||
收益账户为流水物化汇总(余额变更必有流水);MVP 全 mock 打款,状态机/幂等/对账为真实接入留位。
|
||
前端据此 vite-plugin-mock 自动生成 mock。
|
||
|
||
servers:
|
||
- url: http://localhost:48080
|
||
description: 本地(Swagger/Knife4j http://localhost:48080/doc.html)
|
||
|
||
paths:
|
||
# ===========================================================================
|
||
# 产品端 /app-api(创作者,用户 Token + DataPermission:只见自己账户/流水/提现)
|
||
# ===========================================================================
|
||
/app-api/trade/account/mine:
|
||
get:
|
||
tags: [app-trade]
|
||
summary: 我的收益账户(T-TRD-01;余额/累计收益/累计提现/冻结中,金额单位分)
|
||
description: >-
|
||
返回当前创作者收益账户(game_trade_account,DataPermission 限本人 user_id)。
|
||
账户不存在时返回零值账户(首次有收益或首次查询时由服务端按需初始化,余额全 0)。
|
||
金额字段一律单位「分」。
|
||
responses:
|
||
'200':
|
||
description: 成功
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultTradeAccount' }
|
||
|
||
/app-api/trade/income/page:
|
||
get:
|
||
tags: [app-trade]
|
||
summary: 我的收益流水分页(T-TRD-02;source=ad/tip、gross/net/share_rate、结算日)
|
||
description: 返回当前创作者收益流水(game_trade_income,DataPermission 限本人)。供创作者查看每笔分账明细与对账。
|
||
parameters:
|
||
- { name: source, in: query, required: false, schema: { type: integer, enum: [1, 2] }, description: '收入来源过滤:1广告 2打赏' }
|
||
- { name: settleDateStart, in: query, required: false, schema: { type: string, format: date }, description: 结算日起(yyyy-MM-dd) }
|
||
- { name: settleDateEnd, in: query, required: false, schema: { type: string, format: date }, description: 结算日止(yyyy-MM-dd) }
|
||
- { name: pageNo, in: query, required: false, schema: { type: integer, default: 1 } }
|
||
- { name: pageSize, in: query, required: false, schema: { type: integer, default: 10 } }
|
||
responses:
|
||
'200':
|
||
description: 成功
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultIncomePage' }
|
||
|
||
/app-api/trade/my/income-summary:
|
||
get:
|
||
tags: [app-trade]
|
||
summary: 我的收益汇总(T-TRD-09;看板「收益」转真,聚合已结算收入,金额单位分)
|
||
description: >-
|
||
返回当前创作者的收益汇总(聚合 game_trade_income,DataPermission 限本人 user_id=getLoginUserId())。
|
||
供产品端创作者看板「收益」卡片由桩转真:累计实得净额(Σnet_amount)、累计原始收入(Σgross_amount)、
|
||
累计入账笔数;不含未结算广告收入(那在 ad 台账 settle_status=0,未分账入账即不计)。
|
||
归属隔离硬边界:聚合谓词强制 user_id=当前登录创作者,禁前端传入 userId。
|
||
responses:
|
||
'200':
|
||
description: 成功
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultIncomeSummary' }
|
||
|
||
/app-api/trade/withdraw/apply:
|
||
post:
|
||
tags: [app-trade]
|
||
summary: 发起提现(T-TRD-03;幂等键=bizNo,校验 ≥withdraw-min 且 ≤可提现余额,冻结余额)
|
||
description: >-
|
||
创作者发起提现申请。服务端校验:
|
||
1) amount ≥ Nacos trade.withdraw-min(满 5 元=500 分,否则 1-106-002-001 未达提现门槛);
|
||
2) amount ≤ game_trade_account.balance 可提现余额(否则 1-106-002-002 余额不足);
|
||
3) bizNo 幂等(对应 game_trade_withdraw.uk_biz_no,重复提交同一 bizNo 返回已有申请、不重复冻结,1-106-002-003 为非幂等的并发冲突时抛)。
|
||
校验通过:原子地从 balance 扣减并加到 frozen(冻结中=提现处理中),落 game_trade_withdraw(status=0 待审核),返回提现单 ID。
|
||
余额变更必有流水/可追溯:冻结即提现申请记录本身,打款/驳回时再调整 frozen 与 total_withdraw。
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/WithdrawApplyReqVO' }
|
||
responses:
|
||
'200':
|
||
description: 返回新建提现申请 ID
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultLong' }
|
||
|
||
/app-api/trade/withdraw/page:
|
||
get:
|
||
tags: [app-trade]
|
||
summary: 我的提现记录分页(T-TRD-04;状态机/金额/审核驳回理由)
|
||
description: 返回当前创作者提现申请记录(game_trade_withdraw,DataPermission 限本人)。
|
||
parameters:
|
||
- { name: status, in: query, required: false, schema: { type: integer, enum: [0, 1, 2, 3, 4] }, description: '提现状态过滤(见 status 状态机)' }
|
||
- { name: pageNo, in: query, required: false, schema: { type: integer, default: 1 } }
|
||
- { name: pageSize, in: query, required: false, schema: { type: integer, default: 10 } }
|
||
responses:
|
||
'200':
|
||
description: 成功
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultWithdrawPage' }
|
||
|
||
# ===========================================================================
|
||
# 管理端 /admin-api(运营/管理员 RBAC):提现审核 + 收益流水查询 + 平台营收报表
|
||
# ===========================================================================
|
||
/admin-api/trade/withdraw/page:
|
||
get:
|
||
tags: [admin-trade]
|
||
summary: 提现审核队列分页(T-TRD-05;跨创作者,按状态/用户筛选)
|
||
description: 管理端查看提现申请(默认取 status=0 待审核),用于审核处理。
|
||
parameters:
|
||
- { name: status, in: query, required: false, schema: { type: integer, enum: [0, 1, 2, 3, 4] }, description: '默认取 status=0 待审核' }
|
||
- { name: userId, in: query, required: false, schema: { type: integer, format: int64 }, description: 按创作者用户 ID 筛选 }
|
||
- { name: pageNo, in: query, required: false, schema: { type: integer, default: 1 } }
|
||
- { name: pageSize, in: query, required: false, schema: { type: integer, default: 10 } }
|
||
responses:
|
||
'200':
|
||
description: 成功
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultWithdrawPage' }
|
||
|
||
/admin-api/trade/withdraw/audit:
|
||
post:
|
||
tags: [admin-trade]
|
||
summary: 提现审核(T-TRD-06;通过/驳回,驱动状态机;非法流转服务端拒绝)
|
||
description: >-
|
||
管理端审核提现申请,驱动状态机(合法流转见 components.x-state-machines):
|
||
- 通过(decision=1):status 0→1(审核通过打款中),随即经打款渠道(Nacos trade.payout-channel)发起转账。
|
||
【M4 真实化】转账「发起」与「终态」解耦:审核事务内仅置 1(打款中)并发起转账单(异步),
|
||
资金终态由打款回调驱动——回调成功 1→2(frozen→total_withdraw)、失败 1→4(打款失败,frozen 退回 balance)。
|
||
渠道=mock 时同步直通:发起即视为成功,但仍走 status CAS(1→2)置终态,不抄近路直接置 2。
|
||
渠道=wxpay/alipay 时委托 pay 模块 PayTransferApi.createTransfer 发起企业付款,等 pay 异步 notify 回调驱动终态。
|
||
红线:真实渠道注入失败时 fail-fast 报错挂起提现单(status 留 1),严禁静默降级到 mock 假打款吞真钱。
|
||
- 驳回(decision=2):status 0→3(驳回),把冻结金额 frozen 退回 balance(资金原路退回,必填 rejectReason)。
|
||
仅 status=0 待审核可审核;非法流转(如已打款再审核)服务端拒绝(1-106-003-001 非法状态流转)。
|
||
幂等:以提现单 id + 当前 status 乐观校验,重复审核同一单不二次发钱/二次退款。
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/WithdrawAuditReqVO' }
|
||
responses:
|
||
'200':
|
||
description: 成功
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultBoolean' }
|
||
|
||
/admin-api/trade/withdraw/payout-notify:
|
||
post:
|
||
tags: [admin-trade]
|
||
summary: 打款回调(T-TRD-10;驱动提现终态 1→2/1→4;服务端内部回调,非前端入口)
|
||
description: >-
|
||
【M4 真实化】打款异步终态回调入口。打款渠道(mock 同步直通 / wxpay/alipay 经 pay notify)完成后调用本端点驱动终态:
|
||
- success=true:status CAS 1→2(已打款),账户 frozen→total_withdraw(settlePaid);
|
||
- success=false:status CAS 1→4(打款失败),账户 frozen→balance(refundFrozen,可由创作者重新发起新单)。
|
||
幂等:以提现单 id + 当前 status=1 乐观 CAS(重复回调命中 0 行即跳过,不二次发钱/二次退款);
|
||
透传 transferRef(pay 转账单号)落库供对账。仅服务端内部/可信边界调用(mock 渠道本进程直调、真实渠道由 pay notify 转发),不暴露给 app 端。
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/WithdrawPayoutNotifyReqVO' }
|
||
responses:
|
||
'200':
|
||
description: 成功(含幂等命中——重复回调返回 true 不重复处理)
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultBoolean' }
|
||
|
||
/admin-api/trade/income/page:
|
||
get:
|
||
tags: [admin-trade]
|
||
summary: 收益流水查询分页(T-TRD-07;跨创作者对账,按来源/创作者/结算日筛选,只读)
|
||
description: >-
|
||
管理端查看全量收益流水(game_trade_income),供平台对账。
|
||
对账锚点:source_ref(广告收入=game_ad_revenue.id)+ trace_id 与 ad 侧台账对齐。
|
||
parameters:
|
||
- { name: userId, in: query, required: false, schema: { type: integer, format: int64 }, description: 按创作者用户 ID 筛选 }
|
||
- { name: source, in: query, required: false, schema: { type: integer, enum: [1, 2] }, description: '收入来源:1广告 2打赏' }
|
||
- { name: settleDate, in: query, required: false, schema: { type: string, format: date }, description: 结算日(yyyy-MM-dd)筛选 }
|
||
- { name: pageNo, in: query, required: false, schema: { type: integer, default: 1 } }
|
||
- { name: pageSize, in: query, required: false, schema: { type: integer, default: 10 } }
|
||
responses:
|
||
'200':
|
||
description: 成功
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultIncomePage' }
|
||
|
||
/admin-api/trade/report/revenue:
|
||
get:
|
||
tags: [admin-trade]
|
||
summary: 平台营收报表聚合(T-TRD-08;按日聚合 gross/net/平台分成/提现,只读)
|
||
description: >-
|
||
平台营收报表(只读聚合):按结算日聚合收益流水的原始收入(gross)、创作者实得(net)、平台分成(gross-net),
|
||
及提现申请的打款总额。供运营看平台收入与分账结构。
|
||
parameters:
|
||
- { name: statDateStart, in: query, required: true, schema: { type: string, format: date }, description: 统计起始日(yyyy-MM-dd) }
|
||
- { name: statDateEnd, in: query, required: true, schema: { type: string, format: date }, description: 统计结束日(yyyy-MM-dd) }
|
||
responses:
|
||
'200':
|
||
description: 成功
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultRevenueReport' }
|
||
|
||
# ===========================================================================
|
||
# U2 经济:admin 赋余额 / admin 赋订阅(admin 端,RBAC)
|
||
# ===========================================================================
|
||
/admin-api/trade/account/grant:
|
||
post:
|
||
tags: [admin-trade]
|
||
summary: admin 赋余额(U2;运营手动给创作者账户充值,real 入账+流水可对账)
|
||
description: >-
|
||
运营手动给创作者收益账户赋余额(活动奖励/客诉补偿等)。
|
||
- 幂等:以 bizNo 命中 game_trade_grant.uk_biz_no 去重——同一 bizNo 重复提交返回已有流水 ID(不重复入账、不重复发钱);
|
||
- 流水佐证:赋余额「不」走 game_trade_income(避免污染营收报表 gross/net 聚合),单独记 game_trade_grant 流水可对账;
|
||
- 账户:余额原子增(balance += amount、total_income += amount,维持不变式 balance+frozen+total_withdraw=total_income);
|
||
- 操作人:取自 token(getLoginUserId()),落 game_trade_grant.operator_user_id(审计可溯,非前端入参);
|
||
- 校验:amount 必须 > 0(赋余额只增不减,1-106-005-000)。
|
||
RBAC:@ss.hasPermission('trade:account:grant')。返回赋余额流水 ID。
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/GrantBalanceReqVO' }
|
||
responses:
|
||
'200':
|
||
description: 成功(含幂等命中——重复 bizNo 返回已有流水 ID)
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultLong' }
|
||
|
||
/admin-api/trade/subscription/grant:
|
||
post:
|
||
tags: [admin-trade]
|
||
summary: admin 赋订阅(U2;运营/B 端手动开通会员,三档套餐 + 续期叠加)
|
||
description: >-
|
||
运营/B 端手动给用户开通会员订阅(MVP 不接真实支付收单,归 pay 日历闸门;本轮 admin 手动赋)。
|
||
- 套餐 plan:1 月度 / 2 季度 / 3 年度(仅标识权益档位,到期时间由 durationDays 决定;非法 1-106-006-000);
|
||
- 续期叠加:已有未过期订阅则新到期 = max(now, 旧 expire) + durationDays 天(不丢未用时长);已过期/无记录从 now 起算;
|
||
- 幂等:以 bizNo 内联在订阅行 last_grant_biz_no——同一 bizNo 重复提交不重复延长时长(重试安全);
|
||
- 一用户一行(game_trade_subscription.uk_user),upsert 同一行(CAS 防并发续期 lost-update);
|
||
- 操作人取自 token,落 operator_user_id(审计可溯);durationDays 必须 > 0(1-106-006-001)。
|
||
RBAC:@ss.hasPermission('trade:subscription:grant')。返回赋值后到期时间(expireTime)。
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/GrantSubscriptionReqVO' }
|
||
responses:
|
||
'200':
|
||
description: 成功(返回最新到期时间;同 bizNo 幂等返回当前到期时间不重复延长)
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultLocalDateTime' }
|
||
|
||
# ===========================================================================
|
||
# U2 经济:C 端查订阅态(app 端,用户 Token,归属隔离)
|
||
# ===========================================================================
|
||
/app-api/trade/subscription/mine:
|
||
get:
|
||
tags: [app-trade]
|
||
summary: 我的会员订阅态(U2;是否有效/套餐/到期时间)
|
||
description: >-
|
||
返回当前用户会员订阅态(game_trade_subscription,DataPermission 限本人 user_id)。
|
||
有效性以 expire_time>now 实时判定(active=true 即会员权益开启,effectiveStatus=0 生效中/1 已过期)——
|
||
不依赖 cron 物化过期也能正确反映过期态。从未订阅时返回空态(subscribed=false、active=false),不返 null。
|
||
responses:
|
||
'200':
|
||
description: 成功
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CommonResultSubscription' }
|
||
|
||
components:
|
||
schemas:
|
||
# ---- Huijing CommonResult 信封 ----
|
||
CommonResultBoolean:
|
||
type: object
|
||
properties:
|
||
code: { type: integer, description: '0=成功,非0=错误码 1-106-***-***' }
|
||
data: { type: boolean }
|
||
msg: { type: string }
|
||
CommonResultLong:
|
||
type: object
|
||
properties:
|
||
code: { type: integer }
|
||
data: { type: integer, format: int64, description: 新建提现申请 ID }
|
||
msg: { type: string }
|
||
CommonResultTradeAccount:
|
||
type: object
|
||
properties:
|
||
code: { type: integer }
|
||
data: { $ref: '#/components/schemas/TradeAccountRespVO' }
|
||
msg: { type: string }
|
||
CommonResultIncomePage:
|
||
type: object
|
||
properties:
|
||
code: { type: integer }
|
||
data:
|
||
type: object
|
||
properties:
|
||
list: { type: array, items: { $ref: '#/components/schemas/IncomeRespVO' } }
|
||
total: { type: integer, format: int64 }
|
||
msg: { type: string }
|
||
CommonResultWithdrawPage:
|
||
type: object
|
||
properties:
|
||
code: { type: integer }
|
||
data:
|
||
type: object
|
||
properties:
|
||
list: { type: array, items: { $ref: '#/components/schemas/WithdrawRespVO' } }
|
||
total: { type: integer, format: int64 }
|
||
msg: { type: string }
|
||
CommonResultRevenueReport:
|
||
type: object
|
||
properties:
|
||
code: { type: integer }
|
||
data: { $ref: '#/components/schemas/RevenueReportVO' }
|
||
msg: { type: string }
|
||
CommonResultLocalDateTime:
|
||
type: object
|
||
properties:
|
||
code: { type: integer }
|
||
data: { type: string, format: date-time, description: '赋订阅后到期时间(expireTime)' }
|
||
msg: { type: string }
|
||
CommonResultSubscription:
|
||
type: object
|
||
properties:
|
||
code: { type: integer }
|
||
data: { $ref: '#/components/schemas/SubscriptionRespVO' }
|
||
msg: { type: string }
|
||
|
||
# ---- 请求 VO ----
|
||
WithdrawApplyReqVO:
|
||
type: object
|
||
description: 发起提现入参。bizNo 为幂等键,amount 单位分。
|
||
required: [amount, bizNo]
|
||
properties:
|
||
amount: { type: integer, format: int64, minimum: 1, description: '提现金额(单位:分)。服务端校验 ≥Nacos trade.withdraw-min(500=5元) 且 ≤可提现余额 balance' }
|
||
bizNo: { type: string, minLength: 1, description: '提现业务单号 = 幂等键(前端生成唯一,对应 game_trade_withdraw.uk_biz_no;重复提交返回已有申请、不重复冻结)' }
|
||
remark: { type: string, maxLength: 255, description: 备注(可空) }
|
||
WithdrawAuditReqVO:
|
||
type: object
|
||
description: 提现审核入参(管理端)。decision 驱动状态机;驳回必填 rejectReason。
|
||
required: [id, decision]
|
||
properties:
|
||
id: { type: integer, format: int64, description: 提现申请 ID }
|
||
decision: { type: integer, enum: [1, 2], description: '审核决策:1通过(0→1→mock 打款 2) 2驳回(0→3,冻结退回余额)' }
|
||
rejectReason: { type: string, maxLength: 255, description: '驳回理由(decision=2 必填,回填创作者)' }
|
||
remark: { type: string, maxLength: 255, description: 审核备注(可空) }
|
||
GrantBalanceReqVO:
|
||
type: object
|
||
description: admin 赋余额入参(U2,管理端)。bizNo 幂等键;amount 单位分、只增不减。操作人取自 token,非入参。
|
||
required: [userId, amount, bizNo]
|
||
properties:
|
||
userId: { type: integer, format: int64, description: 被赋余额的创作者用户 ID }
|
||
amount: { type: integer, format: int64, minimum: 1, description: '赋余额金额(单位:分,只增不减,>0)' }
|
||
bizNo: { type: string, minLength: 1, description: '赋余额业务单号 = 幂等键(调用端生成唯一,对应 game_trade_grant.uk_biz_no;重复提交返回已有流水、不重复入账)' }
|
||
remark: { type: string, maxLength: 255, description: '赋余额备注(赋值原因,可空)' }
|
||
GrantSubscriptionReqVO:
|
||
type: object
|
||
description: admin 赋订阅入参(U2,管理端)。bizNo 幂等键(同 bizNo 不重复延长);plan 套餐;durationDays 续期叠加。操作人取自 token,非入参。
|
||
required: [userId, plan, durationDays, bizNo]
|
||
properties:
|
||
userId: { type: integer, format: int64, description: 订阅用户 ID }
|
||
plan: { type: integer, enum: [1, 2, 3], description: '订阅套餐:1 月度 / 2 季度 / 3 年度' }
|
||
durationDays: { type: integer, minimum: 1, description: '赋订阅时长(天,续期叠加,>0)' }
|
||
bizNo: { type: string, minLength: 1, description: '赋订阅业务单号 = 幂等键(调用端生成唯一;同 bizNo 不重复延长时长,重试安全)' }
|
||
remark: { type: string, maxLength: 255, description: '订阅备注(开通原因,可空)' }
|
||
|
||
# ---- 响应 VO ----
|
||
TradeAccountRespVO:
|
||
type: object
|
||
description: >-
|
||
创作者收益账户(game_trade_account 物化汇总)。金额一律单位「分」。
|
||
约束:balance/frozen/total_income/total_withdraw 由分账入账与提现流程维护,余额变更必有对应流水/单据。
|
||
properties:
|
||
userId: { type: integer, format: int64, description: 创作者用户 ID }
|
||
balance: { type: integer, format: int64, description: '可提现余额(单位:分)' }
|
||
frozen: { type: integer, format: int64, description: '冻结中(提现处理中占用,单位:分)' }
|
||
totalIncome: { type: integer, format: int64, description: '累计入账(= 分账入账 + admin 赋余额之和,单位:分;U2 起含 admin 赋余额,维持 balance+frozen+total_withdraw 恒等式)' }
|
||
totalWithdraw: { type: integer, format: int64, description: '累计已提现(已打款成功总额,单位:分)' }
|
||
updateTime: { type: string, format: date-time }
|
||
IncomeRespVO:
|
||
type: object
|
||
description: >-
|
||
收益流水(game_trade_income,分账后入账)。net = gross × shareRate(创作者实得)。
|
||
对账锚点:source + sourceRef 唯一(广告=ad + game_ad_revenue.id),与 ad 台账对齐。
|
||
properties:
|
||
id: { type: integer, format: int64, description: 收益流水 ID }
|
||
userId: { type: integer, format: int64, description: 创作者用户 ID }
|
||
source: { type: integer, enum: [1, 2], description: '收入来源:1广告(ad) 2打赏(tip)' }
|
||
sourceRef: { type: string, description: '来源单号(对账锚点):广告=game_ad_revenue.id;打赏=打赏单 id。与 source 组成唯一键防重复分账' }
|
||
grossAmount: { type: integer, format: int64, description: '原始收入(分账前,单位:分)' }
|
||
shareRate: { type: number, format: double, description: '分账比例(DECIMAL,如 0.8000=创作者得 80%;来自 Nacos trade.creator-share 快照)' }
|
||
netAmount: { type: integer, format: int64, description: '创作者实得 = gross × shareRate(单位:分;落账时定格,不随配置变动)' }
|
||
settleDate: { type: string, format: date, description: 'T+1 结算日(yyyy-MM-dd)' }
|
||
traceId: { type: string, description: '全链路 traceId(广告来源透传自 game_ad_revenue.trace_id,供对账)' }
|
||
createTime: { type: string, format: date-time }
|
||
WithdrawRespVO:
|
||
type: object
|
||
description: 提现申请(game_trade_withdraw)。status 状态机见 x-state-machines;金额单位分。
|
||
properties:
|
||
id: { type: integer, format: int64, description: 提现申请 ID }
|
||
userId: { type: integer, format: int64, description: 创作者用户 ID }
|
||
amount: { type: integer, format: int64, description: '提现金额(单位:分)' }
|
||
status: { type: integer, enum: [0, 1, 2, 3, 4], description: '状态机:0待审核 1审核通过打款中 2已打款 3驳回 4打款失败' }
|
||
bizNo: { type: string, description: '提现业务单号(幂等键)' }
|
||
channel: { type: string, description: '打款渠道(MVP=mock;真实 later 接 pay 渠道)' }
|
||
applyTime: { type: string, format: date-time, description: 申请时间 }
|
||
auditTime: { type: string, format: date-time, description: 审核时间(审核后写入) }
|
||
auditUserId: { type: integer, format: int64, description: 审核人用户 ID(管理端) }
|
||
payTime: { type: string, format: date-time, description: 打款时间(打款成功后写入) }
|
||
rejectReason: { type: string, description: 驳回理由(status=3 时) }
|
||
remark: { type: string, description: 备注 }
|
||
transferRef: { type: string, description: '打款转账单号(M4:渠道发起转账后回填,对账锚点;mock 渠道为本地 mock 单号)' }
|
||
RevenueReportVO:
|
||
type: object
|
||
description: 平台营收报表聚合(按统计区间汇总;金额单位分)
|
||
properties:
|
||
statDateStart: { type: string, format: date, description: 统计起始日 }
|
||
statDateEnd: { type: string, format: date, description: 统计结束日 }
|
||
totalGross: { type: integer, format: int64, description: '区间内原始收入合计(分账前,单位:分)' }
|
||
totalNet: { type: integer, format: int64, description: '区间内创作者实得合计(单位:分)' }
|
||
totalPlatform: { type: integer, format: int64, description: '区间内平台分成合计 = totalGross - totalNet(单位:分)' }
|
||
totalWithdrawPaid: { type: integer, format: int64, description: '区间内已打款提现合计(单位:分)' }
|
||
# ---- U2 新增:会员订阅态(C 端查) ----
|
||
SubscriptionRespVO:
|
||
type: object
|
||
description: >-
|
||
会员订阅态(game_trade_subscription)。有效性以 expire_time>now 实时判定(active=true 即会员权益开启)——
|
||
不依赖 cron 物化过期。从未订阅时返回空态(subscribed=false、active=false),其余字段为 null。
|
||
properties:
|
||
subscribed: { type: boolean, description: '是否曾订阅过(有订阅记录):false=从未订阅' }
|
||
active: { type: boolean, description: '是否当前有效(expire_time>now 实时判定):C 端据此开启会员权益' }
|
||
plan: { type: integer, enum: [1, 2, 3], description: '订阅套餐:1 月度 / 2 季度 / 3 年度(未订阅为 null)' }
|
||
effectiveStatus: { type: integer, enum: [0, 1], description: '有效状态(按 expire_time 实时回算):0 生效中 / 1 已过期(未订阅为 null)' }
|
||
startTime: { type: string, format: date-time, description: '本次订阅生效起始时间(未订阅为 null)' }
|
||
expireTime: { type: string, format: date-time, description: '到期时间(权威字段:expire_time>now 即有效;未订阅为 null)' }
|
||
# ---- M4 新增:我的收益汇总(T-TRD-09 看板转真) ----
|
||
IncomeSummaryVO:
|
||
type: object
|
||
description: 创作者收益汇总(聚合 game_trade_income 本人已结算收入;金额单位分)
|
||
properties:
|
||
totalNet: { type: integer, format: int64, description: '累计创作者实得净额(Σnet_amount,单位:分)' }
|
||
totalGross: { type: integer, format: int64, description: '累计原始收入(Σgross_amount,单位:分)' }
|
||
incomeCount: { type: integer, format: int64, description: '累计入账笔数(已结算分账流水条数)' }
|
||
CommonResultIncomeSummary:
|
||
type: object
|
||
properties:
|
||
code: { type: integer }
|
||
data: { $ref: '#/components/schemas/IncomeSummaryVO' }
|
||
msg: { type: string }
|
||
# ---- M4 新增:打款回调入参(T-TRD-10 驱动提现终态 1→2/1→4) ----
|
||
WithdrawPayoutNotifyReqVO:
|
||
type: object
|
||
description: 打款回调入参(服务端内部/可信边界调用;驱动提现终态)
|
||
required: [withdrawId, success]
|
||
properties:
|
||
withdrawId: { type: integer, format: int64, description: '提现单 ID(game_trade_withdraw.id)' }
|
||
success: { type: boolean, description: 'true=打款成功(1→2);false=打款失败(1→4 退回 balance)' }
|
||
transferRef: { type: string, description: '渠道转账单号(对账锚点;mock 渠道为本地 mock 单号,可空)' }
|
||
failReason: { type: string, description: '打款失败原因(success=false 时回填,可空)' }
|
||
|
||
# ===========================================================================
|
||
# 状态机契约(合法流转;非法流转由 Service 服务端拒绝,DO 层不承载校验)
|
||
# ===========================================================================
|
||
x-state-machines:
|
||
description: trade 模块资金状态机定义,供两侧 agent 实现一致;非法流转一律服务端拒绝(1-106-003-001)。
|
||
withdraw:
|
||
field: 'game_trade_withdraw.status'
|
||
states:
|
||
'0': 待审核(apply 落库初始态;冻结金额已从 balance 转入 frozen)
|
||
'1': 审核通过打款中(审核通过 0→1 后发起转账;mock 渠道同步直通经 CAS 置 2,真实渠道等待 pay 异步回调驱动终态)
|
||
'2': 已打款(终态成功;1→2:frozen 扣减、total_withdraw 累加)
|
||
'3': 驳回(终态;0→3:frozen 退回 balance,资金原路退回)
|
||
'4': 打款失败(终态;1→4:frozen 退回 balance,可由创作者重新发起新单)
|
||
legalTransitions:
|
||
- '0 → 1(审核通过)'
|
||
- '0 → 3(审核驳回,frozen→balance)'
|
||
- '1 → 2(打款成功,frozen→total_withdraw)'
|
||
- '1 → 4(打款失败,frozen→balance)'
|
||
illegalExamples:
|
||
- '2 → * (已打款为终态,不可再变)'
|
||
- '3 → * / 4 → * (驳回/失败为终态)'
|
||
- '0 → 2(未经审核直接打款,拒绝)'
|
||
adRevenueSettle:
|
||
field: 'game_ad_revenue.settle_status(ad 侧表,trade 经 AdRevenueApi.markSettled 驱动)'
|
||
states:
|
||
'0': 未结算(ad 计费落台账初始态)
|
||
'1': 已结算(trade 分账入账成功后经 markSettled 回标)
|
||
legalTransitions:
|
||
- '0 → 1(trade 分账成功回标,幂等:已是 1 跳过)'
|
||
note: '该状态位权威表在 ad 模块 game_ad_revenue,trade 只经 Feign markSettled 驱动,不直接写 ad 表'
|
||
|
||
# ===========================================================================
|
||
# 上游 Feign 依赖(消费 ad/community/pay 模块;跨模块只依赖对方 -api 走 Feign)
|
||
# ===========================================================================
|
||
x-feign-dependencies:
|
||
description: trade 消费其它模块 -api 的 Feign 契约。权威签名见各模块 -api,此处仅声明依赖与用法;跨模块禁依赖对方 -server。
|
||
dependsOn:
|
||
# ① ad:T+1 分账结算(权威 seam 在 ad.yaml x-feign-contracts)
|
||
- module: ad
|
||
apiInterface: 'com.wanxiang.huijing.game.module.ad.api.AdRevenueApi'
|
||
operations:
|
||
- 'getUnsettledRevenue(LocalDate statDate) → List<AdRevenueRespDTO>:拉指定归集日未结算广告收入'
|
||
- 'markSettled(List<Long> revenueIds) → boolean:分账入账成功后回标 ad 台账 settle_status=1(幂等)'
|
||
usedBy: 'com.wanxiang.huijing.game.module.trade.job.SettlementJob(T+1 结算定时任务)'
|
||
flow: >-
|
||
SettlementJob:getUnsettledRevenue(T 日) → 按 creatorUserId 聚合 gross → ×Nacos trade.creator-share(0.80) 得 net →
|
||
写 game_trade_income(uk(source, source_ref) 防重) + 累加 game_trade_account.balance → markSettled 回标 ad。
|
||
全链路 trace_id(ad→trade) + source_ref(ad.id→trade) 可对账。
|
||
补偿:入账成功但 markSettled 失败时,下轮重拉但被 trade uk 拦截重复入账,仅重试回标,不重复发钱。
|
||
# ② community:reward 现金发放衔接(权威 seam 在 community.yaml x-feign-contracts;M4 新增)
|
||
- module: community
|
||
apiInterface: 'com.wanxiang.huijing.game.module.community.api.CommunityNotifyApi'
|
||
operations:
|
||
- 'listPendingCashRewards() → List<RewardGrantDTO>:拉 status=0 且 reward_type=2(现金) 的待发放奖励'
|
||
- 'markRewardGranted(Long rewardId, String grantedRef) → boolean:发放入账成功后回写 granted_ref + status 0→1(CAS 幂等)'
|
||
usedBy: 'com.wanxiang.huijing.game.module.trade.job.RewardPayoutJob(reward 现金发放,M4 新增)'
|
||
flow: >-
|
||
RewardPayoutJob:listPendingCashRewards() → 逐条 IncomeService.recordIncome(source=TIP=2, source_ref=reward.id, gross=reward_amount, shareRate=1.0)
|
||
入账到创作者账户 → markRewardGranted 回写 granted_ref(=income.id)+status 0→1。
|
||
幂等:recordIncome uk_source(2, reward.id) 兜底 + reward.status CAS(0→1),复跑不重复发钱;发放失败 reward 留 status=0 下轮重试。
|
||
# ③ pay:真实打款渠道(权威 seam 在 pay-api PayTransferApi;M4 接线,渠道=wxpay/alipay 时启用)
|
||
- module: pay
|
||
apiInterface: 'com.wanxiang.huijing.module.pay.api.transfer.PayTransferApi'
|
||
operations:
|
||
- 'createTransfer(PayTransferCreateReqDTO) → PayTransferCreateRespDTO:发起企业付款到零钱(异步,merchantTransferId=提现 bizNo 幂等)'
|
||
- 'getTransfer(Long id) → PayTransferRespDTO:查转账单状态(对账补偿 job 主动补驱动用)'
|
||
usedBy: 'com.wanxiang.huijing.game.module.trade.framework.payout.WxPayoutClient/AlipayPayoutClient(M4,仅 Nacos trade.payout-channel=wxpay/alipay 时;mock 渠道不依赖 pay)'
|
||
condition: '仅真实渠道启用;需 huijing-module-pay-server 接入 huijing-server。mock 渠道(默认)走 trade 本地 MockPayoutClient 同步直通,零 pay 依赖。'
|