openapi: 3.0.3 info: title: Supply Console API Contract Draft version: 1.0.0-draft description: | 供应侧三大页面(账号挂载、套餐发布、收益结算)接口契约草案。 安全边界要求: 1) 仅接受平台鉴权头(Authorization),不接受 query key 鉴权。 2) 任何响应不得返回可复用上游凭证明文片段。 变更日志: - 2026-03-27:新增幂等请求头组件与写操作挂载;补充 409/202 幂等语义示例。 - 2026-03-27:命名策略调整为 `/supply` 主路径;`/supplier` 保留为兼容 alias。 servers: - url: https://api.example.com description: Production security: - BearerAuth: [] tags: - name: SupplyAccounts - name: SupplyPackages - name: SupplySettlements - name: SupplyEarnings - name: SupplierBilling paths: /api/v1/supply/accounts/verify: post: tags: [SupplyAccounts] summary: 验证供应账号凭证可用性 operationId: verifySupplyAccount requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VerifySupplyAccountRequest' responses: '200': description: 验证成功 content: application/json: schema: $ref: '#/components/schemas/VerifySupplyAccountResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '422': $ref: '#/components/responses/BusinessError' /api/v1/supply/accounts: post: tags: [SupplyAccounts] summary: 创建供应账号 operationId: createSupplyAccount parameters: - $ref: '#/components/parameters/XRequestIdHeader' - $ref: '#/components/parameters/IdempotencyKeyHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateSupplyAccountRequest' responses: '201': description: 创建成功 content: application/json: schema: $ref: '#/components/schemas/CreateSupplyAccountResponse' '202': $ref: '#/components/responses/AcceptedInProgress' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/BusinessError' /api/v1/supply/accounts/{accountId}/activate: post: tags: [SupplyAccounts] summary: 激活供应账号 operationId: activateSupplyAccount parameters: - $ref: '#/components/parameters/AccountIdParam' responses: '200': description: 激活成功 content: application/json: schema: $ref: '#/components/schemas/SupplyAccountStatusResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' /api/v1/supply/accounts/{accountId}/suspend: post: tags: [SupplyAccounts] summary: 暂停供应账号 operationId: suspendSupplyAccount parameters: - $ref: '#/components/parameters/AccountIdParam' responses: '200': description: 暂停成功 content: application/json: schema: $ref: '#/components/schemas/SupplyAccountStatusResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' /api/v1/supply/accounts/{accountId}: delete: tags: [SupplyAccounts] summary: 删除供应账号 operationId: deleteSupplyAccount parameters: - $ref: '#/components/parameters/AccountIdParam' responses: '204': description: 删除成功 '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' /api/v1/supply/accounts/{accountId}/audit-logs: get: tags: [SupplyAccounts] summary: 查询账号审计日志 operationId: listSupplyAccountAuditLogs parameters: - $ref: '#/components/parameters/AccountIdParam' - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PageSizeParam' responses: '200': description: 查询成功 content: application/json: schema: $ref: '#/components/schemas/AuditLogListResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /api/v1/supply/packages/draft: post: tags: [SupplyPackages] summary: 保存套餐草稿 operationId: saveSupplyPackageDraft requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SaveSupplyPackageDraftRequest' responses: '201': description: 保存成功 content: application/json: schema: $ref: '#/components/schemas/SupplyPackageResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '422': $ref: '#/components/responses/BusinessError' /api/v1/supply/packages/{packageId}/publish: post: tags: [SupplyPackages] summary: 发布套餐上架 operationId: publishSupplyPackage parameters: - $ref: '#/components/parameters/PackageIdParam' - $ref: '#/components/parameters/XRequestIdHeader' - $ref: '#/components/parameters/IdempotencyKeyHeader' responses: '200': description: 发布成功 content: application/json: schema: $ref: '#/components/schemas/SupplyPackageStatusResponse' '202': $ref: '#/components/responses/AcceptedInProgress' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' /api/v1/supply/packages/{packageId}/pause: post: tags: [SupplyPackages] summary: 暂停售卖套餐 operationId: pauseSupplyPackage parameters: - $ref: '#/components/parameters/PackageIdParam' responses: '200': description: 暂停成功 content: application/json: schema: $ref: '#/components/schemas/SupplyPackageStatusResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' /api/v1/supply/packages/{packageId}/unlist: post: tags: [SupplyPackages] summary: 下架套餐 operationId: unlistSupplyPackage parameters: - $ref: '#/components/parameters/PackageIdParam' responses: '200': description: 下架成功 content: application/json: schema: $ref: '#/components/schemas/SupplyPackageStatusResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' /api/v1/supply/packages/batch-price: post: tags: [SupplyPackages] summary: 批量调价 operationId: batchUpdateSupplyPackagePrice parameters: - $ref: '#/components/parameters/XRequestIdHeader' - $ref: '#/components/parameters/IdempotencyKeyHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BatchUpdateSupplyPackagePriceRequest' responses: '200': description: 调价完成 content: application/json: schema: $ref: '#/components/schemas/BatchUpdateSupplyPackagePriceResponse' '202': $ref: '#/components/responses/AcceptedInProgress' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '409': $ref: '#/components/responses/Conflict' /api/v1/supply/packages/{packageId}/clone: post: tags: [SupplyPackages] summary: 复制套餐 operationId: cloneSupplyPackage parameters: - $ref: '#/components/parameters/PackageIdParam' responses: '201': description: 复制成功 content: application/json: schema: $ref: '#/components/schemas/SupplyPackageResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /api/v1/supply/billing: get: tags: [SupplierBilling] summary: 查询供应方账单汇总(canonical) operationId: getSupplyBilling parameters: - $ref: '#/components/parameters/StartDateParam' - $ref: '#/components/parameters/EndDateParam' - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PageSizeParam' responses: '200': description: 查询成功 content: application/json: schema: $ref: '#/components/schemas/SupplierBillingResponse' '401': $ref: '#/components/responses/Unauthorized' /api/v1/supplier/billing: get: tags: [SupplierBilling] summary: 查询供应方账单汇总(alias,兼容路径) description: | Deprecated alias of `/api/v1/supply/billing`. 仅用于历史客户端兼容,不新增能力字段。 deprecated: true operationId: getSupplierBillingAlias parameters: - $ref: '#/components/parameters/StartDateParam' - $ref: '#/components/parameters/EndDateParam' - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PageSizeParam' responses: '200': description: 查询成功 content: application/json: schema: $ref: '#/components/schemas/SupplierBillingResponse' '401': $ref: '#/components/responses/Unauthorized' /api/v1/supply/settlements/withdraw: post: tags: [SupplySettlements] summary: 发起提现申请 operationId: createSupplySettlementWithdraw parameters: - $ref: '#/components/parameters/XRequestIdHeader' - $ref: '#/components/parameters/IdempotencyKeyHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateWithdrawRequest' responses: '201': description: 提现申请创建成功 content: application/json: schema: $ref: '#/components/schemas/CreateWithdrawResponse' '202': $ref: '#/components/responses/AcceptedInProgress' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '409': $ref: '#/components/responses/Conflict' /api/v1/supply/settlements/{settlementId}/cancel: post: tags: [SupplySettlements] summary: 撤销提现申请 operationId: cancelSupplySettlementWithdraw parameters: - $ref: '#/components/parameters/SettlementIdParam' - $ref: '#/components/parameters/XRequestIdHeader' - $ref: '#/components/parameters/IdempotencyKeyHeader' responses: '200': description: 撤销成功 content: application/json: schema: $ref: '#/components/schemas/SupplySettlementStatusResponse' '202': $ref: '#/components/responses/AcceptedInProgress' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' /api/v1/supply/settlements/{settlementId}/statement: get: tags: [SupplySettlements] summary: 下载对账单 operationId: downloadSupplySettlementStatement parameters: - $ref: '#/components/parameters/SettlementIdParam' responses: '200': description: 下载地址 content: application/json: schema: $ref: '#/components/schemas/SettlementStatementResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /api/v1/supply/earnings/records: get: tags: [SupplyEarnings] summary: 查询收益流水 operationId: listSupplyEarningRecords parameters: - $ref: '#/components/parameters/StartDateParam' - $ref: '#/components/parameters/EndDateParam' - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PageSizeParam' responses: '200': description: 查询成功 content: application/json: schema: $ref: '#/components/schemas/SupplyEarningRecordListResponse' '401': $ref: '#/components/responses/Unauthorized' components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT parameters: AccountIdParam: name: accountId in: path required: true schema: type: integer format: int64 PackageIdParam: name: packageId in: path required: true schema: type: integer format: int64 SettlementIdParam: name: settlementId in: path required: true schema: type: integer format: int64 StartDateParam: name: start_date in: query schema: type: string format: date EndDateParam: name: end_date in: query schema: type: string format: date PageParam: name: page in: query schema: type: integer minimum: 1 default: 1 PageSizeParam: name: page_size in: query schema: type: integer minimum: 1 maximum: 200 default: 20 XRequestIdHeader: name: X-Request-Id in: header required: true description: 客户端请求幂等追踪ID(全链路唯一) schema: type: string minLength: 8 maxLength: 128 IdempotencyKeyHeader: name: Idempotency-Key in: header required: true description: 写操作幂等键(同资源同动作语义唯一) schema: type: string minLength: 8 maxLength: 128 responses: BadRequest: description: 参数错误 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' Unauthorized: description: 未认证或认证失效 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' NotFound: description: 资源不存在 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' Conflict: description: 状态冲突 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: idempotencyPayloadMismatch: summary: 幂等键命中但请求体不一致 value: request_id: req_20260327_001 error: code: IDEMPOTENCY_PAYLOAD_MISMATCH message: idempotency key replay with different payload details: retryable: false expected_action: reuse_same_payload_or_new_idempotency_key AcceptedInProgress: description: 首次请求仍在处理,请按建议间隔重试 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: idempotencyInProgress: summary: 幂等处理中重放 value: request_id: req_20260327_002 error: code: IDEMPOTENCY_IN_PROGRESS message: request is processing details: retry_after_ms: 2000 retryable: true BusinessError: description: 业务校验失败 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' schemas: VerifySupplyAccountRequest: type: object required: [provider, account_type, credential_input] properties: provider: type: string enum: [openai, anthropic, gemini, baidu, xfyun, tencent] account_type: type: string enum: [api_key, oauth] credential_input: type: string minLength: 8 maxLength: 4096 writeOnly: true min_quota_threshold: type: number format: double minimum: 0 VerifySupplyAccountResponse: type: object required: [request_id, data] properties: request_id: type: string data: type: object required: [verify_status, risk_score] properties: verify_status: type: string enum: [pass, review_required, reject] available_quota: type: number format: double risk_score: type: integer minimum: 0 maximum: 100 check_items: type: array items: type: object properties: item: type: string result: type: string enum: [pass, fail, warn] message: type: string CreateSupplyAccountRequest: allOf: - $ref: '#/components/schemas/VerifySupplyAccountRequest' - type: object required: [risk_ack] properties: account_alias: type: string minLength: 1 maxLength: 100 risk_ack: type: boolean enum: [true] CreateSupplyAccountResponse: type: object required: [request_id, data] properties: request_id: type: string data: $ref: '#/components/schemas/SupplyAccount' SupplyAccountStatusResponse: type: object required: [request_id, data] properties: request_id: type: string data: type: object required: [account_id, status, updated_at] properties: account_id: type: integer format: int64 status: type: string enum: [pending, active, suspended, disabled] updated_at: type: string format: date-time SupplyAccount: type: object required: [account_id, provider, account_type, status, created_at] properties: account_id: type: integer format: int64 provider: type: string enum: [openai, anthropic, gemini, baidu, xfyun, tencent] account_type: type: string enum: [api_key, oauth] account_alias: type: string status: type: string enum: [pending, active, suspended, disabled] available_quota: type: number format: double risk_score: type: integer created_at: type: string format: date-time SaveSupplyPackageDraftRequest: type: object required: - supply_account_id - model - total_quota - price_per_1m_input - price_per_1m_output - valid_days properties: supply_account_id: type: integer format: int64 model: type: string minLength: 1 maxLength: 100 total_quota: type: number format: double exclusiveMinimum: true minimum: 0 price_per_1m_input: type: number format: double minimum: 0 price_per_1m_output: type: number format: double minimum: 0 valid_days: type: integer minimum: 1 maximum: 365 max_concurrent: type: integer minimum: 1 maximum: 1000 default: 10 rate_limit_rpm: type: integer minimum: 1 maximum: 100000 default: 60 SupplyPackageResponse: type: object required: [request_id, data] properties: request_id: type: string data: $ref: '#/components/schemas/SupplyPackage' SupplyPackage: type: object required: - package_id - supply_account_id - model - status - total_quota - available_quota - created_at properties: package_id: type: integer format: int64 supply_account_id: type: integer format: int64 model: type: string total_quota: type: number format: double available_quota: type: number format: double price_per_1m_input: type: number format: double price_per_1m_output: type: number format: double valid_days: type: integer status: type: string enum: [draft, active, paused, sold_out, expired] created_at: type: string format: date-time SupplyPackageStatusResponse: type: object required: [request_id, data] properties: request_id: type: string data: type: object required: [package_id, status, updated_at] properties: package_id: type: integer format: int64 status: type: string enum: [draft, active, paused, sold_out, expired] updated_at: type: string format: date-time BatchUpdateSupplyPackagePriceRequest: type: object required: [items] properties: items: type: array minItems: 1 maxItems: 200 items: type: object required: [package_id, price_per_1m_input, price_per_1m_output] properties: package_id: type: integer format: int64 price_per_1m_input: type: number format: double minimum: 0 price_per_1m_output: type: number format: double minimum: 0 BatchUpdateSupplyPackagePriceResponse: type: object required: [request_id, data] properties: request_id: type: string data: type: object required: [total, success_count, failed_count] properties: total: type: integer success_count: type: integer failed_count: type: integer failures: type: array items: type: object properties: package_id: type: integer format: int64 error_code: type: string message: type: string SupplierBillingResponse: type: object required: [request_id, data] properties: request_id: type: string data: type: object required: [period, summary] properties: period: type: object properties: start: type: string format: date end: type: string format: date summary: type: object properties: total_revenue: type: number format: double total_orders: type: integer total_usage: type: integer format: int64 total_requests: type: integer format: int64 avg_success_rate: type: number format: double platform_fee: type: number format: double net_earnings: type: number format: double by_platform: type: array items: type: object properties: platform: type: string revenue: type: number format: double orders: type: integer tokens: type: integer format: int64 success_rate: type: number format: double CreateWithdrawRequest: type: object required: [withdraw_amount, payment_method, payment_account, sms_code] properties: withdraw_amount: type: number format: double exclusiveMinimum: true minimum: 0 payment_method: type: string enum: [bank, alipay, wechat] payment_account: type: string minLength: 2 maxLength: 100 sms_code: type: string minLength: 4 maxLength: 10 CreateWithdrawResponse: type: object required: [request_id, data] properties: request_id: type: string data: $ref: '#/components/schemas/SupplySettlement' SupplySettlementStatusResponse: type: object required: [request_id, data] properties: request_id: type: string data: type: object required: [settlement_id, status, updated_at] properties: settlement_id: type: integer format: int64 status: type: string enum: [pending, processing, completed, failed] updated_at: type: string format: date-time SupplySettlement: type: object required: [settlement_id, settlement_no, status, total_amount, net_amount, created_at] properties: settlement_id: type: integer format: int64 settlement_no: type: string status: type: string enum: [pending, processing, completed, failed] total_amount: type: number format: double fee_amount: type: number format: double net_amount: type: number format: double payment_method: type: string enum: [bank, alipay, wechat] created_at: type: string format: date-time SettlementStatementResponse: type: object required: [request_id, data] properties: request_id: type: string data: type: object required: [settlement_id, file_name, download_url, expires_at] properties: settlement_id: type: integer format: int64 file_name: type: string download_url: type: string format: uri expires_at: type: string format: date-time SupplyEarningRecordListResponse: type: object required: [request_id, data, pagination] properties: request_id: type: string data: type: array items: type: object required: [record_id, user_id, earnings_type, amount, status, earned_at] properties: record_id: type: integer format: int64 user_id: type: integer format: int64 settlement_id: type: integer format: int64 earnings_type: type: string enum: [usage, bonus, refund] amount: type: number format: double status: type: string enum: [pending, available, withdrawn, frozen] description: type: string earned_at: type: string format: date-time pagination: $ref: '#/components/schemas/Pagination' AuditLogListResponse: type: object required: [request_id, data, pagination] properties: request_id: type: string data: type: array items: type: object required: [event_id, operator_id, tenant_id, object_type, object_id, request_id, created_at] properties: event_id: type: string operator_id: type: integer format: int64 tenant_id: type: integer format: int64 object_type: type: string object_id: type: string action: type: string before_state: type: object additionalProperties: true after_state: type: object additionalProperties: true request_id: type: string created_at: type: string format: date-time pagination: $ref: '#/components/schemas/Pagination' Pagination: type: object required: [page, page_size, total] properties: page: type: integer page_size: type: integer total: type: integer ErrorResponse: type: object required: [request_id, error] properties: request_id: type: string error: type: object required: [code, message] properties: code: type: string example: SUP_ACC_4001 message: type: string details: type: object additionalProperties: true