openapi: 3.1.0
info:
  title: KYS 后台管理与店铺供货 API
  version: 1.0.0
  description: |
    后台管理员读取、处理本地订单及所有来源售后；店铺处理自有商品和有效供货订单及售后。
    两个分组均使用商户主站 HTTPS 域名，每个请求同时提供对应账户和 API Key。
    普通列表分页，GET 详情返回完整卡密或消息。动作响应的详情快照可能按原分页返回。
    金额单位为人民币元，实付和退款一般两位小数，单价、成本及供货报价可为四位小数。
  license:
    name: Proprietary
    identifier: LicenseRef-KYS-Proprietary
x-kys-contract-revision: 2026-10-28.3
x-kys-guides:
  admin: ./admin-api-guide.md
  seller: ./seller-supply-api-guide.md
x-kys-rate-limits:
  authenticationPerMerchantAndSourceIpPerMinute: 200
  accountTotalPerMinute: 600
  accountTotalBurst: 30
  ordersReadPerMinute: 120
  aftersalesReadPerMinute: 120
  ordersWritePerMinute: 30
  aftersalesWritePerMinute: 30
  description: 管理员额度独立；店铺与同一会员的采购 OpenAPI 共享账户和相应分组额度。
servers:
  - url: https://{merchantDomain}/openapi/v1
    description: 商户提供的主站 HTTPS 域名
    variables:
      merchantDomain:
        default: merchant.example.com
tags:
  - {name: 后台订单, description: 已付款的本地卡密、本地充值、无货源人工充值；包括分站成交订单。}
  - {name: 后台售后, description: 所有来源售后，不按本地货源筛选。}
  - {name: 店铺供货订单, description: 当前店铺自有商品和有效供货订单，包括合法分站成交订单。}
  - {name: 店铺供货售后, description: 沿关联订单确定可见范围，销售与履约权限分别判断。}
paths:
  /admin/orders:
    get:
      tags: [后台订单]
      operationId: listAdminOrders
      summary: 获取本地订单列表
      description: 多个筛选条件共同生效，结果不附加全站统计。默认每页 10 条，最大 100。
      security: [{AdminAccount: [], ApiKey: []}]
      parameters:
        - {$ref: '#/components/parameters/Page'}
        - {$ref: '#/components/parameters/AdminPageSize'}
        - {name: keyword, in: query, schema: {type: string}, description: 完整 D 加 17 位数字订单号按单号精确查询，其余按商品名模糊查询。}
        - {name: orderNo, in: query, schema: {type: string}, description: 订单号模糊查询。}
        - {name: externalOrderNo, in: query, schema: {type: string}, description: 采购方或销售渠道外部订单号模糊查询。}
        - {name: status, in: query, schema: {type: string, enum: [pending, processing, success, canceled, partially_refunded, refunded, all]}}
        - {name: productName, in: query, schema: {type: string}, description: 商品名称模糊查询。}
        - {name: orderInput, in: query, schema: {type: string}, description: 完整充值输入值，例如充值账号。}
        - {name: productType, in: query, schema: {type: string, enum: [card, recharge]}}
        - {name: paymentChannel, in: query, schema: {type: string}, description: 支付渠道代码；sales_channel 用于销售渠道订单筛选。}
        - {name: userId, in: query, schema: {type: integer, minimum: 0}, description: 买家 ID，0 表示游客。}
        - {name: nickname, in: query, schema: {type: string}, description: 买家昵称模糊查询。}
        - {name: adminRemark, in: query, schema: {type: string}, description: 管理备注模糊查询。}
        - {$ref: '#/components/parameters/StartDate'}
        - {$ref: '#/components/parameters/EndDate'}
        - {name: minAmount, in: query, schema: {type: number}, description: 实付金额下限，元。}
        - {name: maxAmount, in: query, schema: {type: number}, description: 实付金额上限，元。}
        - {name: orderType, in: query, schema: {type: string, enum: [platform, partner, supplier, guest, redemption, all]}, description: 来源分类。}
      responses:
        '200':
          description: 分页订单
          content:
            application/json:
              schema: {$ref: '#/components/schemas/AdminOrderListResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /admin/orders/{id}:
    get:
      tags: [后台订单]
      operationId: getAdminOrder
      summary: 获取订单完整详情
      description: cardSecrets 一次返回全部卡密；不存在或超出本地订单范围返回 404002。
      security: [{AdminAccount: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      responses:
        '200':
          description: 订单详情
          content:
            application/json:
              schema: {$ref: '#/components/schemas/AdminOrderDetailResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /admin/orders/process:
    post:
      tags: [后台订单]
      operationId: processAdminOrders
      summary: 单个或批量处理订单
      description: |
        最多 100 项；单笔也使用 orderIds 数组。逐项检查 data[].result，可能部分成功，结果顺序不保证。
        本地卡密成功处理执行平台库存出库。退款受理不代表退款完成，请查询订单详情。
        退款 requestNo 必须在同一次请求重试时保持不变，且参数内容相同。
      security: [{AdminAccount: [], ApiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/AdminOrderProcessRequest'}
            examples:
              finish: {value: {orderIds: [12345], status: success, message: 处理完成}}
              refund: {value: {orderIds: [12345], status: refunded, refundType: full, requestNo: refund_20261028_0001, message: 同意退款}}
      responses:
        '200':
          description: 逐项处理结果；退款项可带 taskId
          content:
            application/json:
              schema: {$ref: '#/components/schemas/AdminProcessResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /admin/aftersales:
    get:
      tags: [后台售后]
      operationId: listAdminAftersales
      summary: 获取所有来源售后列表
      security: [{AdminAccount: [], ApiKey: []}]
      parameters:
        - {$ref: '#/components/parameters/Page'}
        - {$ref: '#/components/parameters/AdminPageSize'}
        - {name: status, in: query, schema: {type: string, enum: [pending, processing, completed, terminated, all, '-1']}}
        - {name: keyword, in: query, schema: {type: string}, description: 与 searchType 同时提交。}
        - {name: searchType, in: query, schema: {type: string, enum: [userId, orderNo]}}
        - {$ref: '#/components/parameters/StartDate'}
        - {$ref: '#/components/parameters/EndDate'}
        - {name: source, in: query, schema: {type: string, enum: [platform, partner, supplier, wechatpay, all]}, description: 来源筛选分类；返回项 source 则为 local/wechatpay 售后通道。}
      responses:
        '200':
          description: 分页售后及关联订单摘要，无 counts
          content:
            application/json:
              schema: {$ref: '#/components/schemas/AdminAftersaleListResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /admin/aftersales/{id}:
    get:
      tags: [后台售后]
      operationId: getAdminAftersale
      summary: 获取售后完整详情
      description: messages 是完整数组，关联订单不受后台本地订单范围筛选。
      security: [{AdminAccount: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      responses:
        '200':
          description: 完整售后
          content:
            application/json:
              schema: {$ref: '#/components/schemas/AdminAftersaleDetailResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /admin/aftersales/{id}/process:
    post:
      tags: [后台售后]
      operationId: processAdminAftersale
      summary: 回复、处理售后
      description: |
        普通售后、微信投诉传 status、replyContent、attachments、requestNo；实物售后传 action 及对应字段。
        按详情真实售后类型处理，不能指定上游或渠道。普通售后完成不等于订单退款。
        微信远端结果待确认时先核对详情与消息，不换编号盲重发。
      security: [{AdminAccount: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - {$ref: '#/components/schemas/AdminOrdinaryAftersaleRequest'}
                - {$ref: '#/components/schemas/AdminPhysicalAftersaleRequest'}
            examples:
              reply: {value: {status: processing, replyContent: 已收到，正在核实, attachments: [], requestNo: reply_20261028_0001}}
              approveExchange: {value: {action: approve, message: 同意换货}}
      responses:
        '200':
          description: 普通/微信为批量结果；实物为实物售后对象
          content:
            application/json:
              schema: {$ref: '#/components/schemas/AdminAftersaleProcessResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/orders:
    get:
      tags: [店铺供货订单]
      operationId: listSellerOrders
      summary: 获取自有商品及供货订单列表
      description: 包含合法分站成交订单；role=supplier 时省略买家实付/退款金额，提供 supplyAmount，可为 null。
      security: [{MemberId: [], ApiKey: []}]
      parameters:
        - {$ref: '#/components/parameters/Page'}
        - {$ref: '#/components/parameters/SellerPageSize'}
        - {$ref: '#/components/parameters/OrderStatusFilter'}
        - {name: productType, in: query, schema: {$ref: '#/components/schemas/ProductType'}}
        - {$ref: '#/components/parameters/ExactOrderNo'}
      responses:
        '200':
          description: 分页店铺订单
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerOrderListResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/orders/{id}:
    get:
      tags: [店铺供货订单]
      operationId: getSellerOrder
      summary: 获取订单完整详情
      description: cards.list 返回全部卡密；按实际履约权限返回充值输入、验证码和操作资格。
      security: [{MemberId: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      responses:
        '200':
          description: 完整订单
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerOrderDetailResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/orders/{id}/recharge-result:
    post:
      tags: [店铺供货订单]
      operationId: processSellerRecharge
      summary: 处理人工充值或上传充值凭证
      description: |
        实际履约店铺处理人工充值。JSON 提交 processing/success/canceled；取消结果须查询确认。
        multipart/form-data 通过 processData 的 JSON 字符串提交 action=saveRechargeProof，并上传 rechargeProofImage。
        上传凭证与成功处理分两次调用。图片 JPEG/PNG/WebP，不超过 2 MiB、4000 万像素。
      security: [{MemberId: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/SellerRechargeRequest'}
            example: {status: success, message: 充值已完成}
          multipart/form-data:
            schema:
              type: object
              required: [processData, rechargeProofImage]
              properties:
                processData: {type: string, description: JSON 对象字符串, example: '{"action":"saveRechargeProof"}'}
                rechargeProofImage: {type: string, format: binary, description: JPEG/PNG/WebP，最多 2097152 字节。}
      responses:
        '200':
          description: 订单详情快照，或仅含 rechargeProof 的上传结果
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerRechargeResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/orders/{id}/deliver:
    post:
      tags: [店铺供货订单]
      operationId: deliverSellerOrder
      summary: 本地卡密出库或实物发货
      description: |
        卡密传空 JSON 对象，由平台库存出库，不上传卡密。实物传 carrierCode、trackingNo。
        本店须为实际履约方；实物还须 canShip=true。响应快照中的卡密可能分页，完整卡密通过 GET 详情获取。
      security: [{MemberId: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - {type: object, maxProperties: 0}
                - {$ref: '#/components/schemas/LogisticsRequest'}
            examples:
              card: {value: {}}
              physical: {value: {carrierCode: sf_express, trackingNo: SFEXAMPLE0001}}
      responses:
        '200':
          description: 订单详情快照
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerOrderDetailResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/orders/{id}/logistics:
    put:
      tags: [店铺供货订单]
      operationId: updateSellerLogistics
      summary: 更新实物运单
      description: 要求本店具备履约权限，且详情 physical.canUpdateLogistics=true。
      security: [{MemberId: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/LogisticsRequest'}
      responses:
        '200':
          description: 订单详情快照
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerOrderDetailResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/orders/{id}/refund:
    post:
      tags: [店铺供货订单]
      operationId: refundSellerOrder
      summary: 退还订单全部剩余可退金额
      description: |
        适用于有资格的成功或部分退款订单，不传金额，兑换订单不支持原支付退款。
        相同 requestNo 重试保持说明一致。受理成功后通过详情 status/refundPending 确认完成。
      security: [{MemberId: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/SellerRefundRequest'}
            example: {message: 同意退款, requestNo: seller_refund_20261028_0001}
      responses:
        '200':
          description: 订单详情快照，可能仍在异步退款中
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerOrderDetailResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/aftersales:
    get:
      tags: [店铺供货售后]
      operationId: listSellerAftersales
      summary: 获取店铺可见售后列表
      security: [{MemberId: [], ApiKey: []}]
      parameters:
        - {$ref: '#/components/parameters/Page'}
        - {$ref: '#/components/parameters/SellerPageSize'}
        - {name: status, in: query, schema: {$ref: '#/components/schemas/AftersaleStatus'}, description: 查询全部时省略，不能传 all。}
        - {$ref: '#/components/parameters/ExactOrderNo'}
      responses:
        '200':
          description: 分页售后
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerAftersaleListResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/aftersales/{id}:
    get:
      tags: [店铺供货售后]
      operationId: getSellerAftersale
      summary: 获取售后完整详情
      description: messages.list 返回全部消息；供货店铺可读，但回复资格由 canReply 表示。
      security: [{MemberId: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      responses:
        '200':
          description: 完整售后详情
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerAftersaleDetailResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/aftersales/{id}/messages:
    post:
      tags: [店铺供货售后]
      operationId: replySellerAftersale
      summary: 回复或举证
      description: 仅销售店铺可向买家回复。相同 requestNo 保持内容一致；动作响应消息可能分页。
      security: [{MemberId: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/SellerMessageRequest'}
      responses:
        '200':
          description: 售后详情快照
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerAftersaleDetailResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/aftersales/{id}/decision:
    post:
      tags: [店铺供货售后]
      operationId: submitSellerAftersaleDecision
      summary: 提交商家处理意见
      description: 记录沟通意见，不裁决、退款或完成售后；与回复使用不同 requestNo。正文最多 245 字符。
      security: [{MemberId: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/SellerDecisionRequest'}
      responses:
        '200':
          description: 售后详情快照
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerAftersaleDetailResponse'}
        default: {$ref: '#/components/responses/Failure'}
  /seller/aftersales/{id}/exchange:
    post:
      tags: [店铺供货售后]
      operationId: processSellerExchange
      summary: 处理已批准换货物流
      description: 仅实际履约店铺处理实物换货的 receive_return/replacement_ship，不提供批准、拒绝或退款裁决。
      security: [{MemberId: [], ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/PathId'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/SellerExchangeRequest'}
      responses:
        '200':
          description: 售后详情快照
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SellerAftersaleDetailResponse'}
        default: {$ref: '#/components/responses/Failure'}
components:
  securitySchemes:
    AdminAccount:
      type: apiKey
      in: header
      name: X-KYS-Admin-Account
      description: 后台管理员登录账户，与独立 ADM_ 密钥配对。
    MemberId:
      type: apiKey
      in: header
      name: X-KYS-User-Id
      description: 店主前台用户 ID，与该会员当前 OpenAPI Key 配对。
    ApiKey:
      type: apiKey
      in: header
      name: X-KYS-Key
      description: 后台仅接受 ADM_ 加 16 位小写十六进制密钥，共 20 个字符；店铺使用现有 AK_ 加 16/32 位小写十六进制密钥。重置后旧 Key 失效。
  parameters:
    PathId:
      name: id
      in: path
      required: true
      description: 订单或售后整数 ID，1～10 位正整数，与单号不同。
      schema: {type: integer, format: int64, minimum: 1, maximum: 9999999999}
    Page:
      name: page
      in: query
      schema: {type: integer, minimum: 1, default: 1}
    AdminPageSize:
      name: pageSize
      in: query
      schema: {type: integer, minimum: 1, maximum: 100, default: 10}
    SellerPageSize:
      name: pageSize
      in: query
      schema: {type: integer, minimum: 1, maximum: 100, default: 20}
    StartDate:
      name: startDate
      in: query
      description: 与 endDate 同时提交，按北京时间自然日包含起止日期。
      schema: {type: string, format: date}
    EndDate:
      name: endDate
      in: query
      description: 与 startDate 同时提交。
      schema: {type: string, format: date}
    OrderStatusFilter:
      name: status
      in: query
      description: 精确筛选；查询全部时省略，不能传 all。
      schema: {$ref: '#/components/schemas/OrderStatus'}
    ExactOrderNo:
      name: orderNo
      in: query
      description: 按订单号精确筛选。
      schema: {type: string}
  responses:
    Failure:
      description: |
        400001 参数或当前业务状态不允许；401001 鉴权失败；403001 权限不足；403002 IP 不符；
        404001/404002 路径或资源不存在；409001 内容/操作冲突；429001 限流；503001 暂时不可用；500001 系统失败。
        域名、入口或网关层拒绝可能使用字符串错误码且没有 requestId，请先检查 HTTP 状态。
      headers:
        Retry-After: {description: 429/503 的建议等待秒数。, schema: {type: integer, minimum: 1}}
        X-Request-Id: {schema: {type: string}}
        X-RateLimit-Limit: {schema: {type: integer}}
        X-RateLimit-Remaining: {schema: {type: integer}}
        X-RateLimit-Reset: {description: Unix 秒级时间。, schema: {type: integer, format: int64}}
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
          example: {code: 401001, message: 身份验证失败, data: null, requestId: req_example}
  schemas:
    OrderStatus:
      type: string
      enum: [pending, processing, success, canceled, partially_refunded, refunded]
      description: 待处理、处理中、成功、已取消、部分退款、已退款。
    AftersaleStatus:
      type: string
      enum: [pending, processing, completed, terminated]
    ProductType:
      type: string
      enum: [card, recharge, physical]
    RequestNo:
      type: string
      minLength: 8
      maxLength: 48
      pattern: '^[A-Za-z0-9_-]{8,48}$'
      description: 本次操作的稳定请求编号；同编号重试保持业务内容一致，仅在接口明确支持时提供幂等保障。
    Timestamp:
      type: integer
      format: int64
      minimum: 0
      description: Unix 秒级时间，未发生时可为 0。
    Money:
      type: number
      description: 人民币元，一般最多两位小数。
    Decimal:
      type: [number, string]
      description: 十进制金额，可能返回数字字符串，最多四位小数；利润可为负。
    NullableString:
      type: [string, 'null']
    AttachmentList:
      type: array
      maxItems: 5
      items: {type: string, format: uri, maxLength: 255, pattern: '^https?://'}
    EmptyOrObject:
      description: 按业务提供的对象；没有内容时可为 []。
      oneOf:
        - {type: object, additionalProperties: true}
        - {type: array, maxItems: 0, items: {}}
    AdminOrderProcessRequest:
      type: object
      required: [orderIds, status]
      properties:
        orderIds: {type: array, minItems: 1, maxItems: 100, items: {type: integer, minimum: 1}}
        status: {$ref: '#/components/schemas/OrderStatus'}
        message: {type: string, maxLength: 255, default: ''}
        processStart: {type: number, default: 0}
        processCurrent: {type: number, default: 50}
        processTarget: {type: number, default: 100}
        refundType: {type: string, description: full 退全部剩余金额；partial 按指定金额退款；通常按这两种模式接入。}
        currentRefundAmount: {$ref: '#/components/schemas/Money'}
        totalRefundedAmount: {type: number, description: 本次退款后的累计已退金额；通常省略由平台计算，提交时必须与本次金额一致。}
        commissionRefundType: {type: string, enum: [noForce, force], default: noForce}
        requestNo: {$ref: '#/components/schemas/RequestNo'}
        refundRecord:
          type: object
          description: 兼容退款记录，通常省略退款单号由平台生成。
          properties:
            refundNo: {type: string, maxLength: 64, pattern: '^[A-Za-z0-9_-]+$'}
      allOf:
        - if: {properties: {status: {enum: [refunded, partially_refunded]}}, required: [status]}
          then: {required: [requestNo]}
      description: canceled 且带退款模式执行退款时也须 requestNo。普通处理不是按 requestNo 幂等。
    AdminOrdinaryAftersaleRequest:
      type: object
      required: [status, requestNo]
      properties:
        status: {$ref: '#/components/schemas/AftersaleStatus'}
        replyContent: {type: string, maxLength: 255}
        attachments: {$ref: '#/components/schemas/AttachmentList'}
        requestNo: {$ref: '#/components/schemas/RequestNo'}
      anyOf:
        - {required: [replyContent], properties: {replyContent: {minLength: 1}}}
        - {required: [attachments], properties: {attachments: {minItems: 1}}}
      not: {required: [action]}
    AdminPhysicalAftersaleRequest:
      type: object
      required: [action]
      properties:
        action: {type: string, enum: [reply, approve, reject, close, receive_return, replacement_ship]}
        message: {type: string, maxLength: 255}
        requestNo: {$ref: '#/components/schemas/RequestNo'}
        carrierCode: {type: string, description: '取详情 carrierOptions[].code，例如 sf_express。'}
        trackingNo: {$ref: '#/components/schemas/TrackingNo'}
      allOf:
        - if: {properties: {action: {const: reply}}, required: [action]}
          then: {required: [requestNo, message], properties: {message: {minLength: 1}}}
        - if: {properties: {action: {const: replacement_ship}}, required: [action]}
          then: {required: [carrierCode, trackingNo]}
    SellerRechargeRequest:
      type: object
      required: [status]
      properties:
        status: {type: string, enum: [processing, success, canceled]}
        message: {type: string, maxLength: 255, default: ''}
    TrackingNo:
      type: string
      minLength: 1
      maxLength: 64
      pattern: '^[A-Za-z0-9-]+$'
      description: 平台保存时转为大写。
    LogisticsRequest:
      type: object
      required: [carrierCode, trackingNo]
      properties:
        carrierCode: {type: string, minLength: 1, description: '取 physical.carrierOptions[].code，例如 sf_express。'}
        trackingNo: {$ref: '#/components/schemas/TrackingNo'}
    SellerRefundRequest:
      type: object
      required: [requestNo]
      properties:
        message: {type: string, maxLength: 255, default: ''}
        requestNo: {$ref: '#/components/schemas/RequestNo'}
    SellerMessageRequest:
      type: object
      required: [requestNo]
      properties:
        content: {type: string, maxLength: 255}
        attachments: {$ref: '#/components/schemas/AttachmentList'}
        requestNo: {$ref: '#/components/schemas/RequestNo'}
      anyOf:
        - {required: [content], properties: {content: {minLength: 1}}}
        - {required: [attachments], properties: {attachments: {minItems: 1}}}
    SellerDecisionRequest:
      allOf:
        - {$ref: '#/components/schemas/SellerMessageRequest'}
        - {type: object, properties: {content: {type: string, maxLength: 245}}}
    SellerExchangeRequest:
      type: object
      required: [action]
      properties:
        action: {type: string, enum: [receive_return, replacement_ship]}
        message: {type: string, maxLength: 255}
        carrierCode: {type: string, description: '取 physical.carrierOptions[].code。'}
        trackingNo: {$ref: '#/components/schemas/TrackingNo'}
      allOf:
        - if: {properties: {action: {const: replacement_ship}}, required: [action]}
          then: {required: [carrierCode, trackingNo]}
    BuyerSummary:
      type: object
      properties:
        id: {type: integer, minimum: 0}
        nickname: {type: string}
        avatar: {$ref: '#/components/schemas/NullableString'}
        isGuest: {type: boolean}
    InputTemplate:
      description: 输入模板；没有模板时可为 []。字段值按 fieldKey 对应读取。
      oneOf:
        - type: object
          properties:
            fields:
              type: array
              items:
                type: object
                properties:
                  fieldKey: {type: string}
                  label: {type: string}
                  type: {type: string, enum: [text, number, textarea, select, radio, checkbox, image]}
                  required: {type: boolean}
                  allowMultiple: {type: boolean}
                  placeholder: {type: string}
                  helpText: {type: string}
            requiresVerificationCode: {type: boolean}
        - {type: array, maxItems: 0, items: {}}
    InputValues:
      type: array
      items: {type: object, additionalProperties: true}
      description: 按订单模板 fieldKey 组织的充值资料；值类型由模板决定。
    RechargeProof:
      type: [object, 'null']
      properties:
        url: {type: string, description: 私有凭证访问链接，可能为相对路径，应保密。}
        uploadedAt: {$ref: '#/components/schemas/Timestamp'}
        expiresAt: {$ref: '#/components/schemas/Timestamp'}
    VerificationCodeInfo:
      type: [object, 'null']
      properties:
        status: {type: string}
        code: {$ref: '#/components/schemas/NullableString'}
        submittedAt: {$ref: '#/components/schemas/Timestamp'}
    AdminCard:
      type: object
      properties:
        id: {type: [integer, string], description: 卡密记录 ID，可能是数字字符串。}
        cardNumber: {$ref: '#/components/schemas/NullableString'}
        cardSecret: {$ref: '#/components/schemas/NullableString'}
        cardType: {type: string, description: 卡密展示类型。}
        fixedContentType: {type: integer}
        cardNumberAlias: {type: string}
        cardSecretAlias: {type: string}
    SellerCard:
      type: object
      properties:
        id: {type: integer, format: int64}
        cardNumber: {$ref: '#/components/schemas/NullableString'}
        cardSecret: {$ref: '#/components/schemas/NullableString'}
        expireTime: {$ref: '#/components/schemas/Timestamp'}
    AdminOrder:
      type: object
      required: [id, orderNo, status, productType, quantity]
      description: |
        后台订单字段。列表是字段子集，详情包含卡密、退款及来源扩展信息。
        未适用字段可能省略或为 null；忽略未知附加字段，不将内部履约状态作为最终订单状态。
      properties:
        id: {type: integer, format: int64}
        orderNo: {type: string}
        status: {$ref: '#/components/schemas/OrderStatus'}
        orderType: {type: string, description: 订单记录类型，常见 local/platform/partner/supplier/sales_channel/redemption；与列表来源分类参数不同，platform 筛选可返回 local。}
        source: {type: string, description: 详情中的创建入口，例如 front/openapi。}
        sourceType: {type: string, const: local}
        productType: {type: string, enum: [card, recharge]}
        goodsId: {type: integer}
        skuId: {type: integer, minimum: 0}
        skuName: {$ref: '#/components/schemas/NullableString'}
        goodsName: {$ref: '#/components/schemas/NullableString'}
        goodsImage: {$ref: '#/components/schemas/NullableString'}
        quantity: {type: integer, minimum: 1}
        salePrice: {$ref: '#/components/schemas/Decimal'}
        costPrice: {$ref: '#/components/schemas/Decimal'}
        profit: {$ref: '#/components/schemas/Decimal'}
        totalPrice: {$ref: '#/components/schemas/Money'}
        refundedAmount: {$ref: '#/components/schemas/Money'}
        taxType: {type: string, enum: [exclusive, personal, enterprise], description: 不含税、个人含税、企业含税。}
        taxRate: {type: number}
        taxAmount: {$ref: '#/components/schemas/Money'}
        refundedTaxAmount: {$ref: '#/components/schemas/Money'}
        paymentChannel: {$ref: '#/components/schemas/NullableString'}
        paymentNo: {$ref: '#/components/schemas/NullableString'}
        paymentTime: {$ref: '#/components/schemas/Timestamp'}
        paymentFeeAmount: {$ref: '#/components/schemas/Money'}
        createdAt: {$ref: '#/components/schemas/Timestamp'}
        updatedAt: {$ref: '#/components/schemas/Timestamp'}
        completedAt: {$ref: '#/components/schemas/Timestamp'}
        processingTime: {type: integer, minimum: 0}
        user: {$ref: '#/components/schemas/BuyerSummary'}
        phoneNumber: {$ref: '#/components/schemas/NullableString'}
        isSubsiteOrder: {type: integer, enum: [0, 1]}
        fulfillmentStatus: {type: string}
        orderParams: {type: array, items: {}}
        inputTemplateSnapshot: {$ref: '#/components/schemas/InputTemplate'}
        normalizedInputValues: {$ref: '#/components/schemas/InputValues'}
        resultSnapshot: {$ref: '#/components/schemas/EmptyOrObject'}
        currentFulfillmentAttemptId: {type: [integer, 'null'], format: int64}
        finalFulfillmentAttemptId: {type: [integer, 'null'], format: int64}
        orderDomain: {$ref: '#/components/schemas/NullableString'}
        orderIp: {$ref: '#/components/schemas/NullableString'}
        orderAddress: {$ref: '#/components/schemas/NullableString'}
        remark: {$ref: '#/components/schemas/NullableString'}
        adminRemark: {$ref: '#/components/schemas/NullableString'}
        message: {$ref: '#/components/schemas/NullableString'}
        processMessage: {$ref: '#/components/schemas/NullableString'}
        cardSecrets: {type: array, items: {$ref: '#/components/schemas/AdminCard'}, description: GET 详情一次返回全部。}
        rechargeProof: {$ref: '#/components/schemas/RechargeProof'}
        verificationCodeRequired: {type: boolean}
        verificationCodeInfo: {$ref: '#/components/schemas/VerificationCodeInfo'}
        refundHistory: {type: array, items: {type: object, additionalProperties: true}}
        refundFailure: {type: [object, 'null'], additionalProperties: true}
        storeInfo: {type: object, additionalProperties: true}
        subsiteInfo: {type: object, additionalProperties: true}
        salesChannelInfo: {type: object, additionalProperties: true}
        openApiInfo: {type: object, additionalProperties: true}
        platformProfit: {type: [number, string, 'null']}
        pendingActionRequestCount: {type: integer}
        hasPendingActionRequest: {type: boolean}
        pendingUpstreamActionRequestCount: {type: integer}
        hasPendingUpstreamActionRequest: {type: boolean}
    Pagination:
      type: object
      required: [list, total, page, pageSize]
      properties:
        total: {type: integer, minimum: 0}
        page: {type: integer, minimum: 1}
        pageSize: {type: integer, minimum: 0, description: 普通列表最大 100；完整详情子容器为 total，空容器可为 0。}
    AdminOrderPage:
      allOf:
        - {$ref: '#/components/schemas/Pagination'}
        - type: object
          properties:
            list: {type: array, items: {$ref: '#/components/schemas/AdminOrder'}}
    AdminProcessItem:
      type: object
      required: [id, result, message]
      properties:
        id: {type: integer, format: int64}
        orderNo: {type: string}
        currentStatus: {$ref: '#/components/schemas/OrderStatus'}
        result: {type: string, enum: [success, failure]}
        message: {type: string}
        taskId: {type: integer, format: int64, description: 退款任务 ID，受理不等于资金已退。}
    AdminRelatedOrder:
      type: object
      description: 关联订单摘要或详情；不受本地订单范围筛选，允许返回实物和远程货源订单。
      properties:
        id: {type: integer, format: int64, minimum: 0}
        orderNo: {type: string}
        status: {type: string}
        orderType: {type: string}
        sourceType: {type: string}
        productType: {$ref: '#/components/schemas/ProductType'}
        goodsId: {type: integer}
        skuId: {type: integer}
        goodsName: {$ref: '#/components/schemas/NullableString'}
        skuName: {$ref: '#/components/schemas/NullableString'}
        user: {$ref: '#/components/schemas/BuyerSummary'}
    AftersaleBase:
      type: object
      required: [id, complainNo, orderNo, status]
      properties:
        id: {type: integer, format: int64}
        complainNo: {type: string}
        topic: {type: string}
        orderNo: {type: string}
        status: {$ref: '#/components/schemas/AftersaleStatus'}
        serviceType: {type: string, description: 普通售后为空；实物为 refund_only/return_refund/exchange。}
        serviceStatus: {type: string, description: 实物服务状态，例如 pending/awaiting_return/return_shipped/refunding/awaiting_replacement/completed/rejected/canceled。}
        createdAt: {$ref: '#/components/schemas/Timestamp'}
        updatedAt: {$ref: '#/components/schemas/Timestamp'}
    AdminAftersale:
      allOf:
        - {$ref: '#/components/schemas/AftersaleBase'}
        - type: object
          properties:
            userId: {type: integer}
            source: {type: string, enum: [local, wechatpay], description: 售后通道，与列表来源分类筛选参数不同。}
            order: {$ref: '#/components/schemas/AdminRelatedOrder'}
            messages: {type: array, items: {$ref: '#/components/schemas/AdminMessage'}, description: GET 详情返回的完整数组。}
            physical: {$ref: '#/components/schemas/PhysicalAftersale'}
    AdminMessage:
      type: object
      properties:
        id: {type: integer, format: int64}
        complainId: {type: integer, format: int64}
        sender: {type: string}
        senderLabel: {type: string}
        content: {type: string}
        messageFormat: {type: string, description: 常见 text/image。}
        timestamp: {$ref: '#/components/schemas/Timestamp'}
    AdminAftersalePage:
      allOf:
        - {$ref: '#/components/schemas/Pagination'}
        - type: object
          properties:
            list: {type: array, items: {$ref: '#/components/schemas/AdminAftersale'}}
    BatchAftersaleResult:
      type: object
      required: [total, successCount, failedCount, successList, failedList]
      properties:
        total: {type: integer}
        successCount: {type: integer}
        failedCount: {type: integer}
        successList:
          type: array
          items:
            type: object
            properties:
              index: {type: integer, minimum: 0}
              id: {type: integer, format: int64}
              action: {type: string, example: processed}
        failedList: {type: array, items: {type: object, additionalProperties: true}}
    CarrierOption:
      type: object
      properties:
        code: {type: string, example: sf_express}
        name: {type: string, example: 顺丰速运}
    Logistics:
      type: [object, 'null']
      properties:
        carrierCode: {type: string}
        carrierName: {type: string}
        trackingNo: {type: string}
        status: {type: string}
        shippedAt: {$ref: '#/components/schemas/Timestamp'}
    PhysicalAftersale:
      type: object
      required: [id, serviceType, serviceStatus]
      properties:
        id: {type: integer, format: int64}
        serviceType: {type: string, enum: [refund_only, return_refund, exchange]}
        serviceStatus: {type: string}
        returnAddress: {$ref: '#/components/schemas/EmptyOrObject'}
        returnLogistics: {$ref: '#/components/schemas/Logistics'}
        replacementLogistics: {$ref: '#/components/schemas/Logistics'}
        carrierOptions: {type: array, items: {$ref: '#/components/schemas/CarrierOption'}}
    PhysicalOrder:
      type: object
      properties:
        shippingAddress: {$ref: '#/components/schemas/EmptyOrObject'}
        logistics: {$ref: '#/components/schemas/Logistics'}
        carrierOptions: {type: array, items: {$ref: '#/components/schemas/CarrierOption'}}
        canShip: {type: boolean}
        canUpdateLogistics: {type: boolean}
    SellerOrder:
      type: object
      required: [id, orderNo, status, productType, quantity, isPay, isSeller, role]
      description: |
        自有销售订单返回买家实付金额，纯供货订单省略 totalPrice/refundedAmount，提供供货金额 supplyAmount。
        列表为字段子集，操作资格、卡密及履约资料主要在详情提供；未知附加字段请忽略。
      properties:
        id: {type: integer, format: int64}
        orderNo: {type: string}
        goodsName: {$ref: '#/components/schemas/NullableString'}
        skuName: {$ref: '#/components/schemas/NullableString'}
        goodsImage: {$ref: '#/components/schemas/NullableString'}
        productType: {$ref: '#/components/schemas/ProductType'}
        quantity: {type: integer, minimum: 1}
        status: {$ref: '#/components/schemas/OrderStatus'}
        fulfillmentStatus: {type: string}
        isPay: {type: boolean}
        isSeller: {type: boolean}
        role: {type: string, enum: [seller, supplier]}
        totalPrice: {$ref: '#/components/schemas/Money'}
        refundedAmount: {$ref: '#/components/schemas/Money'}
        supplyAmount: {type: [number, 'null'], description: 本店供货金额，最多四位小数；无历史报价为 null，不代表零元。}
        createdAt: {$ref: '#/components/schemas/Timestamp'}
        paymentTime: {$ref: '#/components/schemas/Timestamp'}
        completedAt: {$ref: '#/components/schemas/Timestamp'}
        remark: {$ref: '#/components/schemas/NullableString'}
        message: {$ref: '#/components/schemas/NullableString'}
        processStart: {type: integer}
        processCurrent: {type: integer}
        processTarget: {type: integer}
        canFulfill: {type: boolean}
        canProcessRecharge: {type: boolean}
        canUploadRechargeProof: {type: boolean}
        canRefund: {type: boolean}
        refundPending: {type: boolean}
        inputTemplateSnapshot: {$ref: '#/components/schemas/InputTemplate'}
        normalizedInputValues: {$ref: '#/components/schemas/InputValues'}
        verificationCodeRequired: {type: boolean}
        verificationCodeInfo: {$ref: '#/components/schemas/VerificationCodeInfo'}
        rechargeProof: {$ref: '#/components/schemas/RechargeProof'}
        physical: {$ref: '#/components/schemas/PhysicalOrder'}
        cards:
          allOf:
            - {$ref: '#/components/schemas/Pagination'}
            - type: object
              properties:
                list: {type: array, items: {$ref: '#/components/schemas/SellerCard'}}
          description: GET 详情完整返回；动作响应快照可能每页 100 条。
    SellerOrderPage:
      allOf:
        - {$ref: '#/components/schemas/Pagination'}
        - type: object
          properties:
            list: {type: array, items: {$ref: '#/components/schemas/SellerOrder'}}
    SellerAftersale:
      allOf:
        - {$ref: '#/components/schemas/AftersaleBase'}
        - type: object
          properties:
            orderId: {type: integer, format: int64}
            goodsName: {$ref: '#/components/schemas/NullableString'}
            productType: {$ref: '#/components/schemas/ProductType'}
            canReply: {type: boolean, description: 仅销售方且售后未结束时具有回复资格。}
            canHandleExchange: {type: boolean, description: 实物换货履约资格，具体动作还须匹配服务状态。}
            physical: {$ref: '#/components/schemas/PhysicalAftersale'}
            messages:
              allOf:
                - {$ref: '#/components/schemas/Pagination'}
                - type: object
                  properties:
                    list: {type: array, items: {$ref: '#/components/schemas/SellerMessage'}}
              description: GET 详情完整返回；动作响应快照可能每页 50 条。
    SellerMessage:
      type: object
      properties:
        id: {type: integer, format: int64}
        sender: {type: string, enum: [user, service]}
        senderLabel: {type: string}
        content: {type: string}
        messageFormat: {type: string, description: 常见 text/image。}
        timestamp: {$ref: '#/components/schemas/Timestamp'}
    SellerAftersalePage:
      allOf:
        - {$ref: '#/components/schemas/Pagination'}
        - type: object
          properties:
            list: {type: array, items: {$ref: '#/components/schemas/SellerAftersale'}}
    RechargeProofSaved:
      type: object
      required: [rechargeProof]
      additionalProperties: false
      properties:
        rechargeProof: {$ref: '#/components/schemas/RechargeProof'}
    SuccessEnvelope:
      type: object
      required: [code, message, data, requestId]
      properties:
        code: {type: integer, const: 0}
        message: {type: string}
        requestId: {type: string}
    ErrorResponse:
      type: object
      required: [code, message, data]
      properties:
        code: {type: [integer, string], description: 接口内为数字业务码；域名或网关拒绝可能为字符串码。}
        message: {type: string}
        data: {type: 'null'}
        requestId: {type: string, description: 接口内部错误提供，域名或网关拒绝可能省略。}
    AdminOrderListResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - {type: object, properties: {data: {$ref: '#/components/schemas/AdminOrderPage'}}}
    AdminOrderDetailResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - {type: object, properties: {data: {$ref: '#/components/schemas/AdminOrder'}}}
    AdminProcessResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - {type: object, properties: {data: {type: array, items: {$ref: '#/components/schemas/AdminProcessItem'}}}}
    AdminAftersaleListResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - {type: object, properties: {data: {$ref: '#/components/schemas/AdminAftersalePage'}}}
    AdminAftersaleDetailResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - {type: object, properties: {data: {$ref: '#/components/schemas/AdminAftersale'}}}
    AdminAftersaleProcessResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - type: object
          properties:
            data:
              oneOf:
                - {$ref: '#/components/schemas/BatchAftersaleResult'}
                - {$ref: '#/components/schemas/PhysicalAftersale'}
    SellerOrderListResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - {type: object, properties: {data: {$ref: '#/components/schemas/SellerOrderPage'}}}
    SellerOrderDetailResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - {type: object, properties: {data: {$ref: '#/components/schemas/SellerOrder'}}}
    SellerAftersaleListResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - {type: object, properties: {data: {$ref: '#/components/schemas/SellerAftersalePage'}}}
    SellerAftersaleDetailResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - {type: object, properties: {data: {$ref: '#/components/schemas/SellerAftersale'}}}
    SellerRechargeResponse:
      allOf:
        - {$ref: '#/components/schemas/SuccessEnvelope'}
        - type: object
          properties:
            data:
              oneOf:
                - {$ref: '#/components/schemas/SellerOrder'}
                - {$ref: '#/components/schemas/RechargeProofSaved'}
