{
  "openapi": "3.1.0",
  "info": {
    "title": "DENGQN Blog API",
    "version": "2.0.0",
    "description": "dengqn.com（DENGQN，全栈工程师 Deng Qingnian 的个人博客）的公开 REST API。\n\n所有响应使用统一信封 `{ code, data, message, success }`：`code` 为 200 表示成功，其他为业务错误码；负载位于 `data`。\n\n公开读取接口无需鉴权；`/admin/*` 命名空间仅面向站长，需通过 `POST /admin/login` 获取 token 并以 `token: Bearer <token>` 请求头携带。\n\n何时使用：检索/引用本站技术文章（GET /articles）、获取经站长审核的中文独立博客友链（GET /links）、读取站内短动态与点赞互动（GET /posts、POST /posts/{id}/like），或提交页面浏览打点（POST /track）。请以合理间隔访问（建议 ≥1 秒），批量合作请先邮件联系 admin@dengqn.com。",
    "contact": {
      "name": "DENGQN (Deng Qingnian)",
      "email": "admin@dengqn.com",
      "url": "https://dengqn.com"
    }
  },
  "servers": [
    {
      "url": "https://dengqn.com/dqn/api/v2",
      "description": "Production"
    },
    {
      "url": "/dqn/api/v2",
      "description": "Same-origin relative"
    }
  ],
  "tags": [
    { "name": "Articles", "description": "博客文章的公开读取" },
    { "name": "Links", "description": "友链（经站长审核的站点交换链接）" },
    { "name": "Posts", "description": "站内短动态（说说）与互动" },
    { "name": "Tracking", "description": "页面浏览打点" },
    { "name": "Admin", "description": "管理端接口（仅站长，需 Bearer token）" }
  ],
  "security": [],
  "paths": {
    "/articles": {
      "get": {
        "tags": ["Articles"],
        "operationId": "listArticles",
        "summary": "获取已发布文章列表",
        "description": "返回已发布（status=published）的文章。列表项的 content 为空字符串，正文请用 getArticleById 获取。不带分页参数时返回全部文章。",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "default": 1 },
            "description": "页码，从 1 开始。"
          },
          {
            "name": "size",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1 },
            "description": "每页数量；缺省时返回全部。"
          }
        ],
        "responses": {
          "200": {
            "description": "文章列表。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ArticleListResponse" }
              }
            }
          },
          "default": {
            "description": "业务错误。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/articles/{id}": {
      "get": {
        "tags": ["Articles"],
        "operationId": "getArticleById",
        "summary": "获取单篇文章",
        "description": "按 ID 返回文章详情。content 字段为后端渲染后的 HTML（无论 contentFormat 是 markdown 还是 html）。",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "description": "文章 ID（24 位十六进制字符串）。"
          }
        ],
        "responses": {
          "200": {
            "description": "文章详情。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ArticleResponse" }
              }
            }
          },
          "default": {
            "description": "业务错误（如文章不存在）。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/links": {
      "get": {
        "tags": ["Links"],
        "operationId": "listLinks",
        "summary": "获取友链列表",
        "description": "返回友链。默认仅返回已审核通过的站点；带 admin token 可按 status 过滤。",
        "parameters": [
          { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "页码，从 1 开始。" },
          { "name": "pageSize", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "每页数量。" },
          { "name": "status", "in": "query", "required": false, "schema": { "type": "integer", "enum": [0, 1] }, "description": "0=待审核，1=已通过。需要管理员权限。" },
          { "name": "search", "in": "query", "required": false, "schema": { "type": "string" }, "description": "按站点名/作者/URL 搜索。" }
        ],
        "responses": {
          "200": {
            "description": "友链列表。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LinkListResponse" }
              }
            }
          },
          "default": {
            "description": "业务错误。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/links/{id}": {
      "get": {
        "tags": ["Links"],
        "operationId": "getLinkById",
        "summary": "获取单个友链",
        "description": "按 ID 返回友链详情。",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "友链 ID。" }
        ],
        "responses": {
          "200": {
            "description": "友链详情。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LinkResponse" }
              }
            }
          },
          "default": {
            "description": "业务错误（如不存在）。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/posts": {
      "get": {
        "tags": ["Posts"],
        "operationId": "listPosts",
        "summary": "获取公开动态列表",
        "description": "返回站内短动态（说说）流，含作者、内容块、图片与互动计数。",
        "parameters": [
          { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "页码，从 1 开始。" },
          { "name": "pageSize", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "每页数量。" },
          { "name": "status", "in": "query", "required": false, "schema": { "type": "integer" }, "description": "按状态过滤。" },
          { "name": "userID", "in": "query", "required": false, "schema": { "type": "string" }, "description": "按作者过滤。" },
          { "name": "search", "in": "query", "required": false, "schema": { "type": "string" }, "description": "全文搜索。" }
        ],
        "responses": {
          "200": {
            "description": "动态列表。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PostListResponse" }
              }
            }
          },
          "default": {
            "description": "业务错误。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/posts/{id}": {
      "get": {
        "tags": ["Posts"],
        "operationId": "getPostById",
        "summary": "获取单条动态",
        "description": "按 ID 返回动态详情。",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "动态 ID。" }
        ],
        "responses": {
          "200": {
            "description": "动态详情。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PostResponse" }
              }
            }
          },
          "default": {
            "description": "业务错误（如不存在）。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/posts/{id}/like": {
      "post": {
        "tags": ["Posts"],
        "operationId": "likePost",
        "summary": "为动态点赞",
        "description": "为指定动态增加一次点赞。无需鉴权；重复调用由服务端幂等处理。",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "动态 ID。" }
        ],
        "responses": {
          "200": {
            "description": "点赞成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OkResponse" }
              }
            }
          },
          "default": {
            "description": "业务错误。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      },
      "delete": {
        "tags": ["Posts"],
        "operationId": "unlikePost",
        "summary": "取消动态点赞",
        "description": "取消对指定动态的点赞。",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "动态 ID。" }
        ],
        "responses": {
          "200": {
            "description": "取消成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OkResponse" }
              }
            }
          },
          "default": {
            "description": "业务错误。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/track": {
      "post": {
        "tags": ["Tracking"],
        "operationId": "trackPageView",
        "summary": "页面浏览打点",
        "description": "记录一次页面浏览。仅接受路径与时间戳，不含个人标识。供本站前端使用，聚合数据可通过管理端统计接口查看。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/TrackRequest" },
              "examples": {
                "pageview": { "value": { "path": "/articles", "ts": 1761234567 } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "已记录。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OkResponse" }
              }
            }
          }
        }
      }
    },
    "/admin/login": {
      "post": {
        "tags": ["Admin"],
        "operationId": "adminLogin",
        "summary": "管理员登录",
        "description": "以用户名密码换取 Bearer token。仅面向站长，不对外发放凭证。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LoginRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "登录成功，返回 token。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LoginResponse" }
              }
            }
          },
          "default": {
            "description": "用户名或密码错误。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" },
                "examples": {
                  "badCredentials": {
                    "value": { "code": 401, "data": null, "message": "用户名或密码错误", "success": false }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/admin/articles": {
      "get": {
        "tags": ["Admin"],
        "operationId": "adminListArticles",
        "summary": "管理端文章列表",
        "description": "含草稿在内的全部文章。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "页码。" },
          { "name": "size", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "每页数量。" }
        ],
        "responses": {
          "200": {
            "description": "文章列表。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ArticleListResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Admin"],
        "operationId": "adminCreateArticle",
        "summary": "创建文章",
        "description": "以 markdown 或 html 正文创建文章。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ArticleUpsertRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "创建成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ArticleResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/admin/articles/{id}": {
      "get": {
        "tags": ["Admin"],
        "operationId": "adminGetArticle",
        "summary": "管理端获取文章",
        "description": "按 ID 获取任意状态的文章（含草稿）。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "文章 ID。" }
        ],
        "responses": {
          "200": {
            "description": "文章详情。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ArticleResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      },
      "put": {
        "tags": ["Admin"],
        "operationId": "adminUpdateArticle",
        "summary": "更新文章",
        "description": "部分更新文章字段。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "文章 ID。" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ArticleUpsertRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ArticleResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      },
      "delete": {
        "tags": ["Admin"],
        "operationId": "adminDeleteArticle",
        "summary": "删除文章",
        "description": "按 ID 删除文章。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "文章 ID。" }
        ],
        "responses": {
          "200": {
            "description": "删除成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OkResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/admin/links": {
      "get": {
        "tags": ["Admin"],
        "operationId": "adminListLinks",
        "summary": "管理端友链列表",
        "description": "含待审核在内的全部友链。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "页码。" },
          { "name": "pageSize", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "每页数量。" },
          { "name": "status", "in": "query", "required": false, "schema": { "type": "integer" }, "description": "按状态过滤。" },
          { "name": "search", "in": "query", "required": false, "schema": { "type": "string" }, "description": "搜索关键字。" }
        ],
        "responses": {
          "200": {
            "description": "友链列表。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LinkListResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Admin"],
        "operationId": "adminCreateLink",
        "summary": "创建友链",
        "description": "新增友链（默认待审核）。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LinkUpsertRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "创建成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LinkResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/admin/links/{id}": {
      "put": {
        "tags": ["Admin"],
        "operationId": "adminUpdateLink",
        "summary": "更新友链",
        "description": "部分更新友链字段。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "友链 ID。" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LinkUpsertRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LinkResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      },
      "delete": {
        "tags": ["Admin"],
        "operationId": "adminDeleteLink",
        "summary": "删除友链",
        "description": "按 ID 删除友链。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "友链 ID。" }
        ],
        "responses": {
          "200": {
            "description": "删除成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OkResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/admin/links/batch-delete": {
      "post": {
        "tags": ["Admin"],
        "operationId": "adminBatchDeleteLinks",
        "summary": "批量删除友链",
        "description": "按 ID 数组批量删除友链。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["ids"],
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": { "type": "string" },
                    "description": "要删除的友链 ID 列表。"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "删除成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OkResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/admin/links/{id}/status": {
      "put": {
        "tags": ["Admin"],
        "operationId": "adminUpdateLinkStatus",
        "summary": "审核友链",
        "description": "更新友链状态（0=待审核，1=通过）。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "友链 ID。" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["status"],
                "properties": {
                  "status": { "type": "integer", "enum": [0, 1], "description": "目标状态。" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LinkResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/admin/posts": {
      "get": {
        "tags": ["Admin"],
        "operationId": "adminListPosts",
        "summary": "管理端动态列表",
        "description": "全部动态。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "页码。" },
          { "name": "pageSize", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1 }, "description": "每页数量。" },
          { "name": "search", "in": "query", "required": false, "schema": { "type": "string" }, "description": "搜索关键字。" }
        ],
        "responses": {
          "200": {
            "description": "动态列表。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PostListResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Admin"],
        "operationId": "adminCreatePost",
        "summary": "创建动态",
        "description": "发布一条动态（内容块 + 可选图片）。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PostUpsertRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "创建成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PostResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/admin/posts/{id}": {
      "get": {
        "tags": ["Admin"],
        "operationId": "adminGetPost",
        "summary": "管理端获取动态",
        "description": "按 ID 获取动态。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "动态 ID。" }
        ],
        "responses": {
          "200": {
            "description": "动态详情。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PostResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      },
      "put": {
        "tags": ["Admin"],
        "operationId": "adminUpdatePost",
        "summary": "更新动态",
        "description": "部分更新动态。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "动态 ID。" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PostUpsertRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PostResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      },
      "delete": {
        "tags": ["Admin"],
        "operationId": "adminDeletePost",
        "summary": "删除动态",
        "description": "按 ID 删除动态。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "动态 ID。" }
        ],
        "responses": {
          "200": {
            "description": "删除成功。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OkResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/admin/stats/overview": {
      "get": {
        "tags": ["Admin"],
        "operationId": "adminStatsOverview",
        "summary": "站点统计总览",
        "description": "文章/动态/友链/评论数量与累计点赞、浏览。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "responses": {
          "200": {
            "description": "统计总览。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StatsOverviewResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/admin/stats/trend": {
      "get": {
        "tags": ["Admin"],
        "operationId": "adminStatsTrend",
        "summary": "站点统计趋势",
        "description": "按天返回发布量与浏览量趋势。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "parameters": [
          { "name": "days", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "default": 30 }, "description": "统计天数。" }
        ],
        "responses": {
          "200": {
            "description": "趋势数据。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StatsTrendResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    },
    "/admin/stats/engagement": {
      "get": {
        "tags": ["Admin"],
        "operationId": "adminStatsEngagement",
        "summary": "互动统计",
        "description": "累计点赞/转发/评论/浏览与热门动态。需要管理员 token。",
        "security": [{ "tokenHeader": [] }],
        "responses": {
          "200": {
            "description": "互动统计。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StatsEngagementResponse" }
              }
            }
          },
          "401": {
            "description": "未认证。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "tokenHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "token",
        "description": "管理员 token。登录后以「token: Bearer <token>」形式携带。"
      }
    },
    "schemas": {
      "EnvelopeMeta": {
        "type": "object",
        "description": "所有响应共用的信封字段。",
        "properties": {
          "code": { "type": "integer", "description": "200 表示成功，其他为业务错误码。", "example": 200 },
          "message": { "type": "string", "description": "人类可读的提示信息。", "example": "success" },
          "success": { "type": "boolean", "description": "是否成功。", "example": true }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "description": "业务错误响应。code 携带机器可读错误码，message 携带可读信息。",
        "required": ["code", "message", "success"],
        "properties": {
          "code": { "type": "integer", "description": "非 200 的业务错误码（401 未认证、500 服务器错误等）。", "example": 401 },
          "data": { "type": "null", "description": "错误时负载为空。", "nullable": true },
          "message": { "type": "string", "description": "错误说明与解决建议（如「请先登录」）。", "example": "请先登录" },
          "success": { "type": "boolean", "description": "固定为 false。", "example": false }
        }
      },
      "OkResponse": {
        "type": "object",
        "description": "无负载的成功响应。",
        "properties": {
          "code": { "type": "integer", "example": 200 },
          "data": { "type": "null", "nullable": true },
          "message": { "type": "string", "example": "success" },
          "success": { "type": "boolean", "example": true }
        }
      },
      "Article": {
        "type": "object",
        "description": "博客文章。",
        "properties": {
          "id": { "type": "string", "description": "文章 ID。", "example": "6a8527ed23058aa8755622cf" },
          "title": { "type": "string", "description": "标题。", "example": "从一个能聊天的脚本，到可扩展的个人 Agent" },
          "excerpt": { "type": "string", "description": "摘要。", "example": "尝试开发个人订制的 agent" },
          "content": { "type": "string", "description": "正文。列表接口为空字符串；详情接口为渲染后的 HTML。", "example": "<p>我做个人 Agent 的时候……</p>" },
          "contentFormat": { "type": "string", "enum": ["html", "markdown"], "description": "源内容格式（正文返回值始终为 HTML）。", "example": "markdown" },
          "coverImage": { "type": "string", "description": "封面图路径（站内相对路径）。", "example": "/uploads/20260819034930.857_cover.png" },
          "category": { "type": "string", "enum": ["tech", "life", "thoughts"], "description": "分类：tech=技术，life=生活，thoughts=随想。", "example": "tech" },
          "status": { "type": "string", "enum": ["published", "draft"], "description": "发布状态。公开接口只返回 published。", "example": "published" },
          "tag": { "type": "string", "description": "标签（单值）。", "example": "agentapplication" },
          "createdAt": { "type": "string", "description": "创建时间（YYYY-MM-DD HH:mm:ss，UTC+8）。", "example": "2026-08-19 03:50:05" },
          "updatedAt": { "type": "string", "description": "更新时间。", "example": "2026-08-20 15:29:59" }
        }
      },
      "ArticleListData": {
        "type": "object",
        "properties": {
          "articles": { "type": "array", "items": { "$ref": "#/components/schemas/Article" } },
          "total": { "type": "integer", "description": "总数。", "example": 141 },
          "page": { "type": "integer", "example": 1 },
          "pageSize": { "type": "integer", "example": 20 }
        }
      },
      "ArticleListResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeMeta" },
          {
            "type": "object",
            "properties": { "data": { "$ref": "#/components/schemas/ArticleListData" } }
          }
        ]
      },
      "ArticleResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeMeta" },
          {
            "type": "object",
            "properties": { "data": { "$ref": "#/components/schemas/Article" } }
          }
        ]
      },
      "ArticleUpsertRequest": {
        "type": "object",
        "required": ["title", "excerpt", "content", "coverImage", "category", "status", "tag"],
        "properties": {
          "title": { "type": "string", "description": "标题。" },
          "excerpt": { "type": "string", "description": "摘要。" },
          "content": { "type": "string", "description": "正文（markdown 或 html）。" },
          "contentFormat": { "type": "string", "enum": ["html", "markdown"], "description": "正文格式，默认 html。" },
          "coverImage": { "type": "string", "description": "封面图路径。" },
          "category": { "type": "string", "enum": ["tech", "life", "thoughts"], "description": "分类。" },
          "status": { "type": "string", "enum": ["published", "draft"], "description": "发布状态。" },
          "tag": { "type": "string", "description": "标签。" }
        }
      },
      "Link": {
        "type": "object",
        "description": "友链站点。",
        "properties": {
          "id": { "type": "string", "description": "友链 ID。" },
          "avatar": { "type": "string", "description": "站点头像 URL。", "example": "https://example.com/avatar.webp" },
          "siteName": { "type": "string", "description": "站点名称。", "example": "Jack's Space" },
          "authorName": { "type": "string", "description": "作者名称。" },
          "siteDescription": { "type": "string", "description": "站点简介。" },
          "url": { "type": "string", "format": "uri", "description": "站点地址。" },
          "status": { "type": "integer", "enum": [0, 1], "description": "0=待审核，1=已通过。", "example": 1 },
          "createdAt": { "type": "string", "description": "收录时间。" },
          "updatedAt": { "type": "string", "description": "更新时间。" }
        }
      },
      "LinkListData": {
        "type": "object",
        "properties": {
          "list": { "type": "array", "items": { "$ref": "#/components/schemas/Link" } },
          "total": { "type": "integer" },
          "page": { "type": "integer" },
          "pageSize": { "type": "integer" }
        }
      },
      "LinkListResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeMeta" },
          {
            "type": "object",
            "properties": { "data": { "$ref": "#/components/schemas/LinkListData" } }
          }
        ]
      },
      "LinkResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeMeta" },
          {
            "type": "object",
            "properties": { "data": { "$ref": "#/components/schemas/Link" } }
          }
        ]
      },
      "LinkUpsertRequest": {
        "type": "object",
        "required": ["avatar", "siteName", "authorName", "siteDescription", "url"],
        "properties": {
          "avatar": { "type": "string", "description": "站点头像 URL。" },
          "siteName": { "type": "string", "description": "站点名称。" },
          "authorName": { "type": "string", "description": "作者名称。" },
          "siteDescription": { "type": "string", "description": "站点简介。" },
          "url": { "type": "string", "description": "站点地址。" },
          "status": { "type": "integer", "enum": [0, 1], "description": "状态，默认 0。" }
        }
      },
      "PostUser": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "username": { "type": "string" },
          "avatar": { "type": "string" },
          "nickname": { "type": "string" }
        }
      },
      "PostContentBlock": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "description": "内容块类型（如 text）。" },
          "content": { "type": "string", "description": "文本内容。" }
        }
      },
      "PostImage": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "url": { "type": "string" },
          "width": { "type": "integer" },
          "height": { "type": "integer" },
          "alt": { "type": "string" }
        }
      },
      "PostEngagement": {
        "type": "object",
        "properties": {
          "likeCount": { "type": "integer" },
          "repostCount": { "type": "integer" },
          "commentCount": { "type": "integer" },
          "viewCount": { "type": "integer" }
        }
      },
      "Post": {
        "type": "object",
        "description": "站内短动态。",
        "properties": {
          "id": { "type": "string" },
          "user": { "$ref": "#/components/schemas/PostUser" },
          "content": { "type": "array", "items": { "$ref": "#/components/schemas/PostContentBlock" } },
          "images": { "type": "array", "items": { "$ref": "#/components/schemas/PostImage" } },
          "engagement": { "$ref": "#/components/schemas/PostEngagement" },
          "status": { "type": "integer" },
          "createdAt": { "type": "string" },
          "updatedAt": { "type": "string" }
        }
      },
      "PostListData": {
        "type": "object",
        "properties": {
          "list": { "type": "array", "items": { "$ref": "#/components/schemas/Post" } },
          "total": { "type": "integer" },
          "page": { "type": "integer" },
          "pageSize": { "type": "integer" }
        }
      },
      "PostListResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeMeta" },
          {
            "type": "object",
            "properties": { "data": { "$ref": "#/components/schemas/PostListData" } }
          }
        ]
      },
      "PostResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeMeta" },
          {
            "type": "object",
            "properties": { "data": { "$ref": "#/components/schemas/Post" } }
          }
        ]
      },
      "PostUpsertRequest": {
        "type": "object",
        "required": ["content"],
        "properties": {
          "content": { "type": "array", "items": { "$ref": "#/components/schemas/PostContentBlock" }, "description": "内容块。" },
          "images": { "type": "array", "items": { "$ref": "#/components/schemas/PostImage" }, "description": "配图。" },
          "status": { "type": "integer", "description": "状态。" },
          "location": { "type": "null", "nullable": true, "description": "位置信息，可为 null。" },
          "settings": {
            "type": "object",
            "properties": {
              "isPublic": { "type": "boolean" },
              "useLocation": { "type": "boolean" },
              "useMarkdown": { "type": "boolean" }
            }
          }
        }
      },
      "TrackRequest": {
        "type": "object",
        "required": ["path", "ts"],
        "properties": {
          "path": { "type": "string", "description": "被访问的页面路径。", "example": "/articles" },
          "ts": { "type": "integer", "description": "Unix 秒级时间戳。", "example": 1761234567 }
        }
      },
      "LoginRequest": {
        "type": "object",
        "required": ["username", "password"],
        "properties": {
          "username": { "type": "string", "description": "管理员用户名。" },
          "password": { "type": "string", "description": "管理员密码。" }
        }
      },
      "LoginResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeMeta" },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "token": { "type": "string", "description": "Bearer token，放入后续请求的 token 头。" }
                }
              }
            }
          }
        ]
      },
      "StatsOverview": {
        "type": "object",
        "properties": {
          "postsCount": { "type": "integer" },
          "articlesCount": { "type": "integer" },
          "linksCount": { "type": "integer" },
          "commentsCount": { "type": "integer" },
          "totalLikes": { "type": "integer" },
          "totalViews": { "type": "integer" }
        }
      },
      "StatsOverviewResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeMeta" },
          {
            "type": "object",
            "properties": { "data": { "$ref": "#/components/schemas/StatsOverview" } }
          }
        ]
      },
      "StatsTrend": {
        "type": "object",
        "properties": {
          "dates": { "type": "array", "items": { "type": "string" } },
          "postsCreated": { "type": "array", "items": { "type": "integer" } },
          "articlesCreated": { "type": "array", "items": { "type": "integer" } },
          "pageViews": { "type": "array", "items": { "type": "integer" } },
          "uniqueVisitors": { "type": "array", "items": { "type": "integer" } }
        }
      },
      "StatsTrendResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeMeta" },
          {
            "type": "object",
            "properties": { "data": { "$ref": "#/components/schemas/StatsTrend" } }
          }
        ]
      },
      "StatsEngagement": {
        "type": "object",
        "properties": {
          "totalLikes": { "type": "integer" },
          "totalReposts": { "type": "integer" },
          "totalComments": { "type": "integer" },
          "totalViews": { "type": "integer" },
          "topPosts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "content": { "type": "string" },
                "likes": { "type": "integer" },
                "views": { "type": "integer" }
              }
            }
          }
        }
      },
      "StatsEngagementResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeMeta" },
          {
            "type": "object",
            "properties": { "data": { "$ref": "#/components/schemas/StatsEngagement" } }
          }
        ]
      }
    }
  },
  "externalDocs": {
    "description": "开发者门户（用法、何时使用与资源索引）",
    "url": "https://dengqn.com/developers"
  }
}
