{
  "openapi": "3.0.3",
  "info": {
    "title": "数美汇云官网内容管理与发文 API",
    "version": "2026-10-20",
    "description": "后台使用管理员 JWT，自动发文使用文章管理中生成的 smhy_pub_ 独立令牌，不需登录或验证码。令牌仅允许查询、图文草稿维护、媒体上传和数美动态发布；不可管理栏目/用户/令牌，不可编辑 PAGE、下架或删除。令牌生成/撤销及精确作用域见 https://ynsmhy.com/docs/content-publishing/。密码登录仍需 ALTCHA。"
  },
  "servers": [
    {
      "url": "https://ynsmhy.com/smhy-api",
      "description": "生产（已上线）"
    }
  ],
  "paths": {
    "/cms/columns": {
      "get": {
        "summary": "查询栏目",
        "description": "需要权限：cms:column:query 或 cms:article:query/add/edit。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "创建栏目",
        "description": "需要权限：cms:column:add。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Column"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/cms/columns/{id}": {
      "put": {
        "summary": "保存栏目（编码、类型、路径固定）",
        "description": "需要权限：cms:column:edit。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Column"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "删除空栏目",
        "description": "需要权限：cms:column:remove。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "version",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/cms/articles": {
      "get": {
        "summary": "文章分页",
        "description": "需要权限：cms:article:query。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "columnId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "DRAFT",
                "PUBLISHED",
                "OFFLINE"
              ]
            }
          },
          {
            "name": "keyword",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "pageNum",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 15,
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "创建草稿（externalId 重试不覆盖）",
        "description": "需要权限：cms:article:add。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArticleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/cms/articles/by-external-id": {
      "get": {
        "summary": "按外部编号查询草稿",
        "description": "需要权限：cms:article:query。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "externalId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 80
            }
          }
        ],
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/cms/articles/{id}": {
      "get": {
        "summary": "读取完整草稿",
        "description": "需要权限：cms:article:query。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "保存草稿（官网已发布快照保持不变）",
        "description": "需要权限：cms:article:edit。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArticleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "删除未发布或已下架内容",
        "description": "需要权限：cms:article:remove。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "version",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/cms/articles/{id}/preview": {
      "get": {
        "summary": "返回 data.html 预览已保存草稿",
        "description": "需要权限：cms:article:query。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/cms/articles/{id}/publish": {
      "post": {
        "summary": "发布已保存草稿",
        "description": "需要权限：cms:article:publish。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Version"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/cms/articles/{id}/offline": {
      "post": {
        "summary": "下架文章",
        "description": "需要权限：cms:article:publish。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Version"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/cms/media": {
      "post": {
        "summary": "上传 JPG/PNG/GIF（10MB、2500 万像素以内）",
        "description": "需要权限：cms:article:add 或 cms:article:edit。",
        "tags": [
          "后台发文"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/content/columns": {
      "get": {
        "summary": "公开栏目",
        "description": "仅返回已发布且所属栏目及上级栏目启用的内容。",
        "tags": [
          "官网只读"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/content/articles": {
      "get": {
        "summary": "已发布文章列表",
        "description": "仅返回已发布且所属栏目及上级栏目启用的内容。",
        "tags": [
          "官网只读"
        ],
        "security": [],
        "parameters": [
          {
            "name": "columnId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "pageNum",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/content/page": {
      "get": {
        "summary": "已发布正文",
        "description": "仅返回已发布且所属栏目及上级栏目启用的内容。",
        "tags": [
          "官网只读"
        ],
        "security": [],
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "检查 HTTP 状态及 JSON code；业务错误可能仍使用 HTTP 200。成功 code=200，401/403/404/409/429 分别表示认证、权限、不存在、冲突、限流。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/content/render": {
      "get": {
        "summary": "完整服务端 HTML",
        "description": "仅返回已发布且所属栏目及上级栏目启用的内容。",
        "tags": [
          "官网只读"
        ],
        "security": [],
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "官网完整 HTML，no-store。",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "页面未发布或已下架。"
          }
        }
      }
    },
    "/content/sitemap.xml": {
      "get": {
        "summary": "动态 sitemap",
        "description": "仅返回已发布且所属栏目及上级栏目启用的内容。",
        "tags": [
          "官网只读"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "已发布页面与启用列表栏目。",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/cms/publishing-tokens": {
      "get": {
        "summary": "查询自己的发文令牌（仅后台 JWT）",
        "description": "不返回原文或 hash，发文令牌不能调用。",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "code=200 表示成功；Token 原文只在生成时返回一次。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "生成独立发文令牌（仅后台 JWT）",
        "description": "需要四项文章权限；不需要验证码。返回 data.token 和 data.record。",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "days"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 60
                  },
                  "days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365,
                    "example": 90
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "code=200 表示成功；Token 原文只在生成时返回一次。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/cms/publishing-tokens/{id}": {
      "delete": {
        "summary": "撤销自己的发文令牌（仅后台 JWT）",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "code=200 表示成功；Token 原文只在生成时返回一次。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT or smhy_pub_ publishing token"
      }
    },
    "schemas": {
      "ArticleInput": {
        "type": "object",
        "required": [
          "columnId",
          "title",
          "slug",
          "contentHtml"
        ],
        "properties": {
          "columnId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "title": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1
          },
          "slug": {
            "type": "string",
            "maxLength": 120,
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
          },
          "externalId": {
            "type": "string",
            "maxLength": 80,
            "pattern": "^[a-zA-Z0-9._:/-]*$"
          },
          "summary": {
            "type": "string",
            "maxLength": 1000
          },
          "contentHtml": {
            "type": "string",
            "maxLength": 500000,
            "minLength": 1
          },
          "coverUrl": {
            "type": "string",
            "maxLength": 500
          },
          "author": {
            "type": "string",
            "maxLength": 80,
            "default": "数美汇云"
          },
          "tag": {
            "type": "string",
            "maxLength": 100
          },
          "seoTitle": {
            "type": "string",
            "maxLength": 200
          },
          "keywords": {
            "type": "string",
            "maxLength": 300
          },
          "displayDate": {
            "type": "string",
            "format": "date"
          },
          "sortOrder": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9999,
            "default": 0
          },
          "version": {
            "type": "integer",
            "minimum": 0,
            "default": 0
          }
        }
      },
      "Column": {
        "type": "object",
        "required": [
          "code",
          "name",
          "path",
          "type"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "readOnly": true
          },
          "code": {
            "type": "string",
            "maxLength": 40,
            "pattern": "^[a-z][a-z0-9-]*$"
          },
          "name": {
            "type": "string",
            "maxLength": 60
          },
          "parentId": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "default": 0
          },
          "path": {
            "type": "string",
            "maxLength": 180,
            "pattern": "^/(?:[a-z0-9-]+/)*$"
          },
          "navPath": {
            "type": "string",
            "maxLength": 200
          },
          "type": {
            "type": "string",
            "enum": [
              "LIST",
              "PAGE",
              "LINK"
            ]
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "sortOrder": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9999
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "navVisible": {
            "type": "boolean",
            "default": true
          },
          "version": {
            "type": "integer",
            "minimum": 0,
            "default": 0
          }
        }
      },
      "Version": {
        "type": "object",
        "required": [
          "version"
        ],
        "properties": {
          "version": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "Envelope": {
        "type": "object",
        "required": [
          "code",
          "msg"
        ],
        "properties": {
          "code": {
            "type": "integer",
            "example": 200
          },
          "msg": {
            "type": "string"
          },
          "data": {
            "description": "成功时的业务对象；详情及示例参见配套文档。"
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "total": {
            "type": "integer"
          }
        }
      }
    }
  }
}
