openapi: 3.0.0
info:
  title: WHLCore API
  version: 1.0.0
  description: WHLCore 框架自动生成的 API 文档
servers:
  - url: http://localhost
    description: 本地开发服务器

security:
  - TokenAuth: []

tags:
  - name: API
    description: 通用 API 操作

components:
  securitySchemes:
    TokenAuth:
      type: apiKey
      in: header
      name: token
      description: API 访问令牌

  schemas:
    StandardResponse:
      type: object
      properties:
        code:
          type: integer
          description: 响应状态码
          example: 200
        ack:
          type: string
          description: 请求确认信息
          example: "admin/openkeys"
        msg:
          type: string
          description: 响应消息
          example: "success"
        data:
          type: object
          description: 响应数据
          additionalProperties: true

    ErrorResponse:
      type: object
      properties:
        code:
          type: integer
          description: 错误状态码
        ack:
          type: string
          description: 请求确认信息
        msg:
          type: string
          description: 错误消息
        data:
          type: object
          description: 错误时数据为空

  responses:
    ResourceNotChange:
      description: 资源未变化
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 304
            ack: "admin/openkeys"
            msg: "资源未变化"
            data:
              type: object
              description: 错误详情

    BadRequest:
      description: 客户端请求错误
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 400
            ack: "admin/openkeys"
            msg: "客户端请求错误"
            data:
              type: object
              description: 错误详情

    Unauthorized:
      description: 未登录
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 401
            ack: "admin/openkeys"
            msg: "未登录"
            data:
              type: object
              description: 错误详情

    Forbidden:
      description: 未授权
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 403
            ack: "admin/openkeys"
            msg: "未授权"
            data:
              type: object
              description: 错误详情

    NotFound:
      description: 请求目标不存在
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 404
            ack: "admin/openkeys"
            msg: "请求目标不存在"
            data:
              type: object
              description: 错误详情

    MethodNotAllowed:
      description: 操作不允许
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 405
            ack: "admin/openkeys"
            msg: "操作不允许"
            data:
              type: object
              description: 错误详情

    NotAcceptable:
      description: 参数不被接受
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 406
            ack: "admin/openkeys"
            msg: "参数不被接受"
            data:
              type: object
              description: 错误详情

    Conflict:
      description: 请求冲突
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 409
            ack: "admin/openkeys"
            msg: "请求冲突"
            data:
              type: object
              description: 错误详情

    ExpectationFailed:
      description: 未符合要求
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 417
            ack: "admin/openkeys"
            msg: "未符合要求"
            data:
              type: object
              description: 错误详情

    RequestTimeout:
      description: 请求超时
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 408
            ack: "admin/openkeys"
            msg: "请求超时"
            data:
              type: object
              description: 错误详情

    PreconditionFailed:
      description: 前置条件失败
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 412
            ack: "admin/openkeys"
            msg: "前置条件失败"
            data:
              type: object
              description: 错误详情

    UnprocessableEntity:
      description: 请求格式正确但语义错误
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 422
            ack: "admin/openkeys"
            msg: "请求格式正确但语义错误"
            data:
              type: object
              description: 错误详情

    DependencyFailed:
      description: 依赖失败
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 424
            ack: "admin/openkeys"
            msg: "依赖失败"
            data:
              type: object
              description: 错误详情

    UpgradeRequired:
      description: 需要升级
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 426
            ack: "admin/openkeys"
            msg: "需要升级"
            data:
              type: object
              description: 错误详情

    PreconditionRequired:
      description: 需要前置条件
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 428
            ack: "admin/openkeys"
            msg: "需要前置条件"
            data:
              type: object
              description: 错误详情

    TooManyRequests:
      description: 请求次数过多
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 429
            ack: "admin/openkeys"
            msg: "请求次数过多"
            data:
              type: object
              description: 错误详情

    UnavailableForLegalReasons:
      description: 因法律原因不可用
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 451
            ack: "admin/openkeys"
            msg: "因法律原因不可用"
            data:
              type: object
              description: 错误详情

    InternalServerError:
      description: 服务器内部错误
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 500
            ack: "admin/openkeys"
            msg: "服务器内部错误"
            data:
              type: object
              description: 错误详情

    NotImplemented:
      description: 功能未实现
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 501
            ack: "admin/openkeys"
            msg: "功能未实现"
            data:
              type: object
              description: 错误详情

    BadGateway:
      description: 网关错误
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 502
            ack: "admin/openkeys"
            msg: "网关错误"
            data:
              type: object
              description: 错误详情

    ServiceUnavailable:
      description: 服务不可用
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 503
            ack: "admin/openkeys"
            msg: "服务不可用"
            data:
              type: object
              description: 错误详情

    GatewayTimeout:
      description: 网关超时
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 504
            ack: "admin/openkeys"
            msg: "网关超时"
            data:
              type: object
              description: 错误详情

paths:
  /open/detail:
    get:
      operationId: BmcGetDetail
      summary: 获取BMC详情
      description: 需权限字母 r。返回完整画布；有 site 时带产品链接，未设置（含旧数据）则无该字段。
      tags:
        - open
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: BMC唯一标识，必填
      responses:
        '200':
          description: data 为画布对象；site 仅在有值时出现
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                    description: 响应状态码
                    example: 200
                  msg:
                    type: string
                    description: 响应消息
                    example: "ok"
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      title:
                        type: string
                      description:
                        type: string
                      arena:
                        type: string
                      site:
                        type: string
                      author:
                        type: string
                      created:
                        type: string
                      uri:
                        type: string
                      bmc:
                        type: object
        '304':
          $ref: '#/components/responses/ResourceNotChange'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '406':
          $ref: '#/components/responses/NotAcceptable'
        '408':
          $ref: '#/components/responses/RequestTimeout'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '417':
          $ref: '#/components/responses/ExpectationFailed'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '424':
          $ref: '#/components/responses/DependencyFailed'
        '426':
          $ref: '#/components/responses/UpgradeRequired'
        '428':
          $ref: '#/components/responses/PreconditionRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '451':
          $ref: '#/components/responses/UnavailableForLegalReasons'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '501':
          $ref: '#/components/responses/NotImplemented'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /open/list:
    get:
      operationId: BmcGetList
      summary: 获取BMC列表
      description: 需权限字母 r。分页列表；每项在有产品链接时含可选 site，否则无该字段。
      tags:
        - open
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
          description: 页码，验证规则：^\d+$
        - name: limit
          in: query
          required: false
          schema:
            type: integer
          description: 每页条数，最大100，验证规则：^\d+$
        - name: sort
          in: query
          required: false
          schema:
            type: string
          description: 排序字段（updated|created|title），验证规则：^[a-zA-Z_]+$
        - name: order
          in: query
          required: false
          schema:
            type: string
          description: 排序方向，验证规则：^(asc|desc)$
      responses:
        '200':
          description: list 为摘要行（可含 site）；pagination 为分页信息
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                    description: 响应状态码
                    example: 200
                  msg:
                    type: string
                    description: 响应消息
                    example: "ok"
                  data:
                    type: string
        '304':
          $ref: '#/components/responses/ResourceNotChange'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '406':
          $ref: '#/components/responses/NotAcceptable'
        '408':
          $ref: '#/components/responses/RequestTimeout'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '417':
          $ref: '#/components/responses/ExpectationFailed'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '424':
          $ref: '#/components/responses/DependencyFailed'
        '426':
          $ref: '#/components/responses/UpgradeRequired'
        '428':
          $ref: '#/components/responses/PreconditionRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '451':
          $ref: '#/components/responses/UnavailableForLegalReasons'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '501':
          $ref: '#/components/responses/NotImplemented'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /open/search:
    get:
      operationId: BmcGetSearch
      summary: 搜索BMC画布
      description: 需权限字母 r。关键词搜索；命中项在有产品链接时含可选 site。
      tags:
        - open
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
          description: 页码，验证规则：^\d+$
        - name: limit
          in: query
          required: false
          schema:
            type: integer
          description: 每页条数，最大100，验证规则：^\d+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                q:
                  type: string
                  description: 搜索关键词，必填（可用逗号分隔多词），验证规则：.+
                fields:
                  type: string
                  description: 搜索字段，逗号分隔，验证规则：^[a-zA-Z_,]+$
              required:
                - q
      responses:
        '200':
          description: list 为命中摘要（可含 site）；highlight 为高亮信息
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                    description: 响应状态码
                    example: 200
                  msg:
                    type: string
                    description: 响应消息
                    example: "ok"
                  data:
                    type: string
        '304':
          $ref: '#/components/responses/ResourceNotChange'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '406':
          $ref: '#/components/responses/NotAcceptable'
        '408':
          $ref: '#/components/responses/RequestTimeout'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '417':
          $ref: '#/components/responses/ExpectationFailed'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '424':
          $ref: '#/components/responses/DependencyFailed'
        '426':
          $ref: '#/components/responses/UpgradeRequired'
        '428':
          $ref: '#/components/responses/PreconditionRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '451':
          $ref: '#/components/responses/UnavailableForLegalReasons'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '501':
          $ref: '#/components/responses/NotImplemented'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /open/delete:
    delete:
      operationId: BmcDelete
      summary: 删除BMC画布
      description: 需权限字母 d。按 id 删除对应 JSON。
      tags:
        - open
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: BMC唯一标识，必填
      responses:
        '200':
          description: 删除成功后返回 id 与 deleted=true
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                    description: 响应状态码
                    example: 200
                  msg:
                    type: string
                    description: 响应消息
                    example: "ok"
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      deleted:
                        type: boolean
        '304':
          $ref: '#/components/responses/ResourceNotChange'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '406':
          $ref: '#/components/responses/NotAcceptable'
        '408':
          $ref: '#/components/responses/RequestTimeout'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '417':
          $ref: '#/components/responses/ExpectationFailed'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '424':
          $ref: '#/components/responses/DependencyFailed'
        '426':
          $ref: '#/components/responses/UpgradeRequired'
        '428':
          $ref: '#/components/responses/PreconditionRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '451':
          $ref: '#/components/responses/UnavailableForLegalReasons'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '501':
          $ref: '#/components/responses/NotImplemented'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /open/update:
    put:
      operationId: BmcPutUpdate
      summary: 更新BMC画布
      description: 需权限字母 u。可选 site：非空则更新产品链接；传空串清除已有链接（之后响应/UI不再出现）。
      tags:
        - open
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: BMC唯一标识，必填
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  description: 标题，可选
                author:
                  type: string
                  description: 作者，可选
                description:
                  type: string
                  description: 描述，可选
                arena:
                  type: string
                  description: 赛道/领域，可选
                site:
                  type: string
                  description: 产品链接，可选；空串表示清除
                bmc:
                  type: object
                  description: 九宫格内容对象，可选（按字段合并）
      responses:
        '200':
          description: 更新成功后返回 id 与 updated=true
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                    description: 响应状态码
                    example: 200
                  msg:
                    type: string
                    description: 响应消息
                    example: "ok"
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      updated:
                        type: boolean
        '304':
          $ref: '#/components/responses/ResourceNotChange'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '406':
          $ref: '#/components/responses/NotAcceptable'
        '408':
          $ref: '#/components/responses/RequestTimeout'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '417':
          $ref: '#/components/responses/ExpectationFailed'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '424':
          $ref: '#/components/responses/DependencyFailed'
        '426':
          $ref: '#/components/responses/UpgradeRequired'
        '428':
          $ref: '#/components/responses/PreconditionRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '451':
          $ref: '#/components/responses/UnavailableForLegalReasons'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '501':
          $ref: '#/components/responses/NotImplemented'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
  /open/create:
    post:
      operationId: BmcPostCreate
      summary: 创建BMC画布
      description: 需权限字母 c。可选 site 为产品官网或对外引用 URL；缺省或空串不写入，旧数据也不返回该字段。
      tags:
        - open
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: BMC唯一标识（slug），必填
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  description: 标题，必填
                description:
                  type: string
                  description: 描述，可选
                arena:
                  type: string
                  description: 赛道/领域，可选
                site:
                  type: string
                  description: 产品链接（官网或引用URL），可选；空则不存储
                bmc:
                  type: object
                  description: 九宫格内容对象，可选
                author:
                  type: string
                  description: 作者，可选，默认 sxo
              required:
                - title
      responses:
        '200':
          description: 创建成功后返回 id 与 created=true
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                    description: 响应状态码
                    example: 200
                  msg:
                    type: string
                    description: 响应消息
                    example: "ok"
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      created:
                        type: boolean
        '304':
          $ref: '#/components/responses/ResourceNotChange'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '406':
          $ref: '#/components/responses/NotAcceptable'
        '408':
          $ref: '#/components/responses/RequestTimeout'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '417':
          $ref: '#/components/responses/ExpectationFailed'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '424':
          $ref: '#/components/responses/DependencyFailed'
        '426':
          $ref: '#/components/responses/UpgradeRequired'
        '428':
          $ref: '#/components/responses/PreconditionRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '451':
          $ref: '#/components/responses/UnavailableForLegalReasons'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '501':
          $ref: '#/components/responses/NotImplemented'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
