Skip to content

Latest commit

 

History

History
3848 lines (3042 loc) · 75.7 KB

File metadata and controls

3848 lines (3042 loc) · 75.7 KB

校园论坛 API 文档

本文档详细描述了校园论坛系统的所有 API 接口,供客户端开发者参考。

相关仓库:服务端本仓库 XEKernel/school-forum · Android 客户端 XEKernel/school-forum-android

⚠️ 文档与实现差异(2026-08-07 更新说明)

本文档的接口路径已全部更新为带 /api 前缀(2026-08-02 批量同步,共 141 处):

  • 服务端对不带 /api 的旧路径提供 307 重定向兼容,但仅限 GET 请求与显式 JSON 请求;新客户端请一律使用 /api 前缀
  • 认证方式:文档中要求 body 传 userId/viewerId 的接口,代码已改为从 JWT 取身份Authorization: Bearer <token>),body/query 中传入的 userId 会被忽略(安全修复)。
  • 响应格式:文档示例为 data 嵌套;实际成功响应为 { success, message, ...业务字段 }(业务字段直接展开在顶层,如 usertoken)。
  • 已补齐文档(2026-08-07):忘记密码(/api/forgot-password/*)、回复点赞(/api/posts/:id/comments/:commentId/replies/:replyId/like)、群发广播消息(/api/admin/broadcast-message)、QQ 快捷登录(/api/auth/qq/*,可选功能)。
  • 文档仍未收录(均为代码中已存在):POST /api/unfollowGET /api/blocked/:userId、用户数据导出/导入(/api/user/export-data/api/user/import-data)、公开 IP 统计已移除(/api/ip-stats* 现需管理员认证,同 /api/admin/ip-stats*)。

基础信息

  • Base URL: http://your-domain:2080(默认端口 2080,请以 .env 中 PORT 为准)
  • Content-Type: application/json
  • 响应格式: JSON

统一响应格式

实际实现(src/utils/validationUtils.js):业务字段直接展开在顶层,不使用 data 嵌套。 下方章节的响应示例若为旧格式 "data": {...}请将 data 内的字段上提一层,即 {"success": true, "message": "...", ...data 内字段}

成功响应(实际格式):

{
  "success": true,
  "message": "操作成功",
  "token": "...",
  "user": { "...": "..." }
}

统一认证方式

实际实现:身份一律取自 JWT,body/query 中传入的 userId/viewerId/senderId 会被忽略(安全修复,防身份伪造)。 下方章节若要求 body 传 userId 等身份参数,请忽略该参数,改为携带令牌。

需登录接口请求头:

Authorization: Bearer <accessToken>

令牌说明:

  • accessToken:短期令牌(默认 7 天),通过 POST /api/loginPOST /api/register 获取,响应字段 token
  • refreshToken:长期令牌(默认 30 天),通过 POST /api/refresh-token 换取新 accessToken。
  • 管理员接口额外需要管理员身份(登录管理员账号获得),Authorization 同 user 令牌但要求 isAdmin
  • 令牌失效场景:登出(加入黑名单)、修改密码(access 立即失效,refresh 同步失效)、达到有效期。

错误响应(实际格式):

{
  "success": false,
  "message": "错误信息",
  "code": 400
}

HTTP 状态码约定:成功 200/201;参数错误 400;未认证 401;无权限 403;资源不存在 404;服务器内部错误 500(部分接口对未登录访问返回 401)。


目录


基础接口

健康检查

GET /api/health

响应示例

{
  "success": true,
  "message": "服务器运行正常",
  "timestamp": "2024-01-01T00:00:00.000Z"
}

用户模块

发送注册验证码

POST /api/send-verification-code

请求参数

参数 类型 必填 说明
email string 邮箱地址

请求示例

{
  "email": "user@example.com"
}

响应示例

{
  "success": true,
  "message": "验证码已发送到您的邮箱"
}

发送登录验证码

POST /api/send-login-verification-code

请求参数

参数 类型 必填 说明
email string 邮箱地址

发送密码修改验证码

POST /api/send-password-change-code

请求参数

参数 类型 必填 说明
userId string 用户ID
currentPassword string 当前密码

响应示例

{
  "success": true,
  "message": "验证码已发送到您的邮箱",
  "data": {
    "email": "u***@example.com"
  }
}

验证密码修改验证码

POST /api/verify-password-change-code

请求参数

参数 类型 必填 说明
userId string 用户ID
verificationCode string 6位验证码

修改密码

POST /api/change-password

🔑 需要认证

请求参数

参数 类型 必填 说明
userId string 用户ID
verificationCode string 已验证的验证码
newPassword string 新密码

发送邮箱修改验证码

POST /api/send-email-change-code

🔑 需要认证

请求参数

参数 类型 必填 说明
userId string 用户ID
newEmail string 新邮箱地址
currentPassword string 当前密码

验证并完成邮箱修改

POST /api/verify-email-change

🔑 需要认证

请求参数

参数 类型 必填 说明
userId string 用户ID
verificationCode string 6位验证码

修改 QQ 号

POST /api/change-qq

🔑 需要认证

请求参数

参数 类型 必填 说明
userId string 用户ID
newQq string 新 QQ 号
currentPassword string 当前密码

发送账户注销验证码

POST /api/send-deletion-code

🔑 需要认证

请求参数

参数 类型 必填 说明
userId string 用户ID

注销账户

POST /api/delete-account

🔑 需要认证

请求参数

参数 类型 必填 说明
userId string 用户ID
verificationCode string 6位验证码

获取图形验证码

GET /api/captcha

获取一次性图形验证码(SVG 格式),用于登录、注册和管理员登录时的人机验证。

响应示例

{
  "success": true,
  "data": {
    "captchaId": "uuid-string",
    "svg": "<svg>...</svg>"
  }
}

字段说明

字段 类型 说明
captchaId string 验证码唯一标识,提交时需携带
svg string SVG 图片字符串,直接嵌入 DOM 显示

使用规则

  • 验证码为 4 位随机数字,大小写不敏感
  • 有效期 5 分钟,过期自动失效
  • 一次性使用:验证后立即删除,不可重复使用
  • 点击验证码图片可刷新获取新的验证码

用户注册

POST /api/register

请求参数

参数 类型 必填 说明
qq string QQ号
username string 用户名(2-20字符)
password string 密码(至少6位)
email string 邮箱地址
verificationCode string 6位邮箱验证码
captchaId string 图形验证码ID(从 GET /captcha 获取)
captchaCode string 图形验证码(图片中的4位数字)
school string 学校ID
enrollmentYear number 入学年份
className string 班级名称

请求示例

{
  "qq": "12345678",
  "username": "张三",
  "password": "password123",
  "email": "user@example.com",
  "verificationCode": "123456",
  "captchaId": "uuid-from-captcha-api",
  "captchaCode": "3847",
  "school": "XXXX",
  "enrollmentYear": 2024,
  "className": "高一(1)班"
}

响应示例

{
  "success": true,
  "message": "注册成功",
  "data": {
    "user": {
      "id": "uuid-string",
      "qq": "12345678",
      "username": "张三",
      "email": "user@example.com",
      "school": "XXXX",
      "enrollmentYear": 2024,
      "className": "高一(1)班",
      "grade": "高一",
      "createdAt": "2024-01-01T00:00:00.000Z",
      "postCount": 0,
      "commentCount": 0,
      "isActive": true
    }
  }
}

用户登录

POST /api/login

请求参数

参数 类型 必填 说明
email string 邮箱地址
qq string QQ号
password string 密码
verificationCode string 6位邮箱验证码
captchaId string 图形验证码ID(从 GET /captcha 获取)
captchaCode string 图形验证码(图片中的4位数字)

请求示例

{
  "email": "user@example.com",
  "qq": "12345678",
  "password": "password123",
  "verificationCode": "123456",
  "captchaId": "uuid-from-captcha-api",
  "captchaCode": "5621"
}

响应示例

{
  "success": true,
  "message": "登录成功",
  "data": {
    "user": {
      "id": "uuid-string",
      "qq": "12345678",
      "username": "张三",
      "email": "user@example.com",
      "school": "XXXX",
      "grade": "高一",
      "className": "高一(1)班",
      "avatar": "/images/avatars/xxx.jpg",
      "lastLogin": "2024-01-01T00:00:00.000Z"
    },
    "isAdmin": false
  }
}

QQ 快捷登录(QQ 互联 OAuth2.0)

需先在 QQ 互联 申请应用,并在 .env 配置: QQ_APP_IDQQ_APP_SECRETQQ_REDIRECT_URI(回调地址需与 QQ 互联后台一致)。 未配置时接口返回 400「QQ登录未配置」。

查询 QQ 登录配置状态

GET /api/auth/qq/status

未登录可用。返回 { configured: true|false },登录页据此决定是否显示「QQ 快捷登录」按钮。

获取 QQ 授权 URL(登录场景)

GET /api/auth/qq/authorize-url?type=login

未登录可用。返回 { url, state },前端跳转 url 进入 QQ 授权页。

获取 QQ 授权 URL(绑定场景)

GET /api/auth/qq/authorize-url-bind?type=bind

需登录(Authorization: Bearer token)。用于设置页绑定 QQ 到当前账号。

QQ 授权回调(QQ 服务器重定向)

GET /api/auth/qq/callback?code=xxx&state=yyy

QQ 授权完成后由 QQ 服务器调用(即 QQ_REDIRECT_URI)。服务端换取 openid/用户信息后,302 重定向到前端 /qq-callback.html?state=yyy

获取 QQ 授权结果

GET /api/auth/qq/result?state=yyy

前端回调页调用,state 为一次性凭证(会话 10 分钟有效)。返回:

{
  "success": true,
  "type": "login",
  "result": {
    "needProfile": false,
    "user": { "id": "xxx", "username": "xxx" },
    "token": "JWT访问令牌",
    "refreshToken": "JWT刷新令牌",
    "isAdmin": false,
    "isNewDevice": false
  },
  "prefill": { "nickname": "QQ昵称", "avatar": "头像URL", "gender": "male" }
}
  • result.needProfile = false:已有账号,直接登录成功(前端保存 token 跳首页)
  • result.needProfile = true:新用户,跳转 /qq-register.html?state=yyy 补全资料
  • type = "bind":绑定场景,result.bound 表示绑定结果

QQ 新用户补全资料注册

POST /api/auth/qq/complete-profile
Content-Type: application/json

{
  "state": "yyy",
  "username": "用户名(默认预填QQ昵称,重名自动加后缀)",
  "school": "学校名称",
  "enrollmentYear": 2024,
  "className": "1班",
  "birthday": "2000-01-01",
  "gender": "male"
}

创建账号并直接登录(返回 token/refreshToken)。QQ 快捷注册账号使用占位 邮箱(@qq-oauth.local,不可收信),不发送任何邮件通知;用户可在设置页 修改 QQ 号/绑定真实邮箱后恢复邮件功能。

解绑 QQ

POST /api/auth/qq/unbind
Authorization: Bearer token

需登录。QQ 快捷注册账号(占位 QQ 号)禁止解绑,返回 400。

查询 QQ 绑定状态

GET /api/auth/qq/bind-status
Authorization: Bearer token

需登录。返回 { qqBound, qqPlaceholder, configured },设置页渲染绑定卡片。


发送找回密码验证码(忘记密码)

POST /api/forgot-password/send-code
Content-Type: application/json

{
  "qq": "123456789",
  "email": "user@example.com",
  "captchaId": "图形验证码ID",
  "captchaCode": "1234"
}

未登录可用。校验 QQ 号 + 邮箱是否匹配注册信息 + 图形验证码(一次性), 匹配后向该邮箱发送密码重置验证码。统一错误文案("信息不匹配")防止 账号枚举;接口纳入严格限流(5 次/分钟)。

{
  "success": true,
  "message": "验证码已发送"
}

重置密码(忘记密码)

POST /api/forgot-password/reset
Content-Type: application/json

{
  "email": "user@example.com",
  "verificationCode": "123456",
  "newPassword": "NewPass123!"
}

未登录可用。校验邮箱验证码后重置密码。重置成功后该用户所有旧登录 Token(含其他设备)立即失效(passwordChangedAt 机制),需用新密码重新登录。

{
  "success": true,
  "message": "密码重置成功"
}

验证登录状态

POST /api/auth/verify

请求参数

参数 类型 必填 说明
userId string 用户ID

响应示例

{
  "success": true,
  "message": "用户验证通过",
  "data": {
    "user": { ... },
    "isAdmin": false,
    "isBanned": false,
    "valid": true
  }
}

获取用户资料

GET /api/users/:id

路径参数

参数 类型 说明
id string 用户ID

响应示例

{
  "success": true,
  "data": {
    "user": {
      "id": "uuid-string",
      "qq": "12345678",
      "username": "张三",
      "school": "XXXX",
      "grade": "高一",
      "className": "高一(1)班",
      "avatar": "/images/avatars/xxx.jpg",
      "createdAt": "2024-01-01T00:00:00.000Z"
    },
    "stats": {
      "postCount": 10,
      "commentCount": 25,
      "totalLikes": 50,
      "totalViews": 200,
      "joinDate": "2024-01-01T00:00:00.000Z",
      "lastLogin": "2024-01-15T00:00:00.000Z"
    },
    "recentPosts": [ ... ]
  }
}

修改用户资料

PUT /api/users/:id

路径参数

参数 类型 说明
id string 用户ID

请求参数

参数 类型 必填 说明
currentPassword string 当前密码(修改密码时必填)
newPassword string 新密码
username string 新用户名
settings object 用户设置

请求示例

{
  "currentPassword": "oldpassword",
  "newPassword": "newpassword123",
  "username": "新用户名"
}

更新用户设置

PUT /api/users/:id/settings

请求参数

参数 类型 必填 说明
settings object 设置对象

请求示例

{
  "settings": {
    "theme": "dark",
    "notifications": true
  }
}

上传头像

POST /api/users/:id/avatar

请求格式: multipart/form-data

参数

参数 类型 必填 说明
avatar file 头像图片(JPG/PNG/GIF/WebP,最大2MB)

响应示例

{
  "success": true,
  "message": "头像上传成功",
  "data": {
    "user": { ... },
    "avatarUrl": "/images/avatars/xxx.jpg"
  }
}

删除头像

DELETE /api/users/:id/avatar

请求参数:无(通过路径参数指定用户)


图形验证码模块

图形验证码用于登录、注册和管理员登录时的人机验证,防止自动化脚本攻击。

技术实现

  • 纯 SVG 生成:零依赖,服务端字符串拼接生成 SVG 图片
  • 验证码内容:4 位随机数字 + 干扰线(4 条)+ 噪点(30 个)
  • 存储:Redis 优先,不可用时自动降级到内存 Map
  • 有效期:5 分钟(TTL),过期自动清除
  • 一次性:验证成功后立即删除,防止重用

获取图形验证码

GET /api/captcha

获取新的图形验证码,返回 captchaId 和 SVG 图片。

响应示例

{
  "success": true,
  "data": {
    "captchaId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"130\" height=\"48\">...</svg>"
  }
}

字段说明

字段 类型 说明
captchaId string 验证码唯一标识(UUID v4),提交表单时需携带
svg string SVG 图片字符串,可直接嵌入 HTML DOM 显示

使用流程

  1. 客户端调用 GET /captcha 获取 captchaId 和 SVG
  2. 将 SVG 渲染到页面(innerHTML),存储 captchaId
  3. 用户输入图片中的 4 位数字
  4. 提交表单时携带 captchaIdcaptchaCode
  5. 验证失败后应重新获取验证码(点击图片刷新或提交失败自动刷新)

注意事项

  • 验证码比较大小写不敏感
  • 每个 captchaId 只能验证一次,无论成功或失败
  • 5 分钟内未验证自动过期
  • 建议在登录/注册表单提交失败后自动刷新验证码

帖子模块

获取帖子列表

GET /api/posts

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 100 每页数量
search string - 搜索关键词
sortBy string latest 排序方式
categoryId string - 按栏目筛选
viewerId string - 当前查看者ID(用于个性化推荐)

sortBy 可选值

说明
latest 最新发布(默认)
recommended 推荐排序(防信息茧房混合算法)
relevance 综合热度
likes 点赞数排序
favorites 收藏数排序
views 浏览量排序
comments 评论数排序

推荐算法说明:推荐排序采用混合策略,平衡热度、新鲜度、关注动态和随机探索,避免信息茧房。

  • 40% 热门内容 + 25% 关注动态 + 20% 新鲜内容 + 15% 随机探索
  • 关注用户的帖子权重提升 1.5 倍
  • 48 小时内的新帖子有额外新鲜度加分

响应示例

{
  "success": true,
  "data": {
    "posts": [
      {
        "id": "uuid-string",
        "userId": "user-uuid",
        "username": "张三",
        "userAvatar": "/images/avatars/xxx.jpg",
        "school": "XXXX",
        "grade": "高一",
        "className": "高一(1)班",
        "content": "帖子内容...",
        "images": [
          { "url": "/images/xxx.jpg", "filename": "xxx.jpg" }
        ],
        "anonymous": false,
        "timestamp": "2024-01-01T00:00:00.000Z",
        "likes": 10,
        "dislikes": 0,
        "viewCount": 100,
        "category": {
          "id": "category-uuid",
          "name": "学习交流",
          "icon": "fa-book",
          "color": "#4361ee"
        },
        "comments": [ ... ]
      }
    ],
    "categories": [
      {
        "id": "category-uuid",
        "name": "学习交流",
        "description": "学习相关的讨论区",
        "icon": "fa-book",
        "color": "#4361ee",
        "postCount": 25
      }
    ],
    "pagination": {
      "currentPage": 1,
      "totalPages": 10,
      "totalPosts": 100,
      "hasNext": true,
      "hasPrev": false
    }
  }
}

发布帖子

POST /api/posts

请求格式: multipart/form-data

参数

参数 类型 必填 说明
userId string 用户ID
username string 用户名
school string 学校ID
grade string 年级
className string 班级
content string 否* 帖子内容(无图片时必填)
anonymous string 是否匿名("true")
images file[] 否* 图片文件(最多20张)
categoryId string 所属栏目ID(可选)

*内容和图片至少提供一项

响应示例

{
  "success": true,
  "message": "帖子发布成功",
  "data": {
    "post": {
      "id": "uuid-string",
      "userId": "user-uuid",
      "username": "张三",
      "content": "帖子内容...",
      "images": [ ... ],
      "timestamp": "2024-01-01T00:00:00.000Z",
      "likes": 0,
      "likedBy": [],
      "comments": [],
      "viewCount": 0,
      "isDeleted": false
    }
  }
}

获取帖子详情

GET /api/posts/:id

路径参数

参数 类型 说明
id string 帖子ID

编辑帖子

PUT /api/posts/:id

请求格式: multipart/form-data

参数

参数 类型 必填 说明
userId string 用户ID
content string 新内容
deletedImages string JSON数组,要删除的图片URL
images file[] 新增图片

删除帖子

DELETE /api/posts/:id

请求参数

参数 类型 必填 说明
userId string 用户ID(必须是帖子作者)

增加浏览量

POST /api/posts/:id/view

响应示例

{
  "success": true,
  "data": {
    "viewCount": 101
  }
}

点赞帖子

POST /api/posts/:id/like

请求参数

参数 类型 必填 说明
userId string 用户ID

响应示例

{
  "success": true,
  "message": "点赞成功",
  "data": {
    "likes": 11,
    "liked": true,
    "dislikes": 0,
    "disliked": false
  }
}

点踩帖子

POST /api/posts/:id/dislike

请求参数

参数 类型 必填 说明
userId string 用户ID

响应示例

{
  "success": true,
  "message": "点踩成功",
  "data": {
    "dislikes": 1,
    "disliked": true,
    "likes": 10,
    "liked": false
  }
}

添加评论

POST /api/posts/:id/comments

路径参数

参数 类型 说明
id string 帖子ID

请求参数

参数 类型 必填 说明
userId string 用户ID
username string 用户名
content string 评论内容(最多500字)
anonymous boolean 是否匿名

响应示例

{
  "success": true,
  "message": "评论添加成功",
  "data": {
    "comment": {
      "id": "uuid-string",
      "userId": "user-uuid",
      "username": "张三",
      "content": "评论内容",
      "anonymous": false,
      "timestamp": "2024-01-01T00:00:00.000Z"
    }
  }
}

点赞评论

POST /api/posts/:id/comments/:commentId/like
Authorization: Bearer token

需登录。点赞/取消点赞评论(重复点击切换),返回最新点赞数与点赞状态。

路径参数

参数 类型 说明
id string 帖子ID
commentId string 评论ID

响应

{ "success": true, "likes": 1, "liked": true, "message": "点赞成功" }

回复点赞(任意嵌套层级)见「点赞回复(任意层级)」一节。


回复评论

POST /api/posts/:id/comments/:commentId/replies

路径参数

参数 类型 说明
id string 帖子ID
commentId string 评论ID

请求参数

参数 类型 必填 说明
userId string 用户ID
username string 用户名
content string 回复内容
anonymous boolean 是否匿名
replyToId string 被回复的回复ID(嵌套回复)

删除评论

DELETE /api/posts/:id/comments/:commentId

请求参数

参数 类型 必填 说明
userId string 用户ID
replyId string 回复ID(删除回复时)
nestedReplyId string 嵌套回复ID

点赞回复(任意层级)

POST /api/posts/:id/comments/:commentId/replies/:replyId/like
Authorization: Bearer token

需登录。递归定位任意嵌套层级的回复进行点赞/取消点赞(重复点击切换), 支持 6 层嵌套回复中的任意层。点赞时通知回复作者(复用 comment_like 通知)。

请求参数(URL):

参数 类型 说明
id string 帖子ID
commentId string 顶层评论ID
replyId string 目标回复ID(可为任意层级)

响应

{ "success": true, "likes": 1, "liked": true, "message": "点赞成功" }

统一响应格式说明

本系统所有 API 响应均为 JSON 格式,大多数接口采用嵌套式响应

{
  "success": true,
  "message": "操作成功",
  "data": { ... }
}

部分接口(如管理后台、运行模式相关)采用展开式响应,数据直接展开到根级别:

{
  "success": true,
  "message": "操作成功",
  "mode": "normal",
  "maintenanceMessage": "",
  ...其他数据字段
}

⚠️ 客户端注意:调用接口时请根据实际路由判断响应格式:

  • /admin/*/run-mode 相关接口 → 展开式响应
  • 其他接口 → 嵌套式响应(data 字段)
  • 不确定时,可同时检查 response.dataresponse.mode 等字段

栏目模块

获取所有已启用栏目

GET /api/categories

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 20 每页数量

响应示例

{
  "success": true,
  "data": {
    "categories": [
      {
        "id": "uuid-string",
        "name": "学习交流",
        "description": "学习方法和经验分享",
        "icon": "book",
        "color": "#4CAF50",
        "order": 1,
        "isActive": true,
        "postCount": 156
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 5
    }
  }
}

获取单个栏目详情

GET /api/categories/:id

路径参数

参数 类型 说明
id string 栏目ID

响应示例

{
  "success": true,
  "data": {
    "id": "uuid-string",
    "name": "学习交流",
    "description": "学习方法和经验分享",
    "icon": "book",
    "color": "#4CAF50",
    "order": 1,
    "isActive": true,
    "postCount": 156
  }
}

获取栏目帖子

GET /api/categories/:id/posts

路径参数

参数 类型 说明
id string 栏目ID

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 20 每页数量

响应示例

{
  "success": true,
  "data": {
    "posts": [...],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 156
    },
    "category": {
      "id": "uuid-string",
      "name": "学习交流"
    }
  }
}

申请新建栏目

POST /api/category-applications

需要认证

请求参数

参数 类型 必填 说明
categoryName string 栏目名称(最多30字符)
description string 申请理由(最多500字符)

响应示例

{
  "success": true,
  "message": "申请已提交,请等待管理员审核"
}

管理员:获取所有栏目

GET /api/admin/categories

需要管理员权限

响应示例

{
  "success": true,
  "data": {
    "categories": [
      {
        "id": "uuid-string",
        "name": "学习交流",
        "description": "学习方法和经验分享",
        "icon": "book",
        "color": "#4CAF50",
        "order": 1,
        "isActive": true,
        "postCount": 156,
        "createdAt": "2024-01-01T00:00:00.000Z"
      }
    ]
  }
}

管理员:创建栏目

POST /api/admin/categories

需要管理员权限

请求参数

参数 类型 必填 说明
name string 栏目名称
description string 栏目描述
icon string 图标名称(FontAwesome)
color string 主题颜色(十六进制)
order number 排序序号

响应示例

{
  "success": true,
  "message": "栏目创建成功",
  "data": {
    "id": "uuid-string",
    "name": "新栏目",
    "description": "栏目描述",
    "icon": "folder",
    "color": "#2196F3",
    "order": 10,
    "isActive": true,
    "postCount": 0
  }
}

管理员:更新栏目

PUT /api/admin/categories/:id

需要管理员权限

请求参数

参数 类型 必填 说明
name string 栏目名称
description string 栏目描述
icon string 图标名称
color string 主题颜色
order number 排序序号

管理员:删除栏目

DELETE /api/admin/categories/:id

需要管理员权限

响应示例

{
  "success": true,
  "message": "栏目已删除"
}

管理员:切换栏目启用状态

PATCH /api/admin/categories/:id/toggle-status

需要管理员权限

响应示例

{
  "success": true,
  "message": "栏目已禁用",
  "data": {
    "isActive": false
  }
}

管理员:获取所有栏目申请

GET /api/admin/category-applications

需要管理员权限

响应示例

{
  "success": true,
  "data": {
    "applications": [
      {
        "id": "uuid-string",
        "userId": "user-uuid",
        "categoryName": "游戏讨论",
        "description": "希望开设游戏讨论区",
        "status": "pending",
        "createdAt": "2024-01-01T00:00:00.000Z"
      }
    ]
  }
}

管理员:批准栏目申请

POST /api/admin/category-applications/:id/approve

需要管理员权限

响应示例

{
  "success": true,
  "message": "申请已批准,栏目已创建"
}

管理员:拒绝栏目申请

POST /api/admin/category-applications/:id/reject

需要管理员权限

响应示例

{
  "success": true,
  "message": "申请已拒绝"
}

关注模块

关注用户

POST /api/follow

请求参数

参数 类型 必填 说明
followerId string 关注者ID
followingId string 被关注者ID

响应示例

{
  "success": true,
  "message": "关注成功",
  "data": {
    "following": true
  }
}

取消关注

DELETE /api/follow

请求参数

参数 类型 必填 说明
followerId string 关注者ID
followingId string 被关注者ID

检查关注状态

GET /api/follow/status

查询参数

参数 类型 必填 说明
followerId string 关注者ID
followingId string 被关注者ID

响应示例

{
  "success": true,
  "data": {
    "isFollowing": true
  }
}

获取关注统计

GET /api/follow/stats/:userId

响应示例

{
  "success": true,
  "data": {
    "followingCount": 10,
    "followerCount": 25
  }
}

获取关注列表

GET /api/following/:userId

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 20 每页数量
currentUserId string - 当前用户ID

响应示例

{
  "success": true,
  "data": {
    "list": [
      {
        "id": "user-uuid",
        "username": "张三",
        "avatar": "/images/avatars/xxx.jpg",
        "school": "XXXX",
        "grade": "高一",
        "className": "高一(1)班",
        "followedAt": "2024-01-01T00:00:00.000Z",
        "isAdmin": false,
        "isFollowing": true
      }
    ],
    "pagination": {
      "currentPage": 1,
      "totalPages": 5,
      "total": 100,
      "hasNext": true,
      "hasPrev": false
    }
  }
}

获取粉丝列表

GET /api/followers/:userId

参数同关注列表。


获取关注用户的帖子

GET /api/following/posts/:userId

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 10 每页数量

获取新帖子数量

GET /api/follow/new-posts/:userId

响应示例

{
  "success": true,
  "data": {
    "count": 5
  }
}

标记已查看关注动态

POST /api/follow/mark-viewed

请求参数

参数 类型 必填 说明
userId string 用户ID

收藏模块

收藏帖子

POST /api/favorites/:postId

路径参数

参数 类型 说明
postId string 帖子ID

请求参数

参数 类型 必填 说明
userId string 用户ID
tagId string 标签ID

响应示例

{
  "success": true,
  "message": "收藏成功",
  "data": {
    "favorited": true,
    "favorite": {
      "userId": "user-uuid",
      "postId": "post-uuid",
      "tagId": null,
      "createdAt": "2024-01-01T00:00:00.000Z"
    }
  }
}

取消收藏

DELETE /api/favorites/:postId

请求参数

参数 类型 必填 说明
userId string 用户ID

检查是否已收藏

GET /api/favorites/:postId/check

查询参数

参数 类型 必填 说明
userId string 用户ID

响应示例

{
  "success": true,
  "data": {
    "favorited": true,
    "favoriteCount": 10,
    "tagId": "tag-uuid"
  }
}

获取用户收藏列表

GET /api/favorites/user/:userId

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 10 每页数量
tagId string - 按标签筛选

响应示例

{
  "success": true,
  "data": {
    "posts": [
      {
        "id": "post-uuid",
        "content": "帖子内容...",
        "isDeleted": false,
        "userAvatar": "/images/avatars/xxx.jpg",
        "favoriteAt": "2024-01-01T00:00:00.000Z",
        "tagId": "tag-uuid"
      }
    ],
    "pagination": { ... }
  }
}

获取收藏数量

GET /api/favorites/user/:userId/count

更新收藏标签

PUT /api/favorites/:postId/tag

请求参数

参数 类型 必填 说明
userId string 用户ID
tagId string 标签ID(null表示取消标签)

批量删除收藏

POST /api/favorites/batch/delete

请求参数

参数 类型 必填 说明
userId string 用户ID
postIds string[] 帖子ID数组

批量移动到标签

POST /api/favorites/batch/move

请求参数

参数 类型 必填 说明
userId string 用户ID
postIds string[] 帖子ID数组
tagId string 目标标签ID

获取用户标签

GET /api/favorites/tags/:userId

响应示例

{
  "success": true,
  "data": {
    "tags": [
      {
        "_id": "tag-uuid",
        "userId": "user-uuid",
        "name": "学习资料",
        "color": "#4361ee",
        "order": 0,
        "favoriteCount": 5
      }
    ]
  }
}

创建标签

POST /api/favorites/tags

请求参数

参数 类型 必填 说明
userId string 用户ID
name string 标签名称
color string 标签颜色(默认 #4361ee)

更新标签

PUT /api/favorites/tags/:tagId

请求参数

参数 类型 必填 说明
userId string 用户ID
name string 标签名称
color string 标签颜色

删除标签

DELETE /api/favorites/tags/:tagId

请求参数

参数 类型 必填 说明
userId string 用户ID

更新标签排序

PUT /api/favorites/tags/order

请求参数

参数 类型 必填 说明
userId string 用户ID
tagOrders array 排序数据 [{id, order}, ...]

通知模块

获取通知列表

GET /api/notifications

查询参数

参数 类型 必填 说明
userId string 用户ID

响应示例

{
  "success": true,
  "data": {
    "notifications": [
      {
        "id": "notification-uuid",
        "userId": "user-uuid",
        "type": "like",
        "postId": "post-uuid",
        "postTitle": "帖子标题...",
        "fromUserId": "from-user-uuid",
        "fromUsername": "张三",
        "timestamp": "2024-01-01T00:00:00.000Z",
        "read": false,
        "postExists": true
      }
    ]
  }
}

通知类型

type 说明
like 点赞通知
comment 评论通知
comment_reply 回复通知
follow 关注通知
system 系统通知

标记通知已读

POST /api/notifications/:id/read

路径参数

参数 类型 说明
id string 通知ID

请求参数

参数 类型 必填 说明
userId string 用户ID

标记全部已读

POST /api/notifications/read-all

请求参数

参数 类型 必填 说明
userId string 用户ID

举报模块

获取举报类型

GET /api/reports/types

响应示例

{
  "success": true,
  "data": {
    "types": {
      "SPAM": "垃圾广告",
      "HARASSMENT": "骚扰辱骂",
      "INAPPROPRIATE": "不当内容",
      "FALSE_INFO": "虚假信息",
      "COPYRIGHT": "侵权内容",
      "OTHER": "其他"
    }
  }
}

提交举报

POST /api/reports

请求参数

参数 类型 必填 说明
reporterId string 举报人ID
targetType string 目标类型:post/comment
targetId string 目标ID
reason string 举报原因(见类型列表)
description string 详细描述

请求示例

{
  "reporterId": "user-uuid",
  "targetType": "post",
  "targetId": "post-uuid",
  "reason": "SPAM",
  "description": "这是垃圾广告"
}

获取用户举报历史

GET /api/reports/user/:userId

统计模块

获取统计数据

GET /api/stats

响应示例

{
  "success": true,
  "data": {
    "stats": {
      "totalUsers": 1000,
      "totalPosts": 5000,
      "todayPosts": 50,
      "totalComments": 10000,
      "totalLikes": 25000,
      "activeUsers": 500,
      "anonymousPosts": 200
    }
  }
}

搜索

GET /api/search

查询参数

参数 类型 必填 默认值 说明
q string - 搜索关键词
type string posts 搜索类型:posts/users
page number 1 页码
limit number 100 每页数量

配置模块

获取学校列表

GET /api/schools

响应示例

{
  "success": true,
  "data": {
    "schools": [
      {
        "id": "XXXX",
        "name": "XX学校",
        "classInfo": [
          { "year": 2024, "classCount": 25 }
        ]
      }
    ]
  }
}

获取公开配置

GET /api/config/public

响应示例

{
  "success": true,
  "data": {
    "config": {
      "contentLimits": {
        "post": 10000,
        "comment": 500,
        "username": { "min": 2, "max": 20 }
      },
      "upload": {
        "maxFiles": 32,
        "maxFileSize": 33554432,
        "allowedTypes": ["image/jpeg", "image/png", ...]
      }
    }
  }
}

私信模块

发送私信

POST /api/messages

请求参数

参数 类型 必填 说明
senderId string 发送者ID
receiverId string 接收者ID
content string 消息内容(最多2000字)

请求示例

{
  "senderId": "user-uuid-1",
  "receiverId": "user-uuid-2",
  "content": "你好!"
}

响应示例

{
  "success": true,
  "message": "消息发送成功",
  "data": {
    "message": {
      "id": "message-uuid",
      "conversationId": "conversation-uuid",
      "senderId": "user-uuid-1",
      "receiverId": "user-uuid-2",
      "content": "你好!",
      "type": "text",
      "read": false,
      "createdAt": "2024-01-01T00:00:00.000Z",
      "senderUsername": "张三",
      "senderAvatar": "/images/avatars/xxx.jpg"
    }
  }
}

发送规则

  • 互相关注的用户可以无限次互发消息
  • 非互关用户:A发消息给B后,必须等B回复才能继续发送
  • 被对方拉黑或自己拉黑对方时无法发送

获取消息记录

GET /api/messages

查询参数

参数 类型 必填 默认值 说明
userId string - 当前用户ID
otherUserId string - 对方用户ID
limit number 50 每页数量
before string - 获取此时间之前的消息

响应示例

{
  "success": true,
  "data": {
    "messages": [
      {
        "id": "message-uuid",
        "conversationId": "conversation-uuid",
        "senderId": "user-uuid-1",
        "receiverId": "user-uuid-2",
        "content": "你好!",
        "type": "text",
        "read": true,
        "createdAt": "2024-01-01T00:00:00.000Z",
        "senderUsername": "张三",
        "senderAvatar": "/images/avatars/xxx.jpg"
      }
    ]
  }
}

获取未读消息总数

GET /api/messages/unread

查询参数

参数 类型 必填 说明
userId string 用户ID

响应示例

{
  "success": true,
  "data": {
    "unreadCount": 5
  }
}

检查发送权限

GET /api/messages/check-permission

查询参数

参数 类型 必填 说明
senderId string 发送者ID
receiverId string 接收者ID

响应示例

{
  "success": true,
  "data": {
    "canSend": true,
    "reason": "互相关注用户",
    "relation": {
      "isFollowing": true,
      "isFollower": true,
      "isMutualFollow": true
    },
    "blockStatus": {
      "isBlocked": false,
      "isBlockedBy": false
    }
  }
}

获取可联系用户列表

GET /api/messages/contactable-users

查询参数

参数 类型 必填 说明
userId string 用户ID

响应示例

{
  "success": true,
  "data": {
    "users": [
      {
        "id": "user-uuid",
        "username": "张三",
        "avatar": "/images/avatars/xxx.jpg",
        "school": "XXXX",
        "isFollowing": true,
        "isFollower": true
      }
    ]
  }
}

删除单条消息

DELETE /api/messages/:messageId

路径参数

参数 类型 说明
messageId string 消息ID

请求参数

参数 类型 必填 说明
userId string 用户ID

获取会话列表

GET /api/conversations

查询参数

参数 类型 必填 说明
userId string 用户ID

响应示例

{
  "success": true,
  "data": {
    "conversations": [
      {
        "id": "conversation-uuid",
        "otherUser": {
          "id": "user-uuid",
          "username": "张三",
          "avatar": "/images/avatars/xxx.jpg",
          "school": "XXXX"
        },
        "lastMessage": {
          "content": "好的",
          "senderId": "user-uuid",
          "createdAt": "2024-01-01T00:00:00.000Z"
        },
        "updatedAt": "2024-01-01T00:00:00.000Z",
        "unreadCount": 2
      }
    ]
  }
}

删除会话

DELETE /api/conversations/:conversationId

路径参数

参数 类型 说明
conversationId string 会话ID

请求参数

参数 类型 必填 说明
userId string 用户ID

黑名单模块

拉黑用户

POST /api/block

请求参数

参数 类型 必填 说明
blockerId string 拉黑者ID
blockedId string 被拉黑者ID

响应示例

{
  "success": true,
  "message": "拉黑成功",
  "data": {
    "blocked": true
  }
}

注意:拉黑用户时会自动取消双方的关注关系


取消拉黑

POST /api/unblock

请求参数

参数 类型 必填 说明
blockerId string 拉黑者ID
blockedId string 被拉黑者ID

响应示例

{
  "success": true,
  "message": "取消拉黑成功",
  "data": {
    "blocked": false
  }
}

检查拉黑状态

GET /api/block/status

查询参数

参数 类型 必填 说明
blockerId string 用户ID
blockedId string 目标用户ID

响应示例

{
  "success": true,
  "data": {
    "isBlocked": true,
    "isBlockedBy": false
  }
}

检查拉黑关系

GET /api/block/relation

查询参数

参数 类型 必填 说明
userId1 string 用户1 ID
userId2 string 用户2 ID

响应示例

{
  "success": true,
  "data": {
    "hasBlockRelation": true
  }
}

获取拉黑列表

GET /api/blocked/:userId

路径参数

参数 类型 说明
userId string 用户ID

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 20 每页数量

响应示例

{
  "success": true,
  "data": {
    "list": [
      {
        "id": "user-uuid",
        "username": "张三",
        "avatar": "/images/avatars/xxx.jpg",
        "school": "XXXX",
        "grade": "高一",
        "className": "高一(1)班",
        "blockedAt": "2024-01-01T00:00:00.000Z",
        "isAdmin": false
      }
    ],
    "pagination": {
      "currentPage": 1,
      "totalPages": 5,
      "total": 100,
      "hasNext": true,
      "hasPrev": false
    }
  }
}

获取拉黑数量

GET /api/blocked/count/:userId

路径参数

参数 类型 说明
userId string 用户ID

响应示例

{
  "success": true,
  "data": {
    "count": 10
  }
}

公告模块

获取有效公告列表

GET /api/announcements/active

响应示例

{
  "success": true,
  "data": {
    "announcements": [
      {
        "id": "announcement-uuid",
        "title": "系统公告",
        "content": "公告内容...",
        "type": "info",
        "pinned": true,
        "active": true,
        "createdAt": "2024-01-01T00:00:00.000Z",
        "updatedAt": "2024-01-01T00:00:00.000Z"
      }
    ]
  }
}

获取公告详情

GET /api/announcements/:id

路径参数

参数 类型 说明
id string 公告ID

群发站内消息(广播通知全体用户)

POST /api/admin/broadcast-message
Authorization: Bearer adminToken
Content-Type: application/json

{
  "title": "周末活动通知",
  "content": "本周六下午学校篮球场举行社团招新活动,欢迎大家参加!"
}

需管理员登录。创建一条广播通知target: 'all'),所有用户的「消息」 列表均可看到(单文档共享,不逐用户插入,海量用户也不卡)。适合活动、 维护等全体通知。

响应

{ "success": true, "message": "广播消息已发送", "notificationId": "..." }

广播通知的已读机制:广播是全体共享文档,已读状态按用户独立记录 (readBy 数组),read 字段在响应层按当前用户计算——A 用户已读不影响 B 用户的未读状态;"全部已读"与未读数统计均包含广播通知。


运行模式模块

获取当前运行模式

GET /api/run-mode

响应示例(展开式响应):

{
  "success": true,
  "message": "获取成功",
  "mode": "normal",
  "message": null
}

mode 可选值

说明
normal 正常运行
maintenance 维护模式(普通用户无法访问)
readonly 只读模式(无法发帖/评论)

获取维护模式消息

GET /api/maintenance-message

响应示例

{
  "success": true,
  "data": {
    "message": "系统维护中,预计恢复时间:2024-01-01 12:00"
  }
}

管理后台

以下接口需要管理员权限,通过中间件验证。

获取帖子列表(含已删除)

GET /api/admin/posts

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 100 每页数量
search string - 搜索关键词

永久删除帖子

DELETE /api/admin/posts/:id

请求参数

参数 类型 必填 说明
adminId string 管理员ID
reason string 删除原因

获取所有用户

GET /api/admin/users

获取封禁用户列表

GET /api/admin/banned-users

封禁用户

POST /api/admin/users/:id/ban

路径参数

参数 类型 说明
id string 用户ID

请求参数

参数 类型 必填 说明
adminId string 管理员ID
duration number 封禁天数(默认7,365为永久)
reason string 封禁原因

解封用户

POST /api/admin/users/:id/unban

请求参数

参数 类型 必填 说明
adminId string 管理员ID

获取详细统计

GET /api/admin/stats

响应示例

{
  "success": true,
  "data": {
    "stats": {
      "totalUsers": 1000,
      "totalPosts": 5000,
      "bannedUsers": 10,
      "activeUsers": 800,
      "todayPosts": 50,
      "weekPosts": 300,
      "monthPosts": 1000,
      "totalComments": 10000,
      "totalLikes": 25000,
      "anonymousPosts": 200,
      "gradeDistribution": { "高一": 300, "高二": 350 },
      "schoolDistribution": { "XXXX": 500, "YYYY": 300 },
      "topActiveUsers": [ ... ]
    }
  }
}

获取最近活动

GET /api/admin/recent-activity

获取所有评论

GET /api/admin/comments

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 100 每页数量
search string - 搜索关键词

删除评论

DELETE /api/admin/comments/:id

请求参数

参数 类型 必填 说明
adminId string 管理员ID
postId string 帖子ID
reason string 删除原因

获取日志

GET /api/admin/logs

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 50 每页数量
level string ALL 日志级别
search string - 搜索关键词
date string - 日期(YYYY-MM-DD)

获取日志日期列表

GET /api/admin/logs/dates

清空日志

DELETE /api/admin/logs

请求参数

参数 类型 必填 说明
adminId string 管理员ID
date string 日期(不填则清空全部)

删除指定日期日志

DELETE /api/admin/logs/date

请求参数

参数 类型 必填 说明
adminId string 管理员ID
date string 日期(YYYY-MM-DD)

获取举报列表

GET /api/admin/reports

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 100 每页数量
status string - 状态:pending/processed/rejected

获取举报统计

GET /api/admin/reports/stats

处理举报

POST /api/admin/reports/:reportId/process

请求参数

参数 类型 必填 说明
adminId string 管理员ID
action string 操作:approve/reject
banDuration number 封禁天数
note string 备注

获取配置

GET /api/admin/config

更新配置

PUT /api/admin/config

请求参数

参数 类型 必填 说明
adminId string 管理员ID
updates object 配置更新对象

获取管理员列表

GET /api/admin/admins

添加管理员

POST /api/admin/admins

请求参数

参数 类型 必填 说明
adminId string 当前管理员ID
newAdminId string 新管理员QQ号或用户ID

删除管理员

DELETE /api/admin/admins

请求参数

参数 类型 必填 说明
adminId string 当前管理员ID
targetAdminId string 目标管理员ID

获取所有公告(管理员)

GET /api/admin/announcements

创建公告

POST /api/admin/announcements

请求参数

参数 类型 必填 说明
title string 公告标题
content string 公告内容(支持 Markdown)
type string 类型:info/warning/error(默认 info
pinned boolean 是否置顶
active boolean 是否启用(默认 true

更新公告

PUT /api/admin/announcements/:id

参数同创建公告(均为选填)。


删除公告

DELETE /api/admin/announcements/:id

切换公告启用状态

PATCH /api/admin/announcements/:id/toggle-status

切换公告置顶状态

PATCH /api/admin/announcements/:id/toggle-pinned

批量更新公告状态

PATCH /api/admin/announcements/batch-status

请求参数

参数 类型 必填 说明
ids string[] 公告ID数组
active boolean 目标状态

获取 IP 访问统计列表(管理员)

GET /api/admin/ip-stats

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 100 每页数量

获取 IP 统计摘要(管理员)

GET /api/admin/ip-stats/summary

清除指定 IP 统计

DELETE /api/admin/ip-stats/:ip

清除所有 IP 统计

DELETE /api/admin/ip-stats

设置运行模式

POST /api/admin/run-mode

请求参数

参数 类型 必填 说明
mode string normal / maintenance / readonly
message string 维护时显示的消息

栏目管理

获取所有栏目(含禁用的)

GET /api/admin/categories

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 50 每页数量
isActive boolean - 按启用状态筛选

创建栏目

POST /api/admin/categories

请求参数

参数 类型 必填 说明
name string 栏目名称
description string 栏目描述
icon string 图标(FontAwesome类名,默认 fa-folder)
color string 颜色(十六进制,默认 #4361ee)
order number 排序权重(默认0)

更新栏目

PUT /api/admin/categories/:id

路径参数

参数 类型 说明
id string 栏目ID

请求参数

参数 类型 必填 说明
name string 栏目名称
description string 栏目描述
icon string 图标
color string 颜色
order number 排序权重
isActive boolean 是否启用

删除栏目

DELETE /api/admin/categories/:id

路径参数

参数 类型 说明
id string 栏目ID

注意:删除栏目后,该栏目的帖子将移至"无栏目"状态,不会被删除。


切换栏目启用状态

PATCH /api/admin/categories/:id/toggle-status

路径参数

参数 类型 说明
id string 栏目ID

响应示例

{
  "success": true,
  "message": "栏目已启用",
  "data": {
    "category": {
      "id": "uuid-string",
      "name": "学习交流",
      "isActive": true
    }
  }
}

栏目申请管理

获取所有申请

GET /api/admin/category-applications

查询参数

参数 类型 必填 默认值 说明
page number 1 页码
limit number 20 每页数量
status string - 状态:pending/approved/rejected

批准栏目申请

POST /api/admin/category-applications/:id/approve

路径参数

参数 类型 说明
id string 申请ID

请求参数

参数 类型 必填 说明
reviewNote string 审核备注

响应示例

{
  "success": true,
  "message": "栏目「游戏讨论」创建成功",
  "data": {
    "category": {
      "id": "uuid-string",
      "name": "游戏讨论"
    },
    "application": {
      "id": "uuid-string",
      "status": "approved"
    }
  }
}

拒绝栏目申请

POST /api/admin/category-applications/:id/reject

路径参数

参数 类型 说明
id string 申请ID

请求参数

参数 类型 必填 说明
reviewNote string 拒绝原因

自毁模式 - 三级

POST /api/admin/self-destruct/level3

⚠️ 危险操作:删除所有帖子、评论、私信数据


自毁模式 - 二级

POST /api/admin/self-destruct/level2

⚠️ 危险操作:清空整个数据库


自毁模式 - 一级

POST /api/admin/self-destruct/level1

⚠️ 极危险操作:删除论坛所有文件


错误码说明

HTTP状态码 说明
200 成功
201 创建成功
400 请求参数错误
401 未授权(密码错误等)
403 禁止访问(无权限)
404 资源不存在
500 服务器内部错误

数据模型

用户 (User)

/**
 * @typedef {Object} User
 * @property {string} id - UUID
 * @property {string} qq - QQ号
 * @property {string} username - 用户名
 * @property {string} email - 邮箱
 * @property {string} password - 加密密码(不返回)
 * @property {string} school - 学校ID
 * @property {number} enrollmentYear - 入学年份
 * @property {string} className - 班级
 * @property {string} grade - 年级
 * @property {string} [avatar] - 头像URL
 * @property {string} createdAt - 注册时间
 * @property {string} [lastLogin] - 最后登录
 * @property {number} postCount - 发帖数
 * @property {number} commentCount - 评论数
 * @property {boolean} isActive - 是否激活
 * @property {Object} [settings] - 用户设置
 */

帖子 (Post)

/**
 * @typedef {Object} Post
 * @property {string} id - UUID
 * @property {string} userId - 作者ID
 * @property {string} username - 作者名
 * @property {string} school - 学校
 * @property {string} grade - 年级
 * @property {string} className - 班级
 * @property {string} content - 内容
 * @property {Image[]} images - 图片
 * @property {boolean} anonymous - 是否匿名
 * @property {string} timestamp - 发布时间
 * @property {string} [updatedAt] - 更新时间
 * @property {number} likes - 点赞数
 * @property {string[]} likedBy - 点赞用户ID
 * @property {number} dislikes - 点踩数
 * @property {string[]} dislikedBy - 点踩用户ID
 * @property {number} viewCount - 浏览量
 * @property {Comment[]} comments - 评论
 * @property {boolean} isDeleted - 是否删除
 */

图片 (Image)

/**
 * @typedef {Object} Image
 * @property {string} url - 图片URL
 * @property {string} filename - 文件名
 */

评论 (Comment)

/**
 * @typedef {Object} Comment
 * @property {string} id - UUID
 * @property {string} userId - 作者ID
 * @property {string} username - 作者名
 * @property {string} content - 内容
 * @property {boolean} anonymous - 是否匿名
 * @property {string} timestamp - 时间
 * @property {Reply[]} [replies] - 回复
 */

/**
 * @typedef {Object} Reply
 * @property {string} id - UUID
 * @property {string} userId - 作者ID
 * @property {string} username - 作者名
 * @property {string} content - 内容
 * @property {boolean} anonymous - 是否匿名
 * @property {string} replyTo - 回复目标ID
 * @property {string} timestamp - 时间
 * @property {Reply[]} [replies] - 嵌套回复
 */

安全说明

认证机制

JWT 双 Token 机制

本系统采用 JWT 双 Token 认证:

Token 类型 用途 有效期 存储位置
accessToken API 请求认证 7 天 内存 / localStorage
refreshToken 刷新 accessToken 30 天 localStorage
管理员 Token 管理后台认证 24 小时 内存

Android 端 Token 自动刷新

Android 客户端内置 Token 刷新拦截器(TokenRefreshInterceptor),在收到 401 响应时自动:

  1. 清除过期的 accessToken
  2. 使用 refreshToken 请求刷新
  3. 重试原始请求
  4. 若刷新失败,引导用户重新登录

前端 Token 管理

前端通过 userManager.checkAutoLogin() 自动检查登录状态:

  • 有效 Token → 自动登录,初始化用户信息
  • 无效/过期 Token → 保持未登录状态
  • 401 响应 → 清除 Token,跳转登录页

安全防护

请求防护

防护类型 实现方式
XSS 过滤 输入内容经过 sanitize-html 处理
MongoDB 注入防护 SQL 关键词检测 + 参数化查询
请求限流 基于 IP 的滑动窗口限流
登录锁定 连续 5 次登录失败,锁定 30 分钟
恶意 User-Agent 检测并记录可疑请求
图形验证码 登录/注册/管理员登录需通过人机验证(4位随机数字 SVG,5分钟有效,一次性使用)

CORS 配置

  • 开发环境:允许 localhost 和内网 IP(192.168.x.x / 10.x.x.x / 172.16-31.x.x)
  • 生产环境:通过 CORS_ORIGIN 环境变量配置,允许多个来源(逗号分隔)
  • HTTP 环境:禁用 HSTS 和高级安全响应头

管理员认证

  • 独立的 ADMIN_JWT_SECRET 密钥(与用户 JWT 分离)
  • 所有 /admin/* 接口必须携带管理员 Token
  • 维护模式验证使用 JWT Token,不接受伪造的请求头

密码安全

规则 说明
最小长度 8 位(可通过环境变量配置)
大小写字母 必须包含
数字 必须包含
特殊字符 可选(默认关闭)
加密方式 bcryptjs,10 轮盐加密

敏感操作二次验证

以下操作需要通过邮件验证码确认:

  • 修改密码
  • 修改邮箱
  • 注销账户
  • 管理员操作(如自毁模式)

文件上传安全

限制
文件类型 image/jpeg, image/png, image/gif, image/webp
单文件大小 32 MB
单次最多文件 32 张
存储路径 public/images/
头像大小 最大 2 MB

通知 (Notification)

/**
 * @typedef {Object} Notification
 * @property {string} id - UUID
 * @property {string} [userId] - 接收者ID(广播通知为 null)
 * @property {'like'|'comment'|'comment_reply'|'follow'|'system'} type - 通知类型
 * @property {string} [systemType] - 系统通知子类型:'new_device'(新设备登录提醒)、
 *   'broadcast'(管理员群发广播消息)
 * @property {'user'|'all'} [target] - 通知范围:'user' 单用户 / 'all' 全体广播
 * @property {string} [title] - 标题(广播消息/新设备提醒)
 * @property {string} [message] - 正文(system 通知使用;like/comment 等互动通知
 *   用 content 字段)
 * @property {string} [postId] - 帖子ID
 * @property {string} [commentId] - 评论ID
 * @property {string} [fromUserId] - 发送者ID
 * @property {string} [fromUsername] - 发送者用户名
 * @property {string} [content] - 内容
 * @property {string} timestamp - 时间
 * @property {boolean} read - 是否已读(广播通知在响应层按 readBy 计算)
 * @property {string[]} [readBy] - 广播通知已读用户ID列表
 */

说明:系统/广播类通知(system)不使用 postTitle/fromUsername 交互字段,客户端应展示 title + message

收藏 (Favorite)

/**
 * @typedef {Object} Favorite
 * @property {string} id - UUID
 * @property {string} userId - 用户ID
 * @property {string} postId - 帖子ID
 * @property {string} [tagId] - 标签ID
 * @property {string} createdAt - 收藏时间
 */

收藏标签 (FavoriteTag)

/**
 * @typedef {Object} FavoriteTag
 * @property {string} _id - MongoDB ID
 * @property {string} userId - 用户ID
 * @property {string} name - 标签名称
 * @property {string} color - 标签颜色
 * @property {number} order - 排序
 */

关注 (Follow)

/**
 * @typedef {Object} Follow
 * @property {string} id - UUID
 * @property {string} followerId - 关注者ID
 * @property {string} followingId - 被关注者ID
 * @property {string} createdAt - 关注时间
 */

会话 (Conversation)

/**
 * @typedef {Object} Conversation
 * @property {string} id - 会话ID
 * @property {string[]} participants - 参与者ID数组
 * @property {Object} lastMessage - 最后一条消息
 * @property {string} lastMessage.content - 消息内容
 * @property {string} lastMessage.senderId - 发送者ID
 * @property {string} lastMessage.createdAt - 发送时间
 * @property {string} [canInitiateFrom] - 可发起消息的用户ID(非互关时)
 * @property {string} createdAt - 创建时间
 * @property {string} updatedAt - 更新时间
 * @property {boolean} lastMessageRead - 最后消息是否已读
 */

私信 (Message)

/**
 * @typedef {Object} Message
 * @property {string} id - UUID
 * @property {string} conversationId - 会话ID
 * @property {string} senderId - 发送者ID
 * @property {string} receiverId - 接收者ID
 * @property {string} content - 消息内容
 * @property {string} type - 消息类型:text
 * @property {boolean} read - 是否已读
 * @property {string[]} deletedBy - 删除此消息的用户ID列表
 * @property {string} createdAt - 创建时间
 */

黑名单 (Blacklist)

/**
 * @typedef {Object} Blacklist
 * @property {string} id - UUID
 * @property {string} blockerId - 拉黑者ID
 * @property {string} blockedId - 被拉黑者ID
 * @property {string} createdAt - 拉黑时间
 */

举报 (Report)

/**
 * @typedef {Object} Report
 * @property {string} id - UUID
 * @property {string} reporterId - 举报人ID
 * @property {'post'|'comment'} targetType - 目标类型
 * @property {string} targetId - 目标ID
 * @property {string} targetUserId - 被举报用户ID
 * @property {string} reason - 举报原因
 * @property {string} reasonText - 举报原因文本
 * @property {string} [description] - 详细描述
 * @property {'pending'|'processed'|'rejected'} status - 状态
 * @property {string} createdAt - 创建时间
 */

公告 (Announcement)

/**
 * @typedef {Object} Announcement
 * @property {string} id - UUID
 * @property {string} title - 公告标题
 * @property {string} content - 公告内容(支持 Markdown)
 * @property {'info'|'warning'|'error'} type - 公告类型
 * @property {boolean} pinned - 是否置顶
 * @property {boolean} active - 是否启用
 * @property {string} createdAt - 创建时间
 * @property {string} updatedAt - 更新时间
 */

栏目 (Category)

/**
 * @typedef {Object} Category
 * @property {string} id - UUID
 * @property {string} name - 栏目名称
 * @property {string} description - 栏目描述
 * @property {string} icon - 图标(FontAwesome 类名)
 * @property {string} color - 颜色(十六进制)
 * @property {number} order - 排序权重
 * @property {boolean} isActive - 是否启用
 * @property {number} postCount - 帖子数量
 * @property {string} createdBy - 创建者ID
 * @property {string} createdAt - 创建时间
 */

栏目申请 (CategoryApplication)

/**
 * @typedef {Object} CategoryApplication
 * @property {string} id - UUID
 * @property {string} categoryName - 申请栏目名称
 * @property {string} description - 申请理由
 * @property {string} applicantId - 申请人ID
 * @property {string} applicantUsername - 申请人用户名
 * @property {'pending'|'approved'|'rejected'} status - 申请状态
 * @property {string} reviewedBy - 审核者ID
 * @property {string} reviewedAt - 审核时间
 * @property {string} reviewNote - 审核备注
 * @property {string} createdAt - 创建时间
 */

安全说明

安全措施概览

本系统已实施多层安全防护措施,包括但不限于:

安全措施 说明
Helmet 安全头 设置 Content-Security-Policy、X-Frame-Options、HSTS 等安全头
CORS 白名单 仅允许配置的域名进行跨域请求
XSS 过滤 自动过滤请求体中的 XSS 攻击代码
SQL/NoSQL 注入防护 检测并阻止潜在的注入攻击,使用 mongo-sanitize
HPP 防护 防止 HTTP 参数污染攻击
Rate Limiting 基于 Redis 的滑动窗口限流机制
JWT 认证 基于 Token 的身份认证,支持令牌刷新和注销
登录锁定 多次登录失败后锁定账户
请求 ID 追踪 每个请求分配唯一 ID,便于安全审计
图形验证码 登录/注册/管理员登录人机验证,SVG 零依赖生成,Redis+内存双存储

JWT 认证

认证流程

  1. 用户登录成功后,服务器返回 token(访问令牌)和 refreshToken(刷新令牌)
  2. 客户端在后续请求中通过 Authorization: Bearer <token> 头携带令牌
  3. 访问令牌默认有效期 7 天,刷新令牌有效期 30 天
  4. 访问令牌过期后,使用刷新令牌获取新的访问令牌

刷新令牌

POST /api/refresh-token

请求参数

参数 类型 必填 说明
refreshToken string 刷新令牌

响应示例

{
  "success": true,
  "data": {
    "token": "新的访问令牌"
  }
}

登出

POST /api/logout

请求参数

参数 类型 必填 说明
token string 当前访问令牌

管理员认证

管理员在登录时会额外获得 adminToken,用于管理员操作的认证。

管理员认证支持两种方式:

  1. JWT Token(推荐):通过 Authorization: Bearer <adminToken> 头携带
  2. 传统方式(过渡期兼容):通过请求体或查询参数传递 adminId

登录安全

  • 连续 5 次登录失败后,账户将被锁定 30 分钟
  • 锁定期间任何登录尝试都会被拒绝
  • 成功登录后,失败计数自动清零

安全配置

安全配置通过环境变量管理,请参考 .env.example 文件:

环境变量 说明 默认值
JWT_SECRET JWT 密钥 -
JWT_EXPIRES_IN 访问令牌有效期 7d
CORS_ORIGIN 允许的跨域来源(逗号分隔) localhost:2080
LOGIN_MAX_ATTEMPTS 最大登录尝试次数 5
LOGIN_LOCK_TIME 锁定时间(毫秒) 1800000
MAX_REQUEST_SIZE 请求体最大大小(MB) 10

响应头说明

每个 API 响应都会包含以下安全相关头:

响应头 说明
X-Request-ID 请求唯一标识,用于追踪和审计
X-Content-Type-Options 设置为 nosniff,防止 MIME 类型嗅探
X-Frame-Options 设置为 DENY,防止点击劫持
Strict-Transport-Security HSTS 配置,强制使用 HTTPS
Content-Security-Policy 内容安全策略

最佳实践

  1. 生产环境必须

    • 设置强随机的 JWT_SECRETADMIN_JWT_SECRET
    • 配置正确的 CORS_ORIGIN 白名单
    • 启用 HTTPS 并配置 HSTS
  2. 客户端建议

    • 安全存储令牌(避免 localStorage,推荐 httpOnly Cookie)
    • 实现令牌自动刷新机制
    • 处理 401 响应时清除本地令牌并跳转登录
  3. 敏感操作

    • 修改密码、邮箱等敏感操作需要验证码二次验证
    • 管理员操作需要独立的管理员令牌认证