openapi: 3.0.0
info:
  title: Quyun Platform API
  description: API specification for Quyun (曲韵) Platform - A multi-tenant opera content delivery platform.
  version: 1.0.2
servers:
  - url: /v1
    description: Production Server

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  schemas:
    # --- Common Models ---
    Error:
      type: object
      properties:
        code:
          type: integer
        message:
          type: string

    Pager:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        items:
          type: array
          items: {} # Generic placeholder

    # --- User Models ---
    User:
      type: object
      properties:
        id:
          type: string
        phone:
          type: string
        nickname:
          type: string
        avatar:
          type: string
        gender:
          type: string
          enum: [male, female, secret]
        bio:
          type: string
        birthday:
          type: string
          format: date
        location:
          type: object
          properties:
            province:
              type: string
            city:
              type: string
        balance:
          type: number
          readOnly: true
        points:
          type: integer
          readOnly: true
        is_real_name_verified:
          type: boolean
          readOnly: true

    UserUpdate:
      type: object
      properties:
        nickname:
          type: string
        avatar:
          type: string
        gender:
          type: string
          enum: [male, female, secret]
        bio:
          type: string
        birthday:
          type: string
          format: date
        location:
          type: object
          properties:
            province:
              type: string
            city:
              type: string

    # --- Content Models ---
    ContentItem:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        cover:
          type: string
        genre:
          type: string
        type:
          type: string
          enum: [video, audio, article]
        price:
          type: number
        author_id:
          type: string
        author_name:
          type: string
        author_avatar:
          type: string
        views:
          type: integer
        likes:
          type: integer
        is_purchased:
          type: boolean
          description: User specific state

    ContentDetail:
      allOf:
        - $ref: '#/components/schemas/ContentItem'
        - type: object
          properties:
            description:
              type: string
            body:
              type: string # HTML/Markdown
            media_urls:
              type: array
              items:
                type: object
                properties:
                  type: 
                    type: string
                  url: 
                    type: string
                  duration:
                    type: integer
            meta:
              type: object
              properties:
                role:
                  type: string
                key:
                  type: string
                beat:
                  type: string
            is_liked:
              type: boolean
              description: User specific state
            is_favorited:
              type: boolean
              description: User specific state

    Comment:
      type: object
      properties:
        id:
          type: string
        content:
          type: string
        user_id:
          type: string
        user_nickname:
          type: string
        user_avatar:
          type: string
        create_time:
          type: string
        likes:
          type: integer
        is_liked:
          type: boolean
        reply_to:
          type: string # Comment ID

    # --- Tenant/Creator Public Profile ---
    TenantProfile:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        avatar:
          type: string
        cover:
          type: string
        bio:
          type: string
        description:
          type: string
        cert_type:
          type: string
          enum: [personal, enterprise]
        stats:
          type: object
          properties:
            followers:
              type: integer
            contents:
              type: integer
            likes:
              type: integer
        is_following:
          type: boolean

    # --- Order Models ---
    Order:
      type: object
      properties:
        id:
          type: string
        create_time:
          type: string
          format: date-time
        pay_time:
          type: string
          format: date-time
        status:
          type: string
          enum: [unpaid, paid, completed, refunding, refunded, cancelled]
        amount:
          type: number
        quantity:
          type: integer
        items:
          type: array
          items:
            $ref: '#/components/schemas/ContentItem'
        tenant_id:
          type: string
        tenant_name:
          type: string
        is_virtual:
          type: boolean

    # --- Creator Dashboard Models ---
    CreatorStats:
      type: object
      properties:
        total_followers:
          type: object
          properties:
            value:
              type: integer
            trend:
              type: number
              description: Percentage change (e.g. 1.2 for +1.2%)
        total_revenue:
          type: object
          properties:
            value:
              type: number
            trend:
              type: number
        pending_refunds:
          type: integer
        new_messages:
          type: integer

    PayoutAccount:
      type: object
      properties:
        id:
          type: string
        type:
          type: string
          enum: [bank, alipay]
        name:
          type: string
        account:
          type: string
        realname:
          type: string

    UserCouponItem:
      type: object
      properties:
        id:
          type: string
        coupon_id:
          type: string
        title:
          type: string
        description:
          type: string
        type:
          type: string
          enum: [fix_amount, discount]
        value:
          type: integer
        min_order_amount:
          type: integer
        start_at:
          type: string
        end_at:
          type: string
        status:
          type: string
          enum: [unused, used, expired]

    # --- Upload ---
    UploadResult:
      type: object
      properties:
        id:
          type: string
        url:
          type: string
        filename:
          type: string
        size:
          type: integer
        mime_type:
          type: string

security:
  - BearerAuth: []

paths:
  # ============================
  # Public / Auth
  # ============================
  /auth/otp:
    post:
      summary: Send OTP
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                phone:
                  type: string
      responses:
        '200':
          description: OTP sent

  /auth/login:
    post:
      summary: Login or Register with OTP
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                phone:
                  type: string
                otp:
                  type: string
      responses:
        '200':
          description: Successful login
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                  user:
                    $ref: '#/components/schemas/User'

  # ============================
  # Public Content & Interaction
  # ============================
  /contents:
    get:
      summary: List contents (Explore / Search)
      security: []
      parameters:
        - name: keyword
          in: query
          schema:
            type: string
          description: Search keyword
        - name: genre
          in: query
          schema:
            type: string
        - name: tenant_id
          in: query
          schema:
            type: string
          description: Filter by creator
        - name: sort
          in: query
          schema:
            type: string
            enum: [latest, hot, price_asc]
        - name: page
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: List of contents
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Pager'
                  - type: object
                    properties:
                      items:
                        type: array
                        items:
                          $ref: '#/components/schemas/ContentItem'

  /contents/{id}:
    get:
      summary: Get content detail
      security: [] 
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Content detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContentDetail'

  /contents/{id}/comments:
    get:
      summary: Get comments for a content
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Pager'
                  - type: object
                    properties:
                      items:
                        type: array
                        items:
                          $ref: '#/components/schemas/Comment'
    post:
      summary: Post a comment
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                content:
                  type: string
                reply_to:
                  type: string
      responses:
        '200':
          description: Comment created

  /comments/{id}/like:
    post:
      summary: Like a comment
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Liked

  /topics:
    get:
      summary: List curated topics
      security: []
      responses:
        '200':
          description: List of topics
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                    title:
                      type: string
                    cover:
                      type: string
                    tag:
                      type: string
                    count:
                      type: integer

  # ============================
  # Public Tenant Profile
  # ============================
  /tenants/{id}:
    get:
      summary: Get tenant public profile
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TenantProfile'

  /tenants/{id}/follow:
    post:
      summary: Follow a tenant
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Followed
    delete:
      summary: Unfollow a tenant
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Unfollowed

  # ============================
  # User Center
  # ============================
  /me:
    get:
      summary: Get current user profile
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
    put:
      summary: Update user profile
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserUpdate'
      responses:
        '200':
          description: Updated

  /me/realname:
    post:
      summary: Submit real-name authentication
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                realname:
                  type: string
                id_card:
                  type: string
      responses:
        '200':
          description: Submitted

  /me/wallet:
    get:
      summary: Get wallet balance and transactions
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  balance:
                    type: number
                  transactions:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        title:
                          type: string
                        amount:
                          type: number
                        type:
                          type: string
                          enum: [income, expense]
                        date:
                          type: string

  /me/wallet/recharge:
    post:
      summary: Recharge wallet
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                amount:
                  type: number
                method:
                  type: string
                  enum: [wechat, alipay]
      responses:
        '200':
          description: Recharge initiated
          content:
            application/json:
              schema:
                type: object
                properties:
                  pay_params:
                    type: string # QR code or SDK params
                  order_id:
                    type: string

  /me/orders:
    get:
      summary: List user orders
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [all, unpaid, completed, refund]
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Order'

  /me/orders/{id}:
    get:
      summary: Get user order detail
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'

  /me/library:
    get:
      summary: Get purchased content
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ContentItem'

  /me/favorites:
    get:
      summary: Get favorites
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ContentItem'
    post:
      summary: Add to favorites
      parameters:
        - name: content_id
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Added

  /me/favorites/{content_id}:
    delete:
      summary: Remove from favorites
      parameters:
        - name: content_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Removed

  /me/likes:
    get:
      summary: Get liked contents
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ContentItem'
    post:
      summary: Like content
      parameters:
        - name: content_id
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Liked

  /me/likes/{content_id}:
    delete:
      summary: Unlike content
      parameters:
        - name: content_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Unliked

  /me/notifications:
    get:
      summary: Get notifications
      parameters:
        - name: type
          in: query
          schema:
            type: string
            enum: [all, system, order, audit, interaction]
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                    type:
                      type: string
                    title:
                      type: string
                    content:
                      type: string
                    read:
                      type: boolean
                    time:
                      type: string

  /me/coupons:
    get:
      summary: List user coupons
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [unused, used, expired]
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/UserCouponItem'

  # ============================
  # Storage (Presigned)
  # ============================
  /storage/{key}:
    put:
      summary: Upload file (Presigned)
      tags: [Storage]
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
        - name: expires
          in: query
          required: true
          schema:
            type: string
        - name: sign
          in: query
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
      responses:
        '200':
          description: Upload successful
    get:
      summary: Download file (Presigned)
      tags: [Storage]
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
        - name: expires
          in: query
          required: true
          schema:
            type: string
        - name: sign
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Download file
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary

  # ============================
  # Transaction
  # ============================
  /orders:
    post:
      summary: Create Order
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                content_id:
                  type: string
                sku:
                  type: string
                quantity:
                  type: integer
                  default: 1
                user_coupon_id:
                  type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  order_id:
                    type: string

  /orders/{id}/pay:
    post:
      summary: Pay for order
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                method:
                  type: string
                  enum: [wechat, alipay, balance]
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  pay_params:
                    type: string

  /orders/{id}/status:
    get:
      summary: Check order payment status
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [unpaid, paid, completed]

  /webhook/payment/notify:
    post:
      summary: Payment Webhook
      tags: [Transaction]
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                order_id:
                  type: string
                external_id:
                  type: string
      responses:
        '200':
          description: Success

  # ============================
  # Creator Center
  # ============================
  /creator/apply:
    post:
      summary: Apply to become a creator
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                bio:
                  type: string
                avatar:
                  type: string
      responses:
        '200':
          description: Application submitted

  /creator/dashboard:
    get:
      summary: Get creator dashboard stats
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreatorStats'

  /creator/contents:
    get:
      summary: List creator contents
      parameters:
        - name: status
          in: query
          schema:
            type: string
        - name: genre
          in: query
          schema:
            type: string
        - name: keyword
          in: query
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ContentItem'
    post:
      summary: Create/Publish content
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                genre:
                  type: string
                price:
                  type: number
                media_ids:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Created

  /creator/contents/{id}:
    put:
      summary: Update content
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              # fields to update
      responses:
        '200':
          description: Updated
    delete:
      summary: Delete content
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Deleted

  /creator/orders:
    get:
      summary: List sales orders
      parameters:
        - name: status
          in: query
          schema:
            type: string
        - name: keyword
          in: query
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Order'

  /creator/orders/{id}/refund:
    post:
      summary: Process refund
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum: [accept, reject]
                reason:
                  type: string
      responses:
        '200':
          description: Processed

  /creator/settings:
    get:
      summary: Get channel settings
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                  bio:
                    type: string
                  avatar:
                    type: string
                  cover:
                    type: string
                  description:
                    type: string
    put:
      summary: Update channel settings
      requestBody:
        content:
          application/json:
            schema:
              type: object
              # fields to update
      responses:
        '200':
          description: Updated

  /creator/payout-accounts:
    get:
      summary: List payout accounts
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/PayoutAccount'
    post:
      summary: Add payout account
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PayoutAccount'
      responses:
        '200':
          description: Added
    delete:
      summary: Remove payout account
      parameters:
        - name: id
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Removed

  /creator/withdraw:
    post:
      summary: Request withdrawal
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                amount:
                  type: number
                method:
                  type: string
                  enum: [wallet, external]
                account_id:
                  type: string # Required if method is external
      responses:
        '200':
          description: Withdrawal requested

  # ============================
  # Common / Upload
  # ============================
  /upload:
    post:
      summary: Upload file
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                type:
                  type: string
                  enum: [image, video, audio]
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadResult'