{
  "openapi": "3.1.0",
  "info": {
    "title": "Vibe 广场 · 项目投稿接口",
    "version": "1.0.0",
    "description": "提交无需管理员密钥；全部人工审核。仅收录有公开制作依据及国内作者或主体依据的作品。"
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "paths": {
    "/api/v1/submissions": {
      "post": {
        "operationId": "submitProject",
        "summary": "提交项目，等待人工审核",
        "security": [],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,128}$"
            },
            "description": "随机键，同一请求重试沿用原值"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Submission"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "新投稿已进入待审核队列",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Receipt"
                }
              }
            }
          },
          "200": {
            "description": "幂等重试，返回原回执"
          },
          "400": {
            "description": "请求错误，响应包含 error.code / error.message / error.details"
          },
          "409": {
            "description": "请求错误，响应包含 error.code / error.message / error.details"
          },
          "413": {
            "description": "请求错误，响应包含 error.code / error.message / error.details"
          },
          "415": {
            "description": "请求错误，响应包含 error.code / error.message / error.details"
          },
          "422": {
            "description": "请求错误，响应包含 error.code / error.message / error.details"
          },
          "429": {
            "description": "请求错误，响应包含 error.code / error.message / error.details"
          }
        }
      }
    },
    "/api/v1/submissions/{id}": {
      "get": {
        "operationId": "getSubmissionStatus",
        "summary": "凭回执密钥查询状态，仅返回状态及审核说明",
        "security": [
          {
            "receiptToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^sub_[a-f0-9]{32}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "pending / approved / rejected 以及 reviewNote、projectId"
          },
          "404": {
            "description": "请求错误，响应包含 error.code / error.message / error.details"
          }
        }
      }
    },
    "/api/v1/projects": {
      "get": {
        "operationId": "listPublishedProjects",
        "summary": "只返回已收录项目，不含待审核稿或私有联系信息",
        "security": [],
        "responses": {
          "200": {
            "description": "items 数组"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "receiptToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "仅使用POST提交后返回的 receiptToken；不要使用审核密钥"
      }
    },
    "schemas": {
      "Submission": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "description",
          "author",
          "platform",
          "type",
          "tag",
          "url",
          "evidenceUrl",
          "evidenceQuote",
          "chinaReason",
          "chinaSourceUrl",
          "rightsConfirmed"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 60,
            "description": "项目名称"
          },
          "description": {
            "type": "string",
            "minLength": 10,
            "maxLength": 240,
            "description": "用途简介"
          },
          "author": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "platform": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "type": {
            "type": "string",
            "enum": [
              "网站",
              "App",
              "微信小程序"
            ]
          },
          "tag": {
            "type": "string",
            "enum": [
              "生活工具",
              "学习教育",
              "效率办公",
              "设计创作",
              "开发工具",
              "游戏娱乐"
            ]
          },
          "evidenceQuote": {
            "type": "string",
            "minLength": 10,
            "maxLength": 1000,
            "description": "作者公开说明该项目由AI编程制作的原话"
          },
          "chinaReason": {
            "type": "string",
            "minLength": 5,
            "maxLength": 500,
            "description": "公开的中国开发者或国内主体依据"
          },
          "wechatSearchName": {
            "type": "string",
            "maxLength": 60
          },
          "contact": {
            "type": "string",
            "maxLength": 200,
            "description": "可选，仅审核员可见"
          },
          "rightsConfirmed": {
            "type": "boolean",
            "const": true,
            "description": "提交者确认是作者或已获授权，并同意公开项目资料及制作依据"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "公开HTTP(S)链接；不接受内网、带凭据地址或非标准端口"
          },
          "evidenceUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "公开HTTP(S)链接；不接受内网、带凭据地址或非标准端口"
          },
          "chinaSourceUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "公开HTTP(S)链接；不接受内网、带凭据地址或非标准端口"
          },
          "qrCodeUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "公开HTTP(S)链接；不接受内网、带凭据地址或非标准端口"
          },
          "usage": {
            "type": "object",
            "description": "可选；没有可靠的公开人数则省略。审核员单独核对后才展示。下载量、访问量和Star不能充当人数。",
            "additionalProperties": false,
            "required": [
              "count",
              "kind",
              "asOf",
              "sourceUrl",
              "quote"
            ],
            "properties": {
              "count": {
                "type": "integer",
                "minimum": 1,
                "maximum": 1000000000000
              },
              "kind": {
                "type": "string",
                "enum": [
                  "用户（作者披露）",
                  "累计使用人数",
                  "注册用户",
                  "月活跃用户",
                  "日活跃用户"
                ]
              },
              "qualifier": {
                "type": "string",
                "enum": [
                  "",
                  "约",
                  "超过",
                  "至少"
                ]
              },
              "asOf": {
                "type": "string",
                "format": "date"
              },
              "sourceUrl": {
                "type": "string",
                "format": "uri",
                "maxLength": 2048
              },
              "quote": {
                "type": "string",
                "minLength": 5,
                "maxLength": 1000
              }
            }
          },
          "launchUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "可选，作者公开的实际入口；launchUrl须为微信官方HTTPS URL Link，其余为公开HTTP(S)地址。"
          },
          "webEntryUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "可选，作者公开的实际入口；launchUrl须为微信官方HTTPS URL Link，其余为公开HTTP(S)地址。"
          },
          "downloadUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "可选，作者公开的实际入口；launchUrl须为微信官方HTTPS URL Link，其余为公开HTTP(S)地址。"
          },
          "androidUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "可选，作者公开的实际入口；launchUrl须为微信官方HTTPS URL Link，其余为公开HTTP(S)地址。"
          },
          "harmonyUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "可选，作者公开的实际入口；launchUrl须为微信官方HTTPS URL Link，其余为公开HTTP(S)地址。"
          }
        },
        "allOf": [
          {
            "if": {
              "properties": {
                "type": {
                  "const": "微信小程序"
                }
              }
            },
            "then": {
              "anyOf": [
                {
                  "required": [
                    "wechatSearchName"
                  ],
                  "properties": {
                    "wechatSearchName": {
                      "minLength": 1
                    }
                  }
                },
                {
                  "required": [
                    "qrCodeUrl"
                  ],
                  "properties": {
                    "qrCodeUrl": {
                      "minLength": 8
                    }
                  }
                },
                {
                  "required": [
                    "launchUrl"
                  ],
                  "properties": {
                    "launchUrl": {
                      "minLength": 8
                    }
                  }
                }
              ]
            }
          }
        ]
      },
      "Receipt": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "rejected"
            ]
          },
          "createdAt": {
            "type": "integer"
          },
          "statusUrl": {
            "type": "string"
          },
          "receiptToken": {
            "type": "string"
          },
          "replayed": {
            "type": "boolean"
          },
          "message": {
            "type": "string"
          }
        }
      }
    }
  }
}