lili b31cc37a4f chore(brand): 造梦AI→绘境AI 全活层品牌不变量收口(441 文件)
把活层退役旧品牌「造梦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>
2026-06-25 08:29:11 -07:00

557 lines
36 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 依赖。'