Backend API design

Author: Zhenyu Yang yangzhenyu@sust.edu.cn, Sixu Wei reisa@sust.edu.cn

Last updated: Feb 4, 2026

The API documentation is written for the developer to read and does not include the machine-readable OpenAPI format.


0. 统一返回格式

除少数文件上传 API 外,所有 API 返回结构如下:

成功

{
  "code": 0,
  "message": "success",
  "data": { ... }
}

失败

{
  "code": 40001,
  "message": "User not found"
}

1. 认证与用户账号

1.1 登录

微信用户扫码绑定后登录

POST /api/v1/auth/login/wechat

Body

{
  "wxid": "xxxx"
}

邮箱/学号/工号 + 密码登录

POST /api/v1/auth/login/email

Body

{
  "email": "teacher@school.edu",    // 支持邮箱或 user_code
  "password": "your_password",
  "totp_code": "123456"             // 当账号启用 TOTP 时必填
}

已启用 TOTP 的账号在使用微信登录时,同样需要在请求体中携带 totp_code 字段。

1.1.1 双因素认证(TOTP)

为账号启用基于时间的一次性密码:

POST /api/v1/auth/totp/setup

返回值示例:

{
  "code": 0,
  "message": "success",
  "data": {
    "secret": "BASE32SECRET",
    "provisioning_uri": "otpauth://totp/Classroom%20Manager:tester%40school.edu?..."
  }
}

完成扫描后提交验证码以启用:

POST /api/v1/auth/totp/enable

Body

{
  "totp_code": "123456"
}

1.1.2 无密码登录(WebAuthn)

本系统支持使用安全密钥 / 平台设备进行 WebAuthn 认证,流程分为「注册密钥」和「登录」两步。所有接口均遵循统一 success/error 返回结构。

注册安全密钥(需登录后操作)
  1. 请求注册参数(传递可选 device_name 便于备注设备):

POST /api/v1/auth/webauthn/options/register

返回值包含浏览器 navigator.credentials.create 所需的 publicKey 参数。

  1. 提交浏览器生成的凭证:

POST /api/v1/auth/webauthn/register

Body 示例:

{
  "credential": { ... },
  "device_name": "MacBook Touch ID"
}
使用安全密钥登录(匿名可访问)
  1. 请求登录参数:

POST /api/v1/auth/webauthn/options/login

Body

{ "identifier": "teacher@school.edu" }
  1. 提交认证结果以换取 JWT:

POST /api/v1/auth/webauthn/login

Body

{ "credential": { ... } }

关闭 TOTP 认证:

POST /api/v1/auth/totp/disable

Body(如当前已启用,需带上验证码确认):

{
  "totp_code": "123456"
}

1.2 管理员(superadmin / secretary)邀请外方教师注册

POST /api/v1/users/invite-foreign

Body

{
  "email": "teacher@school.edu",
  "full_name": "John Smith"
}

1.3 邮箱注册(外方教师)

POST /api/v1/auth/register/email

Body

{
  "email": "teacher@school.edu",
  "password": "12345678",
  "full_name": "John Smith"
}

1.4 获取当前登录用户信息

GET /api/v1/auth/me

1.5 用户列表(管理员)

GET /api/v1/users

Query

  • role(可选)

  • status(可选)

  • search(可选,匹配姓名/学号/工号)

  • page, page_size(兼容 size)

1.6 创建用户(superadmin)

POST /api/v1/users

1.7 更新用户信息(superadmin / secretary)

PUT /api/v1/users/{id}

1.8 修改角色(superadmin)

PUT /api/v1/users/{id}/role

Body:

{ "role": "assistant" }

1.9 禁用/启用账户

PUT /api/v1/users/{id}/status

1.10 导入人员 Excel

POST /api/v1/users/import
Content-Type: multipart/form-data

字段:file

2. 教室管理 API

2.1 获取教室列表(所有人可见,但字段不同)

未登录用户

GET /api/v1/classrooms/public

返回字段(隐藏敏感信息):

  • building

  • room_number

  • status

  • 当前时间段是否占用(不含占用人信息)

登录用户(带设备信息)

GET /api/v1/classrooms

Query:

  • building

  • capacity_min

  • status

  • page, page_size(兼容 size)

2.2 教室详情

(登录用户)

GET /api/v1/classrooms/{id}

2.3 创建教室(superadmin)

POST /api/v1/classrooms

2.4 更新教室信息(superadmin / assistant)

PUT /api/v1/classrooms/{id}

2.5 删除教室(superadmin)

DELETE /api/v1/classrooms/{id}

2.6 导入教室 Excel(superadmin)

POST /api/v1/classrooms/import
Content-Type: multipart/form-data

2.7 教室课表

获取某一教室在指定日期范围内的占用时间段。

课表由 已批准(approved) 的人工借用记录(source_type=manual)与课程表(CourseSession,通常由课程导入生成)共同生成,并按照时间顺序返回一组时间块。空闲时段也会以 occupied=false 的块返回,便于前端直接绘制时间轴。

GET /api/v1/classrooms/{id}/schedule

Query:

  • start_date(yyyy-mm-dd)

  • end_date(yyyy-mm-dd)

Response

{
  "code": 0,
  "message": "success",
  "data": [
    {
      "start_time": "2025-04-12T00:00:00+08:00",
      "end_time": "2025-04-12T08:00:00+08:00",
      "occupied": false,
      "reason": null
    },
    {
      "start_time": "2025-04-12T08:00:00+08:00",
      "end_time": "2025-04-12T09:40:00+08:00",
      "occupied": true,
      "reason": "Calculus"
    }
  ]
}

未登录用户也可访问此接口,reason 字段在未登录时返回 null

2.7.1 班牌课程(公开)

班牌端获取某教室在日期范围内的课程安排(公开访问,返回课程详情)。

GET /api/v1/signage/classrooms/{id}/schedule

Query:

  • start_date(yyyy-mm-dd)

  • end_date(yyyy-mm-dd)

Response

{
  "code": 0,
  "message": "success",
  "data": {
    "items": [
      {
        "start_time": "2026-02-04T08:00:00+08:00",
        "end_time": "2026-02-04T09:40:00+08:00",
        "course_id": 12,
        "course_name": "Calculus",
        "course_code": "MATH101",
        "teacher_name": "张三",
        "class_name": "计科2024",
        "sections": [1, 2],
        "source": "course"
      }
    ]
  }
}

班牌端可拉取教室配置:

GET /api/v1/signage/classrooms/{id}

返回字段包含 signage_enabledsignage_titlesignage_subtitlesignage_config, 来自班牌设备表记录。

2.8 课程 API

2.8.1 课程列表(登录可用)

GET /api/v1/courses

Query(可选):

  • teacher_id:教师查询自己的课程

  • class_name:学生按班级查询课程

返回:课程基础信息 + sessions(包含 weekdaysectionsweek_list 等字段)。

说明:

  • sessions.weekday 为字符串枚举:MONDAYTUESDAYWEDNESDAYTHURSDAYFRIDAYSATURDAYSUNDAY

  • Web / 小程序等 API 调用方应基于该枚举值做判断,不应依赖后端内部数据库的整数编码

  • 不提供旧 0-6 整数 weekday 协议兼容

2.8.2 导入课程表 Excel(superadmin / secretary)

POST /api/v1/courses/import
Content-Type: multipart/form-data

字段:

  • file:Excel 文件(.xlsx/.xls

  • semester_start_date:学期开始日期(YYYY-MM-DD

说明:

  • 权限:仅 superadmin / secretary

  • 文件格式:.xlsx / .xls,最大 50MB

  • Excel 中 星期 列沿用教务系统导入格式:1-71=周一7=周日),也支持 周一 / 星期一 等中文写法

  • 导入模板必须包含 课程序号 列,缺失会直接报错

  • 错误列表最多返回 20 条,超过时 error_report_available = true

返回(示例):

{
  "code": 0,
  "message": "导入完成:成功 85 条,跳过 10 条",
  "data": {
    "total_rows": 100,
    "processed": 85,
    "skipped": 10,
    "created": 50,
    "updated": 35,
    "errors": [],
    "error_report_available": false
  }
}

3. 教室借用流程 API

3.1 发起借用申请(3 类用户权限不同)

所有登录用户均可申请:

student → counselor

chinese_teacher → assistant

foreign_teacher → assistant

POST /api/v1/borrow

Body

{
  "classroom_id": 121,
  "start_time": "2025-04-12T09:00:00",
  "end_time": "2025-04-12T11:00:00",
  "reason": "Lecture",
  "expected_students": 30,
  "activity_type": "student_activity" | "teaching_activity",
  "is_urgent": true | false,
  "source_type": "manual" | "course",
  "auto_generated": true | false,
  "applicant_id": 12,
  "applicant_code": "2024123456"
}

说明:

  • applicant_id / applicant_codesuperadmin/secretary/assistant 可用,用于代用户提交申请;普通用户忽略该字段。

  • source_type / auto_generated 为后端字段,手动申请时由后端固定为 manual/false

业务规则:

  • 时间限制(与 is_urgent、申请人角色有关):

    • assistant / superadmin:不受时间限制

    • student:仅允许申请未来 +1 到 +7 天

    • chinese_teacher / foreign_teacher / counselor / secretary:仅允许申请未来 至少 +1 天

    • is_urgent=true(加急):只能选择 当天

  • 时间冲突:

    • 与已有手动借用冲突:time conflict with existing borrow application

    • 与课程表冲突:time conflict with course schedule

3.2 借用申请列表(按角色查看)

GET /api/v1/borrow

Query:

  • status

  • reviewer

  • applicant

  • mine(可选,true/1 时只看自己的申请)

  • source_type(可选,manual/course

  • review_level(可选,first_review/second_review

  • activity_type(可选,student_activity/teaching_activity

  • start_date / end_date

  • page, page_size(兼容 size)

3.3 申请详情

GET /api/v1/borrow/{id}

3.4 审核申请(assistant / counselor / superadmin)

PUT /api/v1/borrow/{id}/review

Body

{
  "action": "approve" | "reject" | "forward",
  "remark": "理由",
  "forward_to": "superadmin"
}

forward_to: only when action=forward

系统自动触发:

  • 微信推送(中方教师/学生)

  • 邮件推送(外方教师) 说明:

  • 当申请处于 first_reviewaction=approve 会将申请流转到 second_review(状态仍为 pending)。

  • 当申请处于 second_reviewaction=approve 才会最终 approved

  • assistant / superadmin 允许自审自己的申请,其余角色禁止自审。

3.5 申请者主动取消(诚信扣分)

PUT /api/v1/borrow/{id}/cancel

自动检查:

  • 是否超过课程开始 6 小时

  • 若违反 → 扣信用分

  • 信用分低于 3 → 禁止借用(分数记录接口在用户模块)

3.6 获取同时段空闲教室(紧急换教室用)

GET /api/v1/classrooms/free

Query:

  • start_time

  • end_time

3.7 课程借用改期(申请人或管理员)

仅支持对 source_type=coursestatus=approved 的记录改期:原记录会被取消(cancelled),同时创建一条新的 course 记录并直接 approved

POST /api/v1/borrow/{id}/reschedule

Body:

{
  "classroom_id": 121,
  "start_time": "2026-01-20T08:00:00+08:00",
  "end_time": "2026-01-20T09:50:00+08:00"
}

权限:申请人本人,或 superadmin/secretary/assistant

4. 报修流程 API

4.1 普通报修(所有登录用户)

POST /api/v1/repairs

Body:

{
  "classroom_id": 121,
  "description": "投影仪无法开机",
  "images": ["url1", "url2"]
}

限制:

  • 同一教室存在未完成工单 → 禁止重复提交

4.2 紧急报修

POST /api/v1/repairs/emergency

返回内容:

  • 当日值班 assistant 的电话号码

  • 同时创建工单

4.3 工单列表(assistant/superadmin 查看全部)

GET /api/v1/repairs

权限:

  • assistant / superadmin / counselor:查看全部

  • 其他角色:仅查看自己创建的工单

4.4 更新工单(assistant/superadmin/counselor)

PUT /api/v1/repairs/{id}

Body:

{
  "status": "processing" | "completed",
  "remark": "处理说明"
}

4.5 报修人确认维修完成

PUT /api/v1/repairs/{id}/confirm

5. 举报滥用 API

5.1 提交举报(所有登录用户)

POST /api/v1/abuse

Body:

{
  "classroom_id": 121,
  "description": "发现教室被外人占用",
  "images": []
}

5.2 管理员查看举报(superadmin)

GET /api/v1/abuse

5.3 举报处理

PUT /api/v1/abuse/{id}

6. 公共工具 API

6.1 附件上传

POST /api/v1/upload
Content-Type: multipart/form-data

返回:

{
  "code": 0,
  "message": "success",
  "data": { "url": "/media/uploads/<uuid>.<ext>" }
}

说明:

  • 支持两种后端:本地磁盘与 S3 兼容对象存储,可通过环境变量 FILE_STORAGE_BACKENDlocal/s3)切换。

  • 本地模式默认将文件保存到服务器的 /var/www/classroom-manager/uploads/,可用 LOCAL_UPLOAD_DIRLOCAL_UPLOAD_URL_PREFIX 覆盖存储路径与访问前缀。

  • S3 模式需要提供 S3_BUCKET_NAMES3_ACCESS_KEY_IDS3_SECRET_ACCESS_KEY,可选 S3_ENDPOINT_URLS3_BASE_URL 用于自定义访问域名(适配任意 S3 兼容服务)。

  • 需要登录(Authorization: Bearer <token>)。

  • 当前实现为本地磁盘上传,返回值为站内可访问的相对路径。

6.2 获取系统配置

GET /api/v1/config

返回:

{
  "code": 0,
  "message": "success",
  "data": {
    "season": "winter",
    "season_display": "冬季作息",
    "updated_at": "2026-01-20T10:00:00+08:00"
  }
}

6.3 设置季节作息(仅 superadmin)

POST /api/v1/config/season

Body:

{ "season": "winter" }

7. 常用数据结构(模型)

7.1 User

基于原始需求文档直接映射:

{
  "id": 1,
  "user_code": "2024123456",
  "username": "2024123456",
  "full_name": "张三",
  "email": "123@school.edu",
  "wxid": "wx_xxxxx",
  "role": "student",
  "roles": ["student"],
  "current_role": "student",
  "status": "active",
  "student_class": "计uc24x班",
  "date_joined": "2025-01-01T10:00:00",
  "last_login": "2025-03-10T09:00:00",
  "is_totp_enabled": false
}

7.2 Classroom

来自数据库模板

{
  "id": 88,
  "building": "A楼",
  "room_number": "A101",
  "capacity": 60,
  "equipment": {
    "air_conditioner": true,
    "projector": true
  },
  "status": "available"
}

7.3 Borrow Application

{
  "id": 103,
  "applicant_id": 12,
  "reviewer_role": "assistant",
  "classroom_id": 88,
  "start_time": "...",
  "end_time": "...",
  "reason": "lecture",
  "status": "pending"
}

约束说明:

  • start_time 必须晚于当前时间至少 6 小时,且 end_time 必须晚于 start_time

  • 同一教室在相交时间段内只能存在待审核或已批准的唯一申请,提交时会校验时间冲突。

  • 申请时间限制与申请人角色、is_urgent 有关(详见 3.1)。

  • 冲突校验同时检查:

    • 已有手动借用(pending/approvedsource_type=manual

    • 课程表(CourseSession

8. 操作审计日志

仅超级管理员、秘书、助理(或 Django 后台管理员)可以访问日志接口,所有数据均由后端记录,前端传参仅用于过滤。日志以追加写入的形式保存。

8.1 获取日志列表

GET /api/v1/logs/

查询参数:

  • start_date / end_date:时间范围,支持日期(2025-12-14)或 ISO 时间字符串。

  • user:用户 ID。

  • role:用户角色标识,如 superadminassistant

  • module:业务模块名称。

  • action:动作名称。

返回值使用默认分页结构,每条记录包含用户、角色、模块、动作、目标类型/ID、请求 IP、UA、附加信息以及创建时间等字段。

8.2 导出 CSV

GET /api/v1/logs/export/

支持与列表相同的过滤参数,返回 text/csv 文件,字段顺序与列表保持一致,便于审计归档。

9. 课堂签到 API

9.1 开启课程签到(教师开关)

POST /api/v1/checkin-configs/enable

Body:

{
  "course_id": 12,
  "start_date": "2026-02-01",
  "end_date": "2026-06-30"
}

说明:

  • 任课教师或 superadmin/secretary/assistant 可调用

  • 未传 start_date/end_date 时,默认按课程起止日期生成

  • 只写入 CheckinSession.sections(节次),不落地具体时间

响应示例:

{
  "code": 0,
  "message": "checkin enabled",
  "data": {
    "course_id": 12,
    "enabled": true,
    "start_date": "2026-02-01",
    "end_date": "2026-06-30",
    "created": 32,
    "skipped": 0
  }
}

9.2 关闭课程签到

POST /api/v1/checkin-configs/disable

Body:

{
  "course_id": 12
}

9.3 签到场次列表

GET /api/v1/checkin-sessions

Query(可选):

  • course_id

  • classroom_id

  • status

  • date(单日)

  • start_date / end_date

9.4 签到记录列表

GET /api/v1/checkin-records

Query(可选):

  • session_id

  • student_id

  • status

9.5 教师手动签到

POST /api/v1/checkin-records/manual

Body:

{
  "session_id": 101,
  "student_id": 2001,
  "status": "present",
  "remark": "" 
}

说明:

  • 仅任课教师或 superadmin/secretary/assistant 可调用

  • status 支持 present/late/absent/leave