openapi: 3.1.0
info:
  title: 巨东AI API
  version: 1.2.0
  description: 巨东AI公开、客户、合作方与后台接口的版本化契约。
  x-version-policy:
    major: URL 路径中的 v1；破坏性变更发布新主版本路径。
    compatibleChanges: v1 只允许新增可选字段、端点和枚举能力；客户端必须忽略未知可选字段。
    deprecation: 废弃端点通过 Deprecation 与 Sunset 响应头公告，生产下线前保留迁移窗口。
servers:
  - url: /api/v1
tags:
  - name: System
    description: 无业务数据的服务状态接口。
  - name: Public
    description: 无需登录、只返回已批准公开DTO的演员目录与授权核验接口；演员与公开宣传媒体端点允许无凭据跨域只读，但公开文件不构成生产素材资格、商业授权或生成调用许可。
  - name: Customer
    description: 仅限当前自然人客户会话及其自有项目、合同和授权。
  - name: Partner
    description: 仅限已审核合作方服务端凭证；禁止浏览器直接持有合作方密钥。
  - name: Admin
    description: 仅限后台角色与资源归属校验后的管理接口，所有写操作进入审计。
x-api-boundaries:
  public:
    pathPrefix: /public
    security: none
    dataRule: 仅返回公开白名单 DTO 与水印派生媒体标识。
  customer:
    pathPrefix: /customer
    security: supabaseSession
    dataRule: 按当前自然人账户和具名项目做对象级授权。
  partner:
    pathPrefix: /partner
    security: partnerCredential
    dataRule: 服务端到服务端、最小范围、可轮换、可审计。
  admin:
    pathPrefix: /admin
    security: supabaseSession + server RBAC
    dataRule: 同时校验后台角色、资源归属与动作权限；默认拒绝。
paths:
  /actors:
    get:
      operationId: searchPublicActors
      summary: 搜索和筛选已发布演员
      tags:
        - Public
      security: []
      x-cors:
        allowOrigin: "*"
        allowCredentials: false
        methods: [GET, OPTIONS]
      parameters:
        - in: query
          name: q
          schema:
            type: string
            maxLength: 120
        - in: query
          name: gender
          description: 当前单值性别筛选。
          schema:
            type: string
            enum: [male, female]
        - in: query
          name: age
          description: 历史可饰演年龄兼容参数；当前客户界面不再生成。
          schema:
            type: array
            maxItems: 5
            items:
              type: string
              enum: [child, teen, young, middle, senior]
          explode: true
        - in: query
          name: role
          description: 题材分类多选；同一维度内按任一值匹配。
          schema:
            type: array
            maxItems: 7
            items:
              type: string
              enum: [republican-retro, urban-contemporary, realistic-costume, mystery-crime, fantasy-xianxia, sci-fi-future, european-court]
          explode: true
        - in: query
          name: suitableRole
          description: 精确适配角色多选；同一维度内按任一值匹配，单值请求保持兼容。
          schema:
            type: array
            maxItems: 20
            items:
              type: string
              minLength: 1
              maxLength: 80
          explode: true
        - in: query
          name: tag
          schema:
            type: array
            maxItems: 20
            items:
              type: string
          explode: true
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            maximum: 48
            default: 24
        - in: query
          name: offset
          schema:
            type: integer
            minimum: 0
            maximum: 10000
            default: 0
      responses:
        "200":
          $ref: "#/components/responses/PublicActorSearchSuccess"
        "400":
          $ref: "#/components/responses/PublicBadRequestError"
        "500":
          $ref: "#/components/responses/PublicInternalError"
        "503":
          $ref: "#/components/responses/PublicServiceUnavailableError"
    options:
      operationId: preflightPublicActors
      summary: 公开演员目录跨域预检
      tags: [Public]
      security: []
      responses:
        "204":
          $ref: "#/components/responses/PublicReadPreflight"
  /actors/{actorCode}:
    get:
      operationId: getPublicActor
      summary: 获取单个已发布演员公开档案
      tags:
        - Public
      security: []
      x-cors:
        allowOrigin: "*"
        allowCredentials: false
        methods: [GET, OPTIONS]
      parameters:
        - in: path
          name: actorCode
          required: true
          schema:
            type: string
            pattern: ^ACT-[A-Z0-9]{6,20}$
        - in: query
          name: appearance
          required: false
          description: 选择该演员已发布形象；无效或不属于该演员的值按默认形象处理。
          schema:
            type: string
            pattern: ^APR-[A-F0-9]{10}$
      responses:
        "200":
          $ref: "#/components/responses/PublicActorDetailSuccess"
        "404":
          $ref: "#/components/responses/PublicNotFoundError"
        "500":
          $ref: "#/components/responses/PublicInternalError"
        "503":
          $ref: "#/components/responses/PublicServiceUnavailableError"
    options:
      operationId: preflightPublicActor
      summary: 公开演员详情跨域预检
      tags: [Public]
      security: []
      responses:
        "204":
          $ref: "#/components/responses/PublicReadPreflight"
  /media/{publicId}:
    get:
      operationId: getPublicMediaDerivative
      summary: 读取已发布安全派生媒体
      tags:
        - Public
      security: []
      x-cors:
        allowOrigin: "*"
        allowCredentials: false
        methods: [GET, OPTIONS]
      parameters:
        - in: path
          name: publicId
          required: true
          schema:
            type: string
            format: uuid
        - in: query
          name: format
          required: true
          schema:
            enum:
              - webp
              - jpg
              - png
              - mp4
              - mp3
              - wav
      responses:
        "200":
          $ref: "#/components/responses/PublicMediaSuccess"
        "206":
          $ref: "#/components/responses/PublicMediaPartialContent"
        "304":
          $ref: "#/components/responses/PublicMediaNotModified"
        "404":
          $ref: "#/components/responses/PublicNotFoundError"
        "416":
          $ref: "#/components/responses/PublicMediaRangeNotSatisfiable"
        "503":
          $ref: "#/components/responses/PublicServiceUnavailableError"
    options:
      operationId: preflightPublicMedia
      summary: 公开媒体跨域预检
      tags: [Public]
      security: []
      responses:
        "204":
          $ref: "#/components/responses/PublicReadPreflight"
  /media/{publicId}/captions:
    get:
      operationId: getPublicMediaCaptions
      summary: 读取当前已发布公开视频的 WebVTT 字幕
      tags:
        - Public
      security: []
      x-cors:
        allowOrigin: "*"
        allowCredentials: false
        methods: [GET, OPTIONS]
      parameters:
        - in: path
          name: publicId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          $ref: "#/components/responses/PublicCaptionsSuccess"
        "404":
          $ref: "#/components/responses/PublicNotFoundError"
        "503":
          $ref: "#/components/responses/PublicServiceUnavailableError"
    options:
      operationId: preflightPublicMediaCaptions
      summary: 公开媒体字幕跨域预检
      tags: [Public]
      security: []
      responses:
        "204":
          $ref: "#/components/responses/PublicReadPreflight"
  /customer/authorization/plan-options:
    get:
      operationId: getAuthorizationPlanOptions
      summary: 校验选角并读取当前项目、场景和测试平台上下文
      tags:
        - Customer
      security:
        - supabaseSession: []
      parameters:
        - in: query
          name: actor
          description: 普通选角入口必须提供1–20个演员编号；与历史sessionId入口互斥。
          required: false
          explode: true
          schema:
            type: array
            minItems: 1
            maxItems: 20
            items:
              type: string
              pattern: ^ACT-[A-Z0-9]{6,20}$
        - in: query
          name: sessionId
          description: 历史合作方会话标识；Phase 14可编辑预选上线前，包含旧locked选择的会话固定返回409且不创建交易事实。
          required: false
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: 当前账户可用的项目、末级场景、测试平台上下文和服务端演员定价输入。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthorizationPlanOptionsResponse"
        "400":
          description: 选角参数无效。
        "401":
          description: 缺少有效客户会话。
        "403":
          description: 账户状态或主体类型不可创建个人授权。
        "404":
          description: 一名或多名演员已不可售。
        "409":
          description: 历史locked合作会话不能创建新的按秒订单。
        "503":
          description: 项目或价格服务暂时不可用。
  /customer/orders/drafts:
    post:
      operationId: createCustomerOrderDraft
      summary: 服务端重算并锁定项目版本、订单草稿和人民币价格快照
      tags:
        - Customer
      security:
        - supabaseSession: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateOrderDraftRequest"
      responses:
        "201":
          description: 已创建不可被后续调价改写的订单草稿和逐演员价格快照。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderDraftResponse"
        "400":
          description: 项目、场景、年限或选角参数无效。
        "401":
          description: 缺少有效客户会话。
        "403":
          description: 账户或项目所有权校验失败。
        "404":
          description: 项目、场景或演员已不可用。
        "409":
          description: 生效项目被直接修改、合作平台变更或幂等状态冲突。
        "503":
          description: 订单草稿服务暂时不可用。
  /partner/authorization-sessions:
    post:
      operationId: createPartnerAuthorizationSession
      summary: 由已审核合作平台后端直接建立或恢复服务端身份映射
      tags:
        - Partner
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePartnerAuthorizationSessionRequest"
      responses:
        "201":
          description: 服务端身份映射已建立；响应不含授权URL、state、回跳、Cookie或浏览器会话。
          headers:
            x-request-id:
              schema:
                type: string
            Cache-Control:
              schema:
                const: no-store
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerAuthorizationSessionCreatedResponse"
        "400":
          $ref: "#/components/responses/PartnerApiError"
        "401":
          $ref: "#/components/responses/PartnerApiError"
        "403":
          $ref: "#/components/responses/PartnerApiError"
        "409":
          $ref: "#/components/responses/PartnerApiError"
        "413":
          $ref: "#/components/responses/PartnerApiError"
        "429":
          $ref: "#/components/responses/PartnerApiError"
        "503":
          $ref: "#/components/responses/PartnerApiError"
  /partner/authorization-sessions/{sessionId}:
    get:
      operationId: getPartnerAuthorizationSession
      summary: 查询本平台发起的授权会话结果
      tags:
        - Partner
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      responses:
        "200":
          description: 仅返回当前认证平台与环境拥有的会话允许列表 DTO。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerAuthorizationSessionResponse"
        "400":
          $ref: "#/components/responses/PartnerApiError"
        "401":
          $ref: "#/components/responses/PartnerApiError"
        "403":
          $ref: "#/components/responses/PartnerApiError"
        "404":
          $ref: "#/components/responses/PartnerApiError"
        "409":
          $ref: "#/components/responses/PartnerApiError"
        "429":
          $ref: "#/components/responses/PartnerApiError"
        "503":
          $ref: "#/components/responses/PartnerApiError"
  /partner/generation-reservations:
    post:
      operationId: reservePartnerGenerationQuota
      summary: 在模型启动前为一个合作方测试任务原子签发调用许可并预留逐演员额度
      description: 当前端点只接受test环境；production未启用并返回ENVIRONMENT_NOT_READY。未来production必须通过Phase 32B独立门禁开放。
      x-required-scope: generation:reserve
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ReserveGenerationQuotaRequest" }
      responses:
        "201":
          description: 已形成绑定当前平台、test环境、项目、任务、演员集合和秒数的一次受控测试调用许可，并原子预留全部演员额度；该结果无法律效力、不产生真实扣款且不能升级为production事实。失败时不预留任何演员。
          content:
            application/json:
              schema: { $ref: "#/components/schemas/GenerationReservationResponse" }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "404": { $ref: "#/components/responses/PartnerApiError" }
        "409": { $ref: "#/components/responses/PartnerApiError" }
        "413": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
  /partner/generation-reservations/{externalGenerationId}:
    get:
      operationId: getPartnerGenerationReservation
      summary: 查询本环境、项目和任务的调用许可及额度预留
      description: 当前端点只接受test环境；production未启用并返回ENVIRONMENT_NOT_READY。
      x-required-scope: generation:read
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - { in: path, name: externalGenerationId, required: true, schema: { type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$' } }
        - { in: query, name: projectId, required: true, schema: { type: string, format: uuid } }
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      responses:
        "200":
          description: 返回调用许可状态与逐演员额度事实；不返回价格、私有路径或内部事件账本。test状态始终无法律效力。
          content:
            application/json:
              schema: { $ref: "#/components/schemas/GenerationReservationResponse" }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "404": { $ref: "#/components/responses/PartnerApiError" }
        "409": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
  /partner/generation-reservations/{externalGenerationId}/extend:
    post:
      operationId: extendPartnerGenerationReservation
      summary: 在演员授权有效期内将预留续期至数据库当前时间后60分钟
      description: 当前端点只接受test环境；production未启用并返回ENVIRONMENT_NOT_READY。
      x-required-scope: generation:reserve
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - { in: path, name: externalGenerationId, required: true, schema: { type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$' } }
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/GenerationMutationRequest" }
      responses:
        "200": { description: 续期成功或同请求幂等重放。, content: { application/json: { schema: { $ref: "#/components/schemas/GenerationReservationResponse" } } } }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "404": { $ref: "#/components/responses/PartnerApiError" }
        "409": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
  /partner/generation-reservations/{externalGenerationId}/success:
    post:
      operationId: settlePartnerGenerationSuccess
      summary: 以成功视频编号和精确生成时长结算预留
      description: 当前端点只接受test环境；production未启用并返回ENVIRONMENT_NOT_READY。
      x-required-scope: generation:settle
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - { in: path, name: externalGenerationId, required: true, schema: { type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$' } }
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/GenerationSuccessRequest" }
      responses:
        "200": { description: 可播放视频成功并消费全部演员的精确时长。, content: { application/json: { schema: { $ref: "#/components/schemas/GenerationReservationResponse" } } } }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "404": { $ref: "#/components/responses/PartnerApiError" }
        "409": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
  /partner/generation-reservations/{externalGenerationId}/fail:
    post:
      operationId: releaseFailedPartnerGeneration
      summary: 对没有可播放输出的失败任务释放全部演员预留
      description: 当前端点只接受test环境；production未启用并返回ENVIRONMENT_NOT_READY。
      x-required-scope: generation:settle
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - { in: path, name: externalGenerationId, required: true, schema: { type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$' } }
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/GenerationMutationRequest" }
      responses:
        "200": { description: 失败任务的全部演员额度已原子释放。, content: { application/json: { schema: { $ref: "#/components/schemas/GenerationReservationResponse" } } } }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "404": { $ref: "#/components/responses/PartnerApiError" }
        "409": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
  /partner/generation-reservations/{externalGenerationId}/cancel:
    post:
      operationId: releaseCancelledPartnerGeneration
      summary: 取消任务并释放全部演员预留
      description: 当前端点只接受test环境；production未启用并返回ENVIRONMENT_NOT_READY。
      x-required-scope: generation:settle
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - { in: path, name: externalGenerationId, required: true, schema: { type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$' } }
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/GenerationMutationRequest" }
      responses:
        "200": { description: 已取消并原子释放全部演员额度。, content: { application/json: { schema: { $ref: "#/components/schemas/GenerationReservationResponse" } } } }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "404": { $ref: "#/components/responses/PartnerApiError" }
        "409": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
  /partner/material-access:
    post:
      operationId: issuePartnerMaterialAccess
      summary: 为有效测试授权签发短时单用途受控素材访问
      x-required-scope: materials:issue
      tags:
        - Partner
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IssueMaterialAccessRequest"
      responses:
        "201":
          description: 仅本次 no-store 响应返回一次性 token；不返回 Storage 地址或私有路径。
          headers:
            x-request-id:
              schema:
                type: string
            Cache-Control:
              schema:
                const: private, no-store, max-age=0
            Pragma:
              schema:
                const: no-cache
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MaterialAccessIssuedResponse"
        "400":
          $ref: "#/components/responses/PartnerApiError"
        "401":
          $ref: "#/components/responses/PartnerApiError"
        "403":
          $ref: "#/components/responses/PartnerApiError"
        "409":
          $ref: "#/components/responses/PartnerApiError"
        "413":
          $ref: "#/components/responses/PartnerApiError"
        "429":
          $ref: "#/components/responses/PartnerApiError"
        "503":
          $ref: "#/components/responses/PartnerApiError"
  /partner/material-access/{requestId}/content:
    post:
      operationId: consumePartnerMaterialAccess
      summary: 使用新鲜合作方签名与一次性 token 获取受控素材字节
      x-required-scope: materials:consume
      tags:
        - Partner
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - in: path
          name: requestId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
        - $ref: "#/components/parameters/MaterialAccessToken"
      responses:
        "200":
          description: 单次受控字节响应；token 在数据库中已消费，且返回前完成最终状态复核。
          headers:
            x-request-id:
              schema:
                type: string
            Cache-Control:
              schema:
                const: private, no-store, max-age=0
            Pragma:
              schema:
                const: no-cache
            Referrer-Policy:
              schema:
                const: no-referrer
            X-Content-Type-Options:
              schema:
                const: nosniff
            Content-Disposition:
              schema:
                type: string
                pattern: ^attachment;
          content:
            image/jpeg:
              schema:
                type: string
                format: binary
            image/png:
              schema:
                type: string
                format: binary
            image/webp:
              schema:
                type: string
                format: binary
            video/mp4:
              schema:
                type: string
                format: binary
            audio/mpeg:
              schema:
                type: string
                format: binary
            audio/wav:
              schema:
                type: string
                format: binary
        "400":
          $ref: "#/components/responses/PartnerApiError"
        "401":
          $ref: "#/components/responses/PartnerApiError"
        "403":
          $ref: "#/components/responses/PartnerApiError"
        "404":
          $ref: "#/components/responses/PartnerApiError"
        "409":
          $ref: "#/components/responses/PartnerApiError"
        "413":
          $ref: "#/components/responses/PartnerApiError"
        "429":
          $ref: "#/components/responses/PartnerApiError"
        "503":
          $ref: "#/components/responses/PartnerApiError"
  /partner/callback-signing-keys:
    get:
      operationId: getPartnerCallbackSigningKeys
      summary: 获取测试环境 callback 验签公钥
      tags:
        - Partner
      security: []
      responses:
        "200":
          description: 只返回 current/previous 公钥允许列表；绝不返回私钥。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerCallbackSigningKeysResponse"
  /partner/purchase-options:
    get:
      operationId: getPartnerPurchaseOptions
      summary: 查询后台购买的固定批发规则与可用使用场景
      description: 返回逐演员最低1500秒（60秒步进，上限9000秒）、年限1-3年与33分/秒价格规则的当前值；仅限test环境。
      x-required-scope: purchase:read
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      responses:
        "200":
          description: 当前购买规则快照；价格与秒数规则由服务端权威返回，不写入合作方。
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PurchaseOptionsResponse" }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
  /partner/purchase-orders:
    post:
      operationId: createPartnerPlatformPurchase
      summary: 以预存余额一次性原子购买逐演员授权与生成秒数额度
      description: 单事务全有或全无：余额不足、任一参数非法或任一环节失败时整体回滚，不产生部分扣款或部分授权。同一幂等键重放同一载荷返回原结果；同键不同载荷返回IDEMPOTENCY_CONFLICT。test环境为test_only无法律效力。
      x-required-scope: purchase:create
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PlatformPurchaseRequest" }
      responses:
        "201":
          description: 已原子完成：预存余额扣款、合同包、逐演员授权与首个秒数额度批次；返回扣款后余额与逐演员授权清单。
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PlatformPurchaseResponse" }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "402": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "404": { $ref: "#/components/responses/PartnerApiError" }
        "409": { $ref: "#/components/responses/PartnerApiError" }
        "413": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
    get:
      operationId: getPartnerPlatformPurchase
      summary: 按外部订单号查询本环境的后台购买及其授权结果
      description: 返回购买状态、总金额、合同号与逐演员授权事实；仅限test环境。
      x-required-scope: purchase:read
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - { in: query, name: externalOrderId, required: true, schema: { type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$' } }
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      responses:
        "200":
          description: 购买状态与逐演员授权事实；不存在时found=false。
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PlatformPurchaseQueryResponse" }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
  /partner/fund-balance:
    get:
      operationId: getPartnerFundBalance
      summary: 查询本合作方测试环境的预存余额
      description: 返回以分为单位的余额与最近一次账本更新时间；仅限test环境。
      x-required-scope: funds:read
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      responses:
        "200":
          description: 当前预存余额。
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FundBalanceResponse" }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
  /partner/fund-events:
    get:
      operationId: listPartnerFundEvents
      summary: 分页查询本环境的资金流水
      description: 返回带符号金额（购买扣款为负数）的不可变流水；支持limit与before游标；仅限test环境。
      x-required-scope: funds:read
      tags: [Partner]
      security:
        - partnerCredential: []
          partnerEnvironment: []
          partnerAudience: []
          partnerTimestamp: []
          partnerNonce: []
          partnerSignature: []
      parameters:
        - { in: query, name: limit, required: false, schema: { type: integer, minimum: 1, maximum: 100, default: 50 } }
        - { in: query, name: before, required: false, schema: { type: string, format: date-time } }
        - $ref: "#/components/parameters/PartnerCredential"
        - $ref: "#/components/parameters/PartnerTestEnvironment"
        - $ref: "#/components/parameters/PartnerAudience"
        - $ref: "#/components/parameters/PartnerTimestamp"
        - $ref: "#/components/parameters/PartnerNonce"
        - $ref: "#/components/parameters/PartnerSignature"
      responses:
        "200":
          description: 按时间倒序的资金流水页。
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FundEventsResponse" }
        "400": { $ref: "#/components/responses/PartnerApiError" }
        "401": { $ref: "#/components/responses/PartnerApiError" }
        "403": { $ref: "#/components/responses/PartnerApiError" }
        "429": { $ref: "#/components/responses/PartnerApiError" }
        "503": { $ref: "#/components/responses/PartnerApiError" }
  /health:
    get:
      operationId: getHealth
      summary: 获取服务健康状态
      tags:
        - System
      responses:
        "200":
          description: 服务可响应
          headers:
            x-request-id:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
        "500":
          description: 服务配置无效
          headers:
            x-request-id:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
components:
  parameters:
    ActorId:
      in: path
      name: actorId
      required: true
      schema:
        type: string
        format: uuid
    PartnerCredential:
      in: header
      name: x-jd-credential
      required: true
      schema:
        type: string
        format: uuid
    PartnerEnvironment:
      in: header
      name: x-jd-environment
      required: true
      schema:
        enum:
          - test
          - production
    PartnerTestEnvironment:
      in: header
      name: x-jd-environment
      required: true
      description: 当前生成调用许可端点只接受test；production未启用并返回ENVIRONMENT_NOT_READY。
      schema:
        type: string
        const: test
    PartnerAudience:
      in: header
      name: x-jd-audience
      required: true
      schema:
        type: string
        minLength: 4
        maxLength: 200
    PartnerTimestamp:
      in: header
      name: x-jd-timestamp
      required: true
      schema:
        type: string
        pattern: ^[0-9]{1,12}$
    PartnerNonce:
      in: header
      name: x-jd-nonce
      required: true
      schema:
        type: string
        pattern: ^[A-Za-z0-9_-]{22,86}$
    PartnerSignature:
      in: header
      name: x-jd-signature
      required: true
      schema:
        type: string
        pattern: ^[A-Za-z0-9_-]{86}$
    MaterialAccessToken:
      in: header
      name: x-jd-material-token
      required: true
      schema:
        type: string
        pattern: ^[A-Za-z0-9_-]{43}$
  headers:
    PublicAllowOrigin:
      description: 公开只读端点允许任意Origin，不允许凭据。
      schema:
        type: string
        const: "*"
    PublicAllowMethods:
      description: 公开只读端点只允许GET和OPTIONS。
      schema:
        type: string
        const: GET, OPTIONS
    PublicAllowHeaders:
      description: 公开预检允许的无凭据请求头。
      schema:
        type: string
        const: Accept, Content-Type, If-None-Match, Range, X-Request-Id
    PublicExposeHeaders:
      description: 浏览器允许读取的公开响应头。
      schema:
        type: string
        const: Accept-Ranges, Content-Length, Content-Range, Content-Type, ETag, X-Request-Id
    PublicCrossOriginResourcePolicy:
      description: 允许跨Origin加载公开资源。
      schema:
        type: string
        const: cross-origin
    PublicRequestId:
      description: 报障和日志关联使用的非秘密请求标识。
      schema:
        type: string
    PublicEtag:
      description: 公开媒体实体标签。
      schema:
        type: string
    PublicAcceptRanges:
      description: 公开媒体支持字节范围读取。
      schema:
        type: string
        const: bytes
    PublicContentRange:
      description: 当前媒体字节范围或不可满足范围的总长度。
      schema:
        type: string
    PublicContentLength:
      description: 当前响应正文的字节长度。
      schema:
        type: string
    PublicPreflightMaxAge:
      description: 浏览器可缓存预检结果的秒数。
      schema:
        type: string
        const: "86400"
  responses:
    PublicActorSearchSuccess:
      description: 只含已发布公开字段和已确认公开的安全媒体入口。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PublicActorSearchResponse"
    PublicActorDetailSuccess:
      description: 公开结构化档案、已发布形象、作品和已确认公开的安全媒体。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PublicActorDetailResponse"
    PublicMediaSuccess:
      description: 完整公开安全派生文件；当前发布状态已复核，不授予商业使用权。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
        ETag:
          $ref: "#/components/headers/PublicEtag"
        Accept-Ranges:
          $ref: "#/components/headers/PublicAcceptRanges"
        Content-Length:
          $ref: "#/components/headers/PublicContentLength"
      content:
        application/octet-stream:
          schema:
            type: string
            format: binary
    PublicMediaPartialContent:
      description: 单段Range公开媒体响应。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
        ETag:
          $ref: "#/components/headers/PublicEtag"
        Accept-Ranges:
          $ref: "#/components/headers/PublicAcceptRanges"
        Content-Range:
          $ref: "#/components/headers/PublicContentRange"
        Content-Length:
          $ref: "#/components/headers/PublicContentLength"
      content:
        application/octet-stream:
          schema:
            type: string
            format: binary
    PublicMediaNotModified:
      description: 当前公开媒体ETag未变化，无响应正文。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
        ETag:
          $ref: "#/components/headers/PublicEtag"
    PublicMediaRangeNotSatisfiable:
      description: 请求字节范围超出当前公开媒体大小，无响应正文。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
        Content-Range:
          $ref: "#/components/headers/PublicContentRange"
    PublicCaptionsSuccess:
      description: UTF-8 WebVTT字幕；响应禁止缓存。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
      content:
        text/vtt:
          schema:
            type: string
    PublicReadPreflight:
      description: 无正文跨域预检；不访问数据库或业务服务。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        Access-Control-Max-Age:
          $ref: "#/components/headers/PublicPreflightMaxAge"
    PublicBadRequestError:
      description: 公开读取参数无效。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    PublicNotFoundError:
      description: 公开演员、媒体或字幕不存在、未发布或已撤回。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    PublicInternalError:
      description: 公开读取发生未预期内部错误。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    PublicServiceUnavailableError:
      description: 公开演员或媒体服务暂时不可用。
      headers:
        Access-Control-Allow-Origin:
          $ref: "#/components/headers/PublicAllowOrigin"
        Access-Control-Allow-Methods:
          $ref: "#/components/headers/PublicAllowMethods"
        Access-Control-Allow-Headers:
          $ref: "#/components/headers/PublicAllowHeaders"
        Access-Control-Expose-Headers:
          $ref: "#/components/headers/PublicExposeHeaders"
        Cross-Origin-Resource-Policy:
          $ref: "#/components/headers/PublicCrossOriginResourcePolicy"
        x-request-id:
          $ref: "#/components/headers/PublicRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    PartnerApiError:
      description: 合作方请求因字段、认证、scope、环境、重放、限流或服务状态失败。
      headers:
        x-request-id:
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PartnerErrorResponse"
  securitySchemes:
    supabaseSession:
      type: http
      scheme: bearer
      bearerFormat: Supabase JWT
      description: 逻辑上的单一 Supabase Auth 会话。浏览器由 Supabase SSR 管理动态命名且可能分块的 HttpOnly Cookie；服务端到服务端可使用同一会话 JWT。管理员与客户不使用两套 Cookie，权限由服务端账户状态、数据库角色和对象归属判定。
    partnerCredential:
      type: apiKey
      in: header
      name: x-jd-credential
      description: 合作方 Ed25519 public credential id；私钥只存在于合作方后端。
    partnerEnvironment:
      type: apiKey
      in: header
      name: x-jd-environment
    partnerAudience:
      type: apiKey
      in: header
      name: x-jd-audience
    partnerTimestamp:
      type: apiKey
      in: header
      name: x-jd-timestamp
    partnerNonce:
      type: apiKey
      in: header
      name: x-jd-nonce
    partnerSignature:
      type: apiKey
      in: header
      name: x-jd-signature
  schemas:
    ResponseMeta:
      type: object
      required:
        - requestId
      properties:
        requestId:
          type: string
    PartnerResponseMeta:
      type: object
      additionalProperties: false
      required:
        - request_id
      properties:
        request_id:
          type: string
        access_request_id:
          type: string
          format: uuid
    PartnerErrorResponse:
      type: object
      additionalProperties: false
      required:
        - error
        - meta
      properties:
        error:
          type: object
          additionalProperties: false
          required:
            - code
            - message
          properties:
            code:
              enum:
                - BAD_REQUEST
                - MISSING_AUTHENTICATION
                - MALFORMED_AUTHENTICATION
                - INVALID_SIGNATURE
                - STALE_REQUEST
                - AUDIENCE_MISMATCH
                - ENVIRONMENT_MISMATCH
                - PLATFORM_UNAVAILABLE
                - CREDENTIAL_UNAVAILABLE
                - ENVIRONMENT_NOT_READY
                - SCOPE_FORBIDDEN
                - REDIRECT_NOT_ALLOWED
                - RATE_LIMITED
                - REPLAY_DETECTED
                - SESSION_FORBIDDEN
                - SESSION_NOT_FOUND
                - MATERIAL_ACCESS_DENIED
                - ISSUANCE_ALREADY_COMMITTED
                - IDEMPOTENCY_CONFLICT
                - CONFLICT
                - INSUFFICIENT_FUNDS
                - EXTERNAL_CUSTOMER_UNMAPPED
                - SERVICE_UNAVAILABLE
            message:
              type: string
        meta:
          $ref: "#/components/schemas/PartnerResponseMeta"
    EditableActorSelectionRequest:
      type: object
      additionalProperties: false
      required:
        - version
        - mode
        - actorCodes
      properties:
        version:
          const: 2
        mode:
          const: editable
        actorCodes:
          type: array
          minItems: 1
          maxItems: 20
          uniqueItems: true
          items:
            type: string
            pattern: ^ACT-[A-Z0-9]{6,20}$
    EditableActorSelection:
      type: object
      additionalProperties: false
      description: 已创建会话的不可变可编辑预选快照；省略预选时actorCodes为空数组。
      required:
        - version
        - mode
        - actorCodes
      properties:
        version:
          const: 2
        mode:
          const: editable
        actorCodes:
          type: array
          minItems: 0
          maxItems: 20
          uniqueItems: true
          items:
            type: string
            pattern: ^ACT-[A-Z0-9]{6,20}$
    HistoricalLockedActorSelection:
      type: object
      additionalProperties: false
      description: 仅历史查询兼容；新建会话不接受此模式且不能进入新订单。
      required:
        - version
        - mode
        - actorCodes
      properties:
        version:
          const: 1
        mode:
          const: locked
        actorCodes:
          type: array
          minItems: 1
          maxItems: 20
          uniqueItems: true
          items:
            type: string
            pattern: ^ACT-[A-Z0-9]{6,20}$
    PartnerActorSelection:
      oneOf:
        - $ref: "#/components/schemas/EditableActorSelection"
        - $ref: "#/components/schemas/HistoricalLockedActorSelection"
      discriminator:
        propertyName: version
    PartnerAuthorizationResult:
      type: object
      additionalProperties: false
      required:
        - projectId
        - projectVersionId
        - contractPackageId
        - activationId
        - environment
        - legalEffect
        - realCharge
        - productionMaterialAccess
      properties:
        projectId:
          type: string
          format: uuid
        projectVersionId:
          type: string
          format: uuid
        contractPackageId:
          type: string
          format: uuid
        activationId:
          type: string
          format: uuid
        environment:
          const: test
        legalEffect:
          const: test_only
        realCharge:
          const: false
        productionMaterialAccess:
          const: false
        actorCodes:
          type: array
          maxItems: 20
          uniqueItems: true
          items:
            type: string
            pattern: ^ACT-[A-Z0-9]{6,20}$
    CreatePartnerAuthorizationSessionRequest:
      type: object
      additionalProperties: false
      required:
        - externalUserId
        - externalProjectId
        - contactEmail
        - emailVerified
        - emailVerificationMethod
        - requestedScopes
      properties:
        externalUserId:
          type: string
          minLength: 1
          maxLength: 200
        externalProjectId:
          type: string
          minLength: 1
          maxLength: 200
        contactEmail:
          type: string
          format: email
          maxLength: 254
        emailVerified:
          const: true
        emailVerificationMethod:
          const: partner_account_v1
        requestedScopes:
          type: array
          prefixItems:
            - const: projects:bind
          minItems: 1
          maxItems: 1
        selection:
          $ref: "#/components/schemas/EditableActorSelectionRequest"
    PartnerAuthorizationSession:
      type: object
      additionalProperties: false
      required:
        - sessionId
        - status
        - environment
        - externalProjectId
        - expiresAt
      properties:
        sessionId:
          type: string
          format: uuid
        status:
          enum:
            - created
            - claimed
            - completed
            - cancelled
            - expired
        environment:
          enum:
            - test
            - production
        externalProjectId:
          type: string
        expiresAt:
          type: string
          format: date-time
        accountLinked:
          type: boolean
        claimedAt:
          type:
            - string
            - "null"
          format: date-time
        completedAt:
          type:
            - string
            - "null"
          format: date-time
        cancelledAt:
          type:
            - string
            - "null"
          format: date-time
        selection:
          $ref: "#/components/schemas/PartnerActorSelection"
        result:
          $ref: "#/components/schemas/PartnerAuthorizationResult"
    PartnerAuthorizationSessionCreated:
      type: object
      additionalProperties: false
      required:
        - sessionId
        - status
        - environment
        - externalProjectId
        - expiresAt
        - platformSlug
        - accountLinked
        - selection
      properties:
        sessionId:
          type: string
          format: uuid
        status:
          const: claimed
        environment:
          const: test
        externalProjectId:
          type: string
        expiresAt:
          type: string
          format: date-time
        platformSlug:
          type: string
          pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
        accountLinked:
          const: true
        selection:
          $ref: "#/components/schemas/EditableActorSelection"
    PartnerAuthorizationSessionCreatedResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          $ref: "#/components/schemas/PartnerAuthorizationSessionCreated"
        meta:
          $ref: "#/components/schemas/PartnerResponseMeta"
    PartnerAuthorizationSessionResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          $ref: "#/components/schemas/PartnerAuthorizationSession"
        meta:
          $ref: "#/components/schemas/PartnerResponseMeta"
    GenerationReservationItem:
      type: object
      additionalProperties: false
      required: [actorCode, licenseId, licenseYears, purchasedSeconds, consumedSeconds, reservedSeconds, remainingSeconds, expiresAt, status]
      properties:
        actorCode: { type: string, pattern: '^ACT-[A-Z0-9]{6,20}$' }
        licenseId: { type: string, format: uuid }
        licenseYears: { type: integer, minimum: 1, maximum: 3 }
        purchasedSeconds: { type: integer, minimum: 60, maximum: 9000, multipleOf: 60 }
        consumedSeconds: { type: integer, minimum: 0 }
        reservedSeconds: { type: integer, minimum: 0 }
        remainingSeconds: { type: integer, minimum: 0 }
        expiresAt: { type: string, format: date-time }
        status: { type: string, enum: [usable, exhausted, suspended, expired, terminated] }
    GenerationReservation:
      type: object
      description: 与一个平台环境、项目、外部任务、演员集合和秒数绑定的调用许可及额度预留。当前API只允许test环境，因此reserved仅表示无法律效力的受控测试许可；未来production语义受独立门禁约束。
      additionalProperties: false
      required: [reservationId, projectId, externalGenerationId, requestedSeconds, status, expiresAt, settledAt, items]
      properties:
        reservationId: { type: string, format: uuid }
        projectId: { type: string, format: uuid }
        externalGenerationId: { type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$' }
        requestedSeconds: { type: integer, minimum: 1, maximum: 9000 }
        status: { type: string, enum: [reserved, consumed, released_failed, released_cancelled, released_expired] }
        expiresAt: { type: string, format: date-time }
        settledAt: { type: [string, "null"], format: date-time }
        items:
          type: array
          minItems: 1
          maxItems: 20
          items: { $ref: "#/components/schemas/GenerationReservationItem" }
    GenerationReservationResponse:
      type: object
      additionalProperties: false
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/GenerationReservation" }
        meta: { $ref: "#/components/schemas/PartnerResponseMeta" }
    PlatformPurchaseActorConfiguration:
      type: object
      description: 逐演员购买配置；generationSeconds为60的倍数且最低1500秒，licenseYears 1-3年。合作方不提交价格；服务端按演员当前权威价计算并在购买结果中返回快照（当前共享测试演员均为33分/秒）。
      additionalProperties: false
      required: [actorCode, licenseYears, generationSeconds]
      properties:
        actorCode: { type: string, pattern: '^ACT-[A-Z0-9]{6,20}$' }
        licenseYears: { type: integer, minimum: 1, maximum: 3 }
        generationSeconds: { type: integer, minimum: 1500, maximum: 9000, multipleOf: 60 }
    PlatformPurchaseRequest:
      type: object
      description: 合作方代表其终端客户提交的一次性购买；外部客户必须先通过授权会话领取完成绑定，否则返回EXTERNAL_CUSTOMER_UNMAPPED。customerAcceptedAt为画布侧记录的客户确认时间。
      additionalProperties: false
      required: [externalOrderId, externalCustomerId, subjectKind, subjectName, identityVerified, identityVerifiedAt, identityVerificationReference, customerAcceptedAt, acceptanceReference, projectName, workType, synopsis, releaseChannels, actorConfigurations, idempotencyKey]
      properties:
        externalOrderId: { type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$' }
        externalCustomerId: { type: string, minLength: 1, maxLength: 200 }
        subjectKind: { type: string, enum: [individual, business] }
        subjectName: { type: string, minLength: 2, maxLength: 200 }
        identityVerified: { type: boolean, enum: [true] }
        identityVerifiedAt: { type: string, format: date-time }
        identityVerificationReference: { type: string, minLength: 8, maxLength: 200, pattern: '^[^\\x00-\\x1F\\x7F]+$' }
        customerAcceptedAt: { type: string, format: date-time }
        acceptanceReference: { type: string, minLength: 4, maxLength: 500 }
        projectName: { type: string, minLength: 2, maxLength: 120 }
        workType: { type: string, minLength: 2, maxLength: 80 }
        synopsis: { type: string, minLength: 10, maxLength: 2000 }
        releaseChannels:
          type: array
          minItems: 1
          maxItems: 10
          uniqueItems: true
          items: { type: string, minLength: 1, maxLength: 120 }
        scenarioId: { type: string, format: uuid, description: 必填，从 /partner/purchase-options 的 scenarios 选择 }
        actorConfigurations:
          type: array
          minItems: 1
          maxItems: 20
          items: { $ref: "#/components/schemas/PlatformPurchaseActorConfiguration" }
        idempotencyKey: { type: string, minLength: 16, maxLength: 200, pattern: '^[^\x00-\x1F\x7F]+$' }
    PlatformPurchaseLicense:
      type: object
      additionalProperties: false
      required: [actorCode, licenseId, licenseYears, purchasedSeconds, pricePerSecondCents, generationExpiresAt, commercialExpiresAt, verificationPublicNo, licenseStatus]
      properties:
        actorCode: { type: string, pattern: '^ACT-[A-Z0-9]{6,20}$' }
        licenseId: { type: string, format: uuid }
        licenseYears: { type: integer, minimum: 1, maximum: 3 }
        purchasedSeconds: { type: integer, minimum: 60, maximum: 9000 }
        pricePerSecondCents: { type: integer, minimum: 1 }
        generationExpiresAt: { type: string, format: date-time }
        commercialExpiresAt: { type: string, format: date-time }
        verificationPublicNo: { type: string }
        licenseStatus: { type: string }
    PlatformPurchaseResult:
      type: object
      additionalProperties: false
      required: [purchaseId, status, externalOrderId, totalCents, balanceAfterCents, contractNo, packageId, projectId, effectiveAt, subjectKind, subjectName, identityVerified, identityVerifiedAt, identityVerificationReference, licenses]
      properties:
        purchaseId: { type: string, format: uuid }
        status: { type: string, enum: [settled] }
        externalOrderId: { type: string }
        totalCents: { type: integer, minimum: 1 }
        balanceAfterCents: { type: integer, minimum: 0 }
        contractNo: { type: string }
        packageId: { type: string, format: uuid }
        projectId: { type: string, format: uuid }
        effectiveAt: { type: string, format: date-time }
        subjectKind: { type: string, enum: [individual, business] }
        subjectName: { type: string }
        identityVerified: { type: boolean, enum: [true] }
        identityVerifiedAt: { type: string, format: date-time }
        identityVerificationReference: { type: string, minLength: 8, maxLength: 200, pattern: '^[^\\x00-\\x1F\\x7F]+$' }
        licenses:
          type: array
          minItems: 1
          maxItems: 20
          items: { $ref: "#/components/schemas/PlatformPurchaseLicense" }
    PlatformPurchaseResponse:
      type: object
      additionalProperties: false
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/PlatformPurchaseResult" }
        meta: { $ref: "#/components/schemas/PartnerResponseMeta" }
    PlatformPurchaseQueryResult:
      type: object
      additionalProperties: false
      required: [found, externalOrderId]
      properties:
        found: { type: boolean }
        externalOrderId: { type: string }
        status: { type: string, enum: [prepared, settled, cancelled, expired] }
        totalCents: { type: integer, minimum: 1 }
        subjectKind: { type: string, enum: [individual, business] }
        subjectName: { type: string }
        identityVerified: { type: [boolean, "null"] }
        identityVerifiedAt: { type: [string, "null"], format: date-time }
        identityVerificationReference: { type: [string, "null"], minLength: 8, maxLength: 200, pattern: '^[^\\x00-\\x1F\\x7F]+$' }
        customerAcceptedAt: { type: [string, "null"], format: date-time }
        contractNo: { type: [string, "null"] }
        packageId: { type: [string, "null"], format: uuid }
        projectId: { type: [string, "null"], format: uuid }
        settledAt: { type: [string, "null"], format: date-time }
        createdAt: { type: string, format: date-time }
        licenses:
          type: array
          items: { $ref: "#/components/schemas/PlatformPurchaseLicense" }
    PlatformPurchaseQueryResponse:
      type: object
      additionalProperties: false
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/PlatformPurchaseQueryResult" }
        meta: { $ref: "#/components/schemas/PartnerResponseMeta" }
    PurchaseOptionsResult:
      type: object
      additionalProperties: false
      required: [minimumGenerationSecondsPerActor, maximumGenerationSecondsPerActor, generationSecondsStep, licenseYears, scenarios]
      properties:
        minimumGenerationSecondsPerActor: { type: integer }
        maximumGenerationSecondsPerActor: { type: integer }
        generationSecondsStep: { type: integer }
        licenseYears:
          type: array
          items: { type: integer, minimum: 1, maximum: 3 }
        scenarios:
          type: array
          items:
            type: object
            additionalProperties: false
            required: [scenarioId, scenarioName]
            properties:
              scenarioId: { type: string, format: uuid }
              scenarioName: { type: string }
    PurchaseOptionsResponse:
      type: object
      additionalProperties: false
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/PurchaseOptionsResult" }
        meta: { $ref: "#/components/schemas/PartnerResponseMeta" }
    FundBalanceResult:
      type: object
      additionalProperties: false
      required: [balanceCents, lastEventAt]
      properties:
        balanceCents: { type: integer, minimum: 0 }
        lastEventAt: { type: [string, "null"], format: date-time }
    FundBalanceResponse:
      type: object
      additionalProperties: false
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/FundBalanceResult" }
        meta: { $ref: "#/components/schemas/PartnerResponseMeta" }
    FundEvent:
      type: object
      description: 不可变资金事件；signedAmountCents为带符号金额（购买扣款为负数）。
      additionalProperties: false
      required: [eventId, eventType, signedAmountCents, currency, externalReference, purchaseExternalOrderId, operatorKind, createdAt]
      properties:
        eventId: { type: string, format: uuid }
        eventType: { type: string, enum: [topup, purchase_debit, refund_credit, adjustment] }
        signedAmountCents: { type: integer }
        currency: { type: string, enum: [CNY] }
        externalReference: { type: [string, "null"] }
        purchaseExternalOrderId: { type: [string, "null"] }
        operatorKind: { type: string, enum: [system, admin] }
        createdAt: { type: string, format: date-time }
    FundEventsResult:
      type: object
      additionalProperties: false
      required: [events]
      properties:
        events:
          type: array
          items: { $ref: "#/components/schemas/FundEvent" }
    FundEventsResponse:
      type: object
      additionalProperties: false
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/FundEventsResult" }
        meta: { $ref: "#/components/schemas/PartnerResponseMeta" }
    ReserveGenerationQuotaRequest:
      type: object
      description: 一个真实调用在模型启动前提交一次；多演员必须聚合在同一请求中，不能按演员拆分许可。
      additionalProperties: false
      required: [projectId, externalGenerationId, actorCodes, requestedSeconds, idempotencyKey]
      properties:
        projectId: { type: string, format: uuid }
        externalGenerationId: { type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$' }
        actorCodes:
          type: array
          minItems: 1
          maxItems: 20
          uniqueItems: true
          items: { type: string, pattern: '^ACT-[A-Z0-9]{6,20}$' }
        requestedSeconds: { type: integer, minimum: 1, maximum: 9000 }
        idempotencyKey: { type: string, minLength: 16, maxLength: 200, pattern: '^[^\x00-\x1F\x7F]+$' }
    GenerationMutationRequest:
      type: object
      additionalProperties: false
      required: [projectId, idempotencyKey]
      properties:
        projectId: { type: string, format: uuid }
        idempotencyKey: { type: string, minLength: 16, maxLength: 200, pattern: '^[^\x00-\x1F\x7F]+$' }
    GenerationSuccessRequest:
      type: object
      additionalProperties: false
      required: [projectId, idempotencyKey, durationSeconds, playableVideoId]
      properties:
        projectId: { type: string, format: uuid }
        idempotencyKey: { type: string, minLength: 16, maxLength: 200, pattern: '^[^\x00-\x1F\x7F]+$' }
        durationSeconds: { type: integer, minimum: 1, maximum: 9000 }
        playableVideoId: { type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$' }
    IssueMaterialAccessRequest:
      type: object
      additionalProperties: false
      required:
        - projectId
        - actorId
        - capabilitySlug
        - materialId
        - idempotencyKey
      properties:
        projectId:
          type: string
          format: uuid
        actorId:
          type: string
          format: uuid
        capabilitySlug:
          type: string
          pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
          maxLength: 100
        materialId:
          type: string
          format: uuid
        idempotencyKey:
          type: string
          minLength: 16
          maxLength: 200
          pattern: ^[^\x00-\x1F\x7F]+$
    MaterialAccessIssued:
      type: object
      additionalProperties: false
      required:
        - accessRequestId
        - expiresAt
        - token
        - contentPath
      properties:
        accessRequestId:
          type: string
          format: uuid
        expiresAt:
          type: string
          format: date-time
        token:
          type: string
          pattern: ^[A-Za-z0-9_-]{43}$
        contentPath:
          type: string
          pattern: ^/api/v1/partner/material-access/[0-9a-f-]{36}/content$
    MaterialAccessIssuedResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          $ref: "#/components/schemas/MaterialAccessIssued"
        meta:
          $ref: "#/components/schemas/PartnerResponseMeta"
    PartnerCallbackSigningKey:
      type: object
      additionalProperties: false
      required:
        - algorithm
        - keyId
        - publicKeySpkiPem
        - environment
        - status
      properties:
        algorithm:
          const: Ed25519
        keyId:
          type: string
        publicKeySpkiPem:
          type: string
          pattern: ^-----BEGIN PUBLIC KEY-----
        environment:
          const: test
        status:
          enum:
            - current
            - previous
    PartnerCallbackSigningKeysResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PartnerCallbackSigningKey"
        meta:
          $ref: "#/components/schemas/PartnerResponseMeta"
    HealthResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - service
            - version
            - environment
          properties:
            service:
              const: judong-ai
            version:
              const: v1
            environment:
              enum:
                - development
                - test
                - preview
                - production
        meta:
          $ref: "#/components/schemas/ResponseMeta"
    AuthorizationPlanOptionsResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          type: object
          additionalProperties: false
          required:
            - pricingModel
            - platforms
            - scenarios
            - projects
            - actors
          properties:
            pricingModel:
              const: per_second_v2
            platforms:
              type: array
              items:
                type: object
                additionalProperties: false
                required:
                  - reference
                  - name
                  - environment
                  - productionApproved
                properties:
                  reference:
                    type: string
                  name:
                    type: string
                  environment:
                    const: test
                  productionApproved:
                    const: false
            scenarios:
              type: array
              items:
                type: object
                additionalProperties: false
                required:
                  - id
                  - name
                properties:
                  id:
                    type: string
                    format: uuid
                  name:
                    type: string
            projects:
              type: array
              items:
                type: object
                additionalProperties: false
                required:
                  - id
                  - status
                  - currentVersion
                  - authorizedActorCodes
                properties:
                  id:
                    type: string
                    format: uuid
                  status:
                    enum:
                      - draft
                      - active
                  currentVersion:
                    type: object
                    additionalProperties: false
                    required:
                      - id
                      - name
                      - workType
                      - synopsis
                      - releaseChannels
                      - scenarioId
                      - partnerReference
                    properties:
                      id:
                        type: string
                        format: uuid
                      name:
                        type: string
                      workType:
                        type: string
                      synopsis:
                        type: string
                      releaseChannels:
                        type: array
                        items:
                          type: string
                      scenarioId:
                        type: string
                        format: uuid
                      partnerReference:
                        type: string
                  authorizedActorCodes:
                    type: array
                    items: &a1
                      type: string
                      pattern: ^ACT-[A-Z0-9]{6,20}$
            actors:
              type: array
              items:
                $ref: "#/components/schemas/AuthorizationPlanActor"
        meta:
          $ref: "#/components/schemas/ResponseMeta"
    CreateOrderDraftRequest:
      type: object
      additionalProperties: false
      required:
        - projectId
        - changeKind
        - name
        - workType
        - synopsis
        - releaseChannels
        - scenarioId
        - partnerReference
        - actorConfigurations
        - idempotencyKey
        - authorizationSessionId
      properties:
        projectId:
          type:
            - string
            - "null"
          format: uuid
        changeKind:
          enum:
            - direct
            - amendment
        name:
          type: string
          minLength: 2
          maxLength: 120
        workType:
          type: string
          minLength: 2
          maxLength: 80
        synopsis:
          type: string
          minLength: 10
          maxLength: 2000
        releaseChannels:
          type: array
          minItems: 1
          maxItems: 10
          items:
            type: string
        scenarioId:
          type: string
          format: uuid
        partnerReference:
          type: string
          pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
        actorConfigurations:
          type: array
          minItems: 1
          maxItems: 20
          items:
            type: object
            additionalProperties: false
            required:
              - actorCode
              - licenseYears
              - generationSeconds
            properties:
              actorCode:
                type: string
                pattern: ^ACT-[A-Z0-9]{6,20}$
              licenseYears:
                enum:
                  - 1
                  - 2
                  - 3
              generationSeconds:
                type: integer
                minimum: 60
                maximum: 9000
                multipleOf: 60
        idempotencyKey:
          type: string
          minLength: 16
          maxLength: 200
        authorizationSessionId:
          type: string
          format: uuid
    OrderDraftResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          type: object
          additionalProperties: false
          required:
            - pricingModel
            - id
            - status
            - project
            - orderIds
            - orderCount
            - totalCents
            - items
          properties:
            pricingModel:
              const: per_second_v2
            id:
              type: string
              format: uuid
            status:
              const: draft
            project:
              type: object
              additionalProperties: false
              required:
                - id
                - versionId
                - versionNumber
                - versionKind
                - name
                - workType
                - synopsis
                - releaseChannels
                - scenarioId
                - partnerReference
                - contentHash
              properties:
                id:
                  type: string
                  format: uuid
                versionId:
                  type: string
                  format: uuid
                versionNumber:
                  type: integer
                  minimum: 1
                versionKind:
                  enum:
                    - initial
                    - draft_revision
                    - amendment
                name:
                  type: string
                workType:
                  type: string
                synopsis:
                  type: string
                releaseChannels:
                  type: array
                  items:
                    type: string
                scenarioId:
                  type: string
                  format: uuid
                partnerReference:
                  type: string
                contentHash:
                  type: string
                  pattern: ^[a-f0-9]{64}$
            orderIds:
              type: array
              minItems: 1
              uniqueItems: true
              items:
                type: string
                format: uuid
            orderCount:
              type: integer
              minimum: 1
            totalCents: &a2
              type: integer
              minimum: 1
            items:
              type: array
              minItems: 1
              items:
                type: object
                additionalProperties: false
                required:
                  - actorCode
                  - publicName
                  - pricePerSecondCents
                  - licenseYears
                  - generationSeconds
                  - totalCents
                properties:
                  actorCode: *a1
                  publicName:
                    type: string
                  pricePerSecondCents:
                    type: integer
                    minimum: 1
                    maximum: 16679998619
                  licenseYears:
                    enum:
                      - 1
                      - 2
                      - 3
                  generationSeconds:
                    type: integer
                    minimum: 60
                    maximum: 9000
                    multipleOf: 60
                  totalCents: *a2
        meta:
          $ref: "#/components/schemas/ResponseMeta"
    PublicActorTag:
      type: object
      additionalProperties: false
      required:
        - slug
        - name
      properties:
        slug:
          type: string
        name:
          type: string
    PublicActorWork:
      type: object
      additionalProperties: false
      required:
        - title
        - roleName
        - releaseYear
      properties:
        title:
          type: string
        roleName:
          type: string
        releaseYear:
          type:
            - integer
            - "null"
    PublicMediaPoster:
      type: object
      additionalProperties: false
      required:
        - href
        - alt
      properties:
        href:
          type: string
          format: uri
          description: 可直接读取的绝对公开Storage URL。
        alt:
          type: string
    PublicActorMedia:
      type: object
      additionalProperties: false
      required:
        - publicId
        - extension
        - kind
        - purpose
        - title
        - description
        - alt
        - durationSeconds
        - width
        - height
        - href
        - poster
        - captionHref
        - appearanceCode
      properties:
        publicId:
          type: string
          format: uuid
        extension:
          enum:
            - webp
            - jpg
            - png
            - mp4
            - mp3
            - wav
        kind:
          enum:
            - image
            - video
            - audio
        purpose:
          enum:
            - cover
            - gallery
            - intro
        title:
          type: string
          description: 音频固定为“演员音频”；其他媒体为规范化公开标题，不返回源文件名。
        description:
          type:
            - string
            - "null"
          description: 音频固定为null；其他媒体仅返回已确认公开的说明。
        alt:
          type: string
          description: 音频固定为“演员音频”；其他媒体返回已确认公开的替代文本。
        durationSeconds:
          type:
            - integer
            - "null"
          minimum: 1
        width:
          type:
            - integer
            - "null"
          minimum: 1
        height:
          type:
            - integer
            - "null"
          minimum: 1
        href:
          type: string
          format: uri
          description: 可直接读取的绝对公开Storage URL。
        poster:
          oneOf:
            - type: "null"
            - $ref: "#/components/schemas/PublicMediaPoster"
        captionHref:
          oneOf:
            - type: "null"
            - type: string
              pattern: ^/api/v1/media/[0-9a-f-]+/captions$
        appearanceCode:
          type:
            - string
            - "null"
          pattern: ^APR-[A-F0-9]{10}$
    PublicActorAppearance:
      type: object
      additionalProperties: false
      required:
        - appearanceCode
        - displayName
        - sortOrder
        - roleCategories
        - suitableRoles
      properties:
        appearanceCode:
          type: string
          pattern: ^APR-[A-F0-9]{10}$
        displayName:
          type: string
        sortOrder:
          type: integer
          minimum: 0
        roleCategories:
          type: array
          maxItems: 1
          items:
            type: string
            enum: [republican-retro, urban-contemporary, realistic-costume, mystery-crime, fantasy-xianxia, sci-fi-future, european-court]
        suitableRoles:
          type: array
          items:
            type: string
    PublicActorSummary:
      type: object
      additionalProperties: false
      required:
        - actorCode
        - publicName
        - pricePerSecondCents
        - minimumGenerationSeconds
        - appearanceCount
        - topicCount
        - suitableRoleCount
        - gender
        - playableAgeGroups
        - roleCategories
        - cover
      properties:
        actorCode: *a1
        publicName:
          type: string
        pricePerSecondCents:
          type: integer
          minimum: 1
          maximum: 16679998619
        minimumGenerationSeconds:
          type: integer
          const: 1500
          description: 每演员最低购买秒数（REQ-004）；只约束巨东合作结算秒数，不约束合作方零售价，购买端强制在Phase 32B落地。
        appearanceCount:
          type: integer
          minimum: 1
          maximum: 20
        topicCount:
          type: integer
          minimum: 0
          maximum: 5
        suitableRoleCount:
          type: integer
          minimum: 0
        gender:
          type:
            - string
            - "null"
          enum: [male, female, null]
        playableAgeGroups:
          type: array
          items:
            type: string
            enum: [child, teen, young, middle, senior]
        roleCategories:
          type: array
          items:
            type: string
            enum: [republican-retro, urban-contemporary, realistic-costume, mystery-crime, fantasy-xianxia, sci-fi-future, european-court]
        cover:
          oneOf:
            - type: "null"
            - type: object
              additionalProperties: false
              required:
                - href
                - alt
              properties:
                href:
                  type: string
                  format: uri
                  description: 可直接读取的绝对公开Storage URL。
                alt:
                  type: string
    PublicActorSearchResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PublicActorSummary"
        meta:
          allOf:
            - $ref: "#/components/schemas/ResponseMeta"
            - type: object
              required:
                - total
                - limit
                - offset
              properties:
                total:
                  type: integer
                  minimum: 0
                limit:
                  type: integer
                offset:
                  type: integer
    PublicActorDocument:
      type: object
      additionalProperties: false
      required:
        - href
        - previewHref
        - title
        - pageCount
        - byteSize
        - previewWidth
        - previewHeight
      properties:
        href:
          type: string
          format: uri
          description: 可直接读取的绝对公开PDF URL。
        previewHref:
          type: string
          format: uri
          description: 可直接读取的绝对公开第一页WebP URL。
        title:
          type: string
        pageCount:
          type: integer
          minimum: 1
          maximum: 100
        byteSize:
          type: integer
          minimum: 5
          maximum: 104857600
        previewWidth:
          type: integer
          minimum: 1
          maximum: 2400
        previewHeight:
          type: integer
          minimum: 1
          maximum: 2400
    PublicActorDetailResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          additionalProperties: false
          required:
            - actorCode
            - publicName
            - overview
            - pricePerSecondCents
            - minimumGenerationSeconds
            - gender
            - playableAgeGroups
            - roleCategories
            - age
            - height
            - weight
            - appearances
            - selectedAppearanceCode
            - works
            - media
            - document
          properties:
            actorCode: *a1
            publicName:
              type: string
            overview:
              type: string
            pricePerSecondCents:
              type: integer
              minimum: 1
              maximum: 16679998619
            minimumGenerationSeconds:
              type: integer
              const: 1500
              description: 每演员最低购买秒数（REQ-004）；只约束巨东合作结算秒数，不约束合作方零售价，购买端强制在Phase 32B落地。
            gender:
              type:
                - string
                - "null"
              enum: [male, female, null]
            playableAgeGroups:
              type: array
              items:
                type: string
                enum: [child, teen, young, middle, senior]
            roleCategories:
              type: array
              maxItems: 5
              items:
                type: string
                enum: [republican-retro, urban-contemporary, realistic-costume, mystery-crime, fantasy-xianxia, sci-fi-future, european-court]
            age:
              type:
                - string
                - "null"
            height:
              type:
                - string
                - "null"
            weight:
              type:
                - string
                - "null"
            appearances:
              type: array
              items:
                $ref: "#/components/schemas/PublicActorAppearance"
            selectedAppearanceCode:
              type:
                - string
                - "null"
              pattern: ^APR-[A-F0-9]{10}$
            works:
              type: array
              items:
                $ref: "#/components/schemas/PublicActorWork"
            media:
              type: array
              items:
                $ref: "#/components/schemas/PublicActorMedia"
            document:
              oneOf:
                - type: "null"
                - $ref: "#/components/schemas/PublicActorDocument"
        meta:
          $ref: "#/components/schemas/ResponseMeta"
    ErrorResponse:
      type: object
      required:
        - error
        - meta
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
            message:
              type: string
        meta:
          $ref: "#/components/schemas/ResponseMeta"
    AuthorizationPlanActor:
      type: object
      additionalProperties: false
      required:
        - actorCode
        - publicName
        - pricePerSecondCents
      properties:
        actorCode: *a1
        publicName:
          type: string
          minLength: 1
          maxLength: 80
        pricePerSecondCents:
          type: integer
          minimum: 1
          maximum: 16679998619
