openapi: 3.0.0
info:
  title: Flower Printing API
  version: 2.0.0
servers:
  - url: /api/v1
    description: Default API prefix
tags:
  - name: PrintingOrder
    description: 印染订单接口
  - name: PrintingJob
    description: 印染款式/任务接口
  - name: PlateOrder
    description: 开版订单接口
paths:
  /printing-orders/:
    get:
      tags: [PrintingOrder]
      summary: 列出印染订单
      parameters:
        - in: query
          name: customer
          schema: {type: integer}
          description: 客户 ID。
        - in: query
          name: customer_name
          schema: {type: string}
          description: 客户名称（模糊匹配）。
        - in: query
          name: customer_phone
          schema: {type: string}
          description: 客户电话（模糊匹配）。
        - in: query
          name: fabric
          schema: {type: string}
          description: 面料（模糊匹配）。
        - in: query
          name: area
          schema: {type: string}
          description: 地区（模糊匹配）。
        - in: query
          name: is_urgent
          schema: {type: boolean}
          description: 是否紧急。
        - in: query
          name: is_fabric_received
          schema: {type: boolean}
          description: 布料是否已收。
        - in: query
          name: is_invalid
          schema: {type: boolean}
          description: 是否作废。
        - in: query
          name: outgoing_date_from
          schema: {type: string, format: date}
          description: 出货日期起始。
        - in: query
          name: outgoing_date_to
          schema: {type: string, format: date}
          description: 出货日期结束。
        - in: query
          name: created_date_from
          schema: {type: string, format: date}
          description: 创建日期起始。
        - in: query
          name: created_date_to
          schema: {type: string, format: date}
          description: 创建日期结束。
        - in: query
          name: search
          schema: {type: string}
          description: 全文搜索（客户名称/面料/地区/工艺）。
        - in: query
          name: ordering
          schema: {type: string}
          description: 排序字段，前缀 - 代表倒序。
        - in: query
          name: limit
          schema: {type: integer, minimum: 1}
          description: 分页大小。
        - in: query
          name: offset
          schema: {type: integer, minimum: 0}
          description: 分页偏移量。
      responses:
        '200':
          description: 成功返回分页结果。
      security:
        - BearerAuth: []
    post:
      tags: [PrintingOrder]
      summary: 创建印染订单
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PrintingOrderCreate'
      responses:
        '201': {description: 创建成功}
        '400': {description: 参数错误或流程不可用}
      security:
        - BearerAuth: []

  /printing-orders/{id}/:
    parameters:
      - in: path
        name: id
        required: true
        schema: {type: integer}
        description: 印染订单 ID。
    get:
      tags: [PrintingOrder]
      summary: 获取印染订单详情
      responses:
        '200': {description: 详情数据}
        '404': {description: 未找到}
      security:
        - BearerAuth: []
    put:
      tags: [PrintingOrder]
      summary: 全量更新印染订单
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PrintingOrderCreate'
      responses:
        '200': {description: 更新成功}
        '400': {description: 参数错误或流程不可更改}
      security:
        - BearerAuth: []
    patch:
      tags: [PrintingOrder]
      summary: 局部更新印染订单
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PrintingOrderCreate'
      responses:
        '200': {description: 更新成功}
        '400': {description: 参数错误或流程不可更改}
      security:
        - BearerAuth: []
    delete:
      tags: [PrintingOrder]
      summary: 禁用删除
      responses:
        '405': {description: 删除被禁止，请使用作废接口}

  /printing-orders/{id}/invalidate/:
    post:
      tags: [PrintingOrder]
      summary: 作废印染订单
      responses:
        '200': {description: 作废成功}
        '400': {description: 已作废}
        '403': {description: 无权限}
      security:
        - BearerAuth: []

  /printing-orders/{id}/activate/:
    post:
      tags: [PrintingOrder]
      summary: 恢复印染订单
      responses:
        '200': {description: 恢复成功}
        '400': {description: 订单未作废}
        '403': {description: 无权限}
      security:
        - BearerAuth: []

  /printing-orders/{id}/mark_fabric_received/:
    post:
      tags: [PrintingOrder]
      summary: 标记布料已收
      responses:
        '200': {description: 状态更新成功}
        '400': {description: 已标记}
      security:
        - BearerAuth: []

  /printing-jobs/:
    get:
      tags: [PrintingJob]
      summary: 列出印染款式明细
      parameters:
        - in: query
          name: printing_order
          schema: {type: integer}
          description: 所属印染订单 ID。
        - in: query
          name: product
          schema: {type: integer}
          description: 产品 ID。
        - in: query
          name: product_name
          schema: {type: string}
          description: 产品名称（模糊匹配）。
        - in: query
          name: unit
          schema: {type: string}
          description: 单位（模糊匹配）。
        - in: query
          name: quantity_min
          schema: {type: number}
          description: 数量下限。
        - in: query
          name: quantity_max
          schema: {type: number}
          description: 数量上限。
        - in: query
          name: pieces_min
          schema: {type: integer}
          description: 件数下限。
        - in: query
          name: pieces_max
          schema: {type: integer}
          description: 件数上限。
        - in: query
          name: search
          schema: {type: string}
          description: 全文搜索（产品/单位/尺寸/备注）。
        - in: query
          name: ordering
          schema: {type: string}
          description: 排序字段。
        - in: query
          name: limit
          schema: {type: integer, minimum: 1}
          description: 分页大小。
        - in: query
          name: offset
          schema: {type: integer, minimum: 0}
          description: 分页偏移。
      responses:
        '200': {description: 成功返回分页结果}
      security:
        - BearerAuth: []
    post:
      tags: [PrintingJob]
      summary: 创建印染款式明细
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PrintingJobCreate'
      responses:
        '201': {description: 创建成功}
        '400': {description: 参数错误}
      security:
        - BearerAuth: []

  /printing-jobs/{id}/:
    parameters:
      - in: path
        name: id
        required: true
        schema: {type: integer}
        description: 印染任务 ID。
    get:
      tags: [PrintingJob]
      summary: 获取印染款式详情
      responses:
        '200': {description: 详情数据}
        '404': {description: 未找到}
      security:
        - BearerAuth: []
    put:
      tags: [PrintingJob]
      summary: 全量更新印染款式
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PrintingJobCreate'
      responses:
        '200': {description: 更新成功}
      security:
        - BearerAuth: []
    patch:
      tags: [PrintingJob]
      summary: 局部更新印染款式
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PrintingJobCreate'
      responses:
        '200': {description: 更新成功}
      security:
        - BearerAuth: []
    delete:
      tags: [PrintingJob]
      summary: 禁用删除
      responses:
        '405': {description: 删除被禁止}

  /printing-jobs/{id}/advance-to-next-state/:
    post:
      tags: [PrintingJob]
      summary: 推进到下一个流程状态
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StateAdvanceRequest'
      responses:
        '200': {description: 推进成功}
        '400': {description: 流程已完成或缺少必填参数}
      security:
        - BearerAuth: []

  /printing-jobs/{id}/step-back-one-state/:
    post:
      tags: [PrintingJob]
      summary: 回退一个流程状态
      responses:
        '200': {description: 回退成功}
        '400': {description: 无可回退状态或未绑定流程}
      security:
        - BearerAuth: []

  /printing-jobs/{id}/completed-states/:
    get:
      tags: [PrintingJob]
      summary: 查询已完成的流程节点
      parameters:
        - in: query
          name: include_cancelled
          schema: {type: boolean}
          description: 是否包含已撤销记录，默认 false。
      responses:
        '200': {description: 返回完成节点列表}
      security:
        - BearerAuth: []

  /printing-jobs/{id}/timeline/:
    get:
      tags: [PrintingJob]
      summary: 查看流程时间线
      responses:
        '200': {description: 返回完整时间线（未包含撤销记录）}
      security:
        - BearerAuth: []

  /plate-orders/:
    get:
      tags: [PlateOrder]
      summary: 列出开版订单
      parameters:
        - in: query
          name: customer
          schema: {type: integer}
          description: 客户 ID。
        - in: query
          name: customer_name
          schema: {type: string}
          description: 客户名称（模糊匹配）。
        - in: query
          name: customer_phone
          schema: {type: string}
          description: 客户电话（模糊匹配）。
        - in: query
          name: salesperson
          schema: {type: integer}
          description: 业务员 ID。
        - in: query
          name: merchandiser
          schema: {type: integer}
          description: 跟单员 ID。
        - in: query
          name: plate_type
          schema: {type: string}
          description: 版型（模糊匹配）。
        - in: query
          name: urgency_level
          schema: {type: string}
          description: 紧急程度。
        - in: query
          name: is_invalid
          schema: {type: boolean}
          description: 是否作废。
        - in: query
          name: is_ordered
          schema: {type: boolean}
          description: 是否已下单。
        - in: query
          name: is_mark_frame
          schema: {type: boolean}
          description: 是否套唛架。
        - in: query
          name: fabric
          schema: {type: string}
          description: 面料（模糊匹配）。
        - in: query
          name: style_name
          schema: {type: string}
          description: 款式名称（模糊匹配）。
        - in: query
          name: plate_date_from
          schema: {type: string, format: date}
          description: 开版日期起始。
        - in: query
          name: plate_date_to
          schema: {type: string, format: date}
          description: 开版日期结束。
        - in: query
          name: required_completion_date_from
          schema: {type: string, format: date-time}
          description: 要求完成时间起始（ISO8601）。
        - in: query
          name: required_completion_date_to
          schema: {type: string, format: date-time}
          description: 要求完成时间结束（ISO8601）。
        - in: query
          name: created_date_from
          schema: {type: string, format: date}
          description: 创建日期起始。
        - in: query
          name: created_date_to
          schema: {type: string, format: date}
          description: 创建日期结束。
        - in: query
          name: search
          schema: {type: string}
          description: 全文搜索（设计编号/款式/客户/面料）。
        - in: query
          name: ordering
          schema: {type: string}
          description: 排序字段。
        - in: query
          name: limit
          schema: {type: integer, minimum: 1}
          description: 分页大小。
        - in: query
          name: offset
          schema: {type: integer, minimum: 0}
          description: 分页偏移。
      responses:
        '200': {description: 成功返回分页结果}
      security:
        - BearerAuth: []
    post:
      tags: [PlateOrder]
      summary: 创建开版订单
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlateOrderCreate'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PlateOrderCreate'
      responses:
        '201': {description: 创建成功}
        '400': {description: 参数错误或流程不存在}
      security:
        - BearerAuth: []

  /plate-orders/{id}/:
    parameters:
      - in: path
        name: id
        required: true
        schema: {type: integer}
        description: 开版订单 ID。
    get:
      tags: [PlateOrder]
      summary: 获取开版订单详情
      responses:
        '200': {description: 详情数据}
        '404': {description: 未找到}
      security:
        - BearerAuth: []
    put:
      tags: [PlateOrder]
      summary: 全量更新开版订单
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlateOrderCreate'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PlateOrderCreate'
      responses:
        '200': {description: 更新成功}
      security:
        - BearerAuth: []
    patch:
      tags: [PlateOrder]
      summary: 局部更新开版订单
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlateOrderCreate'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PlateOrderCreate'
      responses:
        '200': {description: 更新成功}
      security:
        - BearerAuth: []
    delete:
      tags: [PlateOrder]
      summary: 禁用删除
      responses:
        '405': {description: 删除被禁止}

  /plate-orders/{id}/invalidate/:
    post:
      tags: [PlateOrder]
      summary: 作废开版订单
      responses:
        '200': {description: 作废成功}
        '400': {description: 已作废}
        '403': {description: 无权限}
      security:
        - BearerAuth: []

  /plate-orders/{id}/activate/:
    post:
      tags: [PlateOrder]
      summary: 恢复开版订单
      responses:
        '200': {description: 恢复成功}
        '400': {description: 未作废}
        '403': {description: 无权限}
      security:
        - BearerAuth: []

  /plate-orders/{id}/advance-to-next-state/:
    post:
      tags: [PlateOrder]
      summary: 推进到下一个流程状态
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StateAdvanceRequest'
      responses:
        '200': {description: 推进成功}
        '400': {description: 流程已完成或缺少必填参数}
      security:
        - BearerAuth: []

  /plate-orders/{id}/step-back-one-state/:
    post:
      tags: [PlateOrder]
      summary: 回退一个流程状态
      responses:
        '200': {description: 回退成功}
        '400': {description: 无可回退状态或未绑定流程}
      security:
        - BearerAuth: []

  /plate-orders/{id}/completed-states/:
    get:
      tags: [PlateOrder]
      summary: 查询已完成的流程节点
      parameters:
        - in: query
          name: include_cancelled
          schema: {type: boolean}
          description: 是否包含已撤销记录，默认 false。
      responses:
        '200': {description: 返回完成节点列表}
      security:
        - BearerAuth: []

  /plate-orders/{id}/timeline/:
    get:
      tags: [PlateOrder]
      summary: 查看流程时间线
      responses:
        '200': {description: 返回完整时间线（未包含撤销记录）}
      security:
        - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  schemas:
    PrintingOrderCreate:
      type: object
      required: [customer, fabric, width]
      properties:
        customer:
          type: integer
          description: 客户 ID。
        fabric:
          type: string
          description: 面料名称。
        width:
          type: string
          description: 幅宽描述。
        is_urgent:
          type: boolean
          description: 是否紧急。
          default: false
        area:
          type: string
          description: 地区。
        address:
          type: string
          description: 详细地址。
        fabric_source:
          type: string
          description: 布料来源。
        is_fabric_received:
          type: boolean
          description: 布料是否已收。
          default: false
        craft:
          type: string
          description: 工艺说明。
        description:
          type: string
          description: 订单描述。
        outgoing_date:
          type: string
          format: date-time
          description: 计划出货日期时间（可仅传 YYYY-MM-DD，将自动视为 00:00:00）。
        curve:
          type: string
          description: 曲线。
        new_curve:
          type: string
          description: 新增曲线。
        position:
          type: string
          description: 印位描述。
        printing_warn:
          type: string
          description: 打印注意事项。
        rolling_warn:
          type: string
          description: 滚筒注意事项。
        production_warn:
          type: string
          description: 生产注意事项。
        is_invalid:
          type: boolean
          description: 是否作废。
          default: false
        process:
          type: integer
          nullable: true
          description: 关联的印染流程 ID，缺省时采用 settings.PRINTING_DEFAULT_PROCESS_ID。
    PrintingJobCreate:
      type: object
      required: [printing_order, product, quantity, unit]
      properties:
        printing_order:
          type: integer
          description: 所属印染订单 ID。
        product:
          type: integer
          description: 产品 ID。
        quantity:
          type: number
          description: 数量，必须大于 0。
        unit:
          type: string
          description: 数量单位。
        size:
          type: string
          nullable: true
          description: 尺寸描述。
        pieces:
          type: integer
          nullable: true
          description: 件数。
        description:
          type: string
          nullable: true
          description: 备注。
    PlateOrderCreate:
      type: object
      required: [customer]
      properties:
        customer:
          type: integer
          description: 客户 ID。
        design_code:
          type: string
          description: 设计编号。
        plate_type:
          type: string
          description: 版型。
        plate_date:
          type: string
          format: date-time
          nullable: true
          description: 开版时间。
        plate_method:
          type: string
          description: 开版方式。
        plate_image:
          type: array
          nullable: true
          description: 已上传开版图片引用列表（先调用 /api/v1/upload/ 获取 file_id）
          items:
            $ref: '#/components/schemas/PlateImageInput'
        image_name:
          type: string
          nullable: true
          description: 开版图片的名称（可独立填写，便于标注原始文件名）。
        plate_notes:
          type: string
          description: 打版注意事项。
        reprint_reason:
          type: string
          description: 复版原因。
        urgency_level:
          type: string
          description: 紧急程度，默认为“正常”。
        is_invalid:
          type: boolean
          description: 是否作废。
          default: false
        area:
          type: string
          description: 区域。
        default_address:
          type: string
          description: 默认地址。
        salesperson:
          type: integer
          nullable: true
          description: 业务员 ID。
        merchandiser:
          type: integer
          nullable: true
          description: 跟单员 ID。
        style_name:
          type: string
          description: 款式名称。
        fabric:
          type: string
          description: 面料。
        width:
          type: string
          description: 幅宽。
        production_method:
          type: string
          description: 做货方式。
        is_mark_frame:
          type: boolean
          description: 是否套唛架。
        drawing_rating:
          type: string
          description: 画图评级。
        color_matching_rating:
          type: string
          description: 调色评级。
        sample_rating:
          type: string
          description: 套样评级。
        difficulty_rating:
          type: string
          description: 难度评级。
        sample_meter:
          type: string
          description: 米样描述。
        required_sample_meters:
          type: number
          nullable: true
          description: 客户要求的米样米数。
        required_completion_date:
          type: string
          format: date-time
          nullable: true
          description: 要求完成时间（含具体时刻）。
        completion_date:
          type: string
          format: date-time
          nullable: true
          description: 实际完成时间。
        approval_result:
          type: string
          description: 审批结果。
        is_ordered:
          type: boolean
          description: 是否已下单。
          default: false
        customer_feedback:
          type: string
          description: 客户反馈。
        process:
          type: integer
          nullable: true
          description: 关联流程 ID，缺省时采用 settings.PLATE_ORDER_DEFAULT_PROCESS_ID。
    PlateImageInput:
      type: object
      required: [file_id]
      properties:
        file_id:
          type: integer
          description: 上传文件的 ID（来自 `/api/v1/upload/`）
        name:
          type: string
          nullable: true
          description: 可选名称，默认使用文件原始名称
    PlateImageItem:
      type: object
      properties:
        file_id:
          type: integer
          nullable: true
          description: 上传文件 ID，若为外部链接可为空
        name:
          type: string
          nullable: true
          description: 图片名称或备注
        url:
          type: string
          nullable: true
          description: 图片可访问 URL
        path:
          type: string
          nullable: true
          description: 图片相对路径
        size:
          type: integer
          nullable: true
          description: 文件大小（字节）
        content_type:
          type: string
          nullable: true
          description: 文件内容类型
        uploaded_at:
          type: string
          format: date-time
          nullable: true
          description: 上传时间
    StateAdvanceRequest:
      type: object
      properties:
        parameters:
          type: object
          additionalProperties:
            type: string
          description: 传递给下一个流程节点的参数键值对，必须满足节点必填要求。
