- Base URL:
http://<host>:3030 - REST 前缀:
/api - 返回格式统一:
{
"code": 0,
"message": "ok",
"data": {}
}code = 0 表示成功,code = 1 表示失败。
除登录与健康检查外,所有 REST 接口都需要在请求头携带:
Authorization: Bearer <JWT>permission = 1: 普通用户permission = 2: 管理员
白名单管理接口仅管理员可访问。
- Method:
GET - Path:
/api/health - Auth: 否
响应示例:
{
"code": 0,
"message": "ok",
"data": {
"service": "xbt2-server"
}
}- Method:
POST - Path:
/api/auth/login - Auth: 否
请求体:
{
"mobile": "13800000000",
"password": "your_password"
}响应体:
{
"code": 0,
"message": "ok",
"data": {
"token": "<jwt>",
"user": {
"uid": 123456,
"name": "张三",
"mobile": "138****0000",
"avatar": "https://...",
"permission": 2
}
}
}说明:
- 若白名单为空,首次登录用户会自动成为管理员(
permission=2)。 - 若账号不在白名单,会返回未授权。
- Method:
GET - Path:
/api/courses - Auth: 是
响应体 data:
[
{
"class_id": 111,
"course_id": 222,
"name": "高等数学",
"teacher": "李老师",
"icon": "https://...",
"is_selected": true
}
]- Method:
POST - Path:
/api/courses/sync - Auth: 是
请求体:无
响应体 data:
{
"count": 12
}- Method:
PUT - Path:
/api/courses/selection - Auth: 是
请求体:
{
"course_ids": [222, 333]
}响应体 data:
{
"selected_count": 2
}说明:
- 当前实现按
course_id更新选中状态(与前端当前实现一致)。
- Method:
GET - Path:
/api/sign/activities - Auth: 是
响应体 data:
[
{
"course_id": 222,
"class_id": 111,
"course_name": "高等数学",
"course_teacher": "李老师",
"icon": "https://...",
"has_more": false,
"activities": [
{
"active_id": 987654,
"activity_name": "课堂签到",
"start_time": 1760000000000,
"end_time": 1760003600000,
"sign_type": 2,
"if_refresh_ewm": false,
"if_photo": false,
"record_source": 0,
"record_source_name": "",
"record_sign_time": 1760000500000,
"course_name": "高等数学",
"course_id": 222,
"class_id": 111,
"course_teacher": "李老师"
}
]
}
]record_source 含义:
0: 尚未签到。-1: 该同学已在学习通自行签到。= 当前用户 uid: 该同学由当前用户代签(本人签到时也是该值)。>0 且 != 当前用户 uid: 该同学已被其他用户代签。
record_source_name 含义:
""(空字符串): 尚未签到。"学习通": 该同学已在学习通自行签到。- 其他字符串(例如
"张三"): 表示该同学被该用户代签。
sign_type 含义:
0普通签到2二维码签到3手势签到4位置签到5签到码签到
说明:当 sign_type=0 且 if_photo=true 时,该活动为拍照签到,应调用 /api/sign/photo。
说明:
- 每门课程默认最多返回最新
5条签到活动(可通过后端Server/config.yaml中的activity_list_limit配置)。 - 当该课程活动总数超过返回条数时,
has_more=true。
- Method:
GET - Path:
/api/sign/classmates - Auth: 是
- Query:
course_id(required)class_id(required)
示例:
GET /api/sign/classmates?course_id=222&class_id=111响应体 data:
[
{
"uid": 10001,
"name": "王五",
"mobile_masked": "139****0000",
"avatar": "https://..."
}
]- Method:
POST - Path:
/api/sign/check - Auth: 是
请求体:
{
"activity_id": 987654,
"user_ids": [10001, 10002]
}说明:
- 后端会自动把当前登录用户加入查询列表。
- 前端可据此过滤出未签用户,再自行并发调用执行签到接口。
响应体 data:
{
"items": [
{
"user_id": 343479151,
"signed": true,
"record_source": 343479151,
"record_source_name": "张三",
"message": "该同学已本人签到"
},
{
"user_id": 10001,
"signed": false,
"record_source": 0,
"record_source_name": "",
"message": "未签到"
}
]
}- Method:
POST - Path:
/api/sign/execute - Auth: 是
请求体:
{
"activity_id": 987654,
"target_uid": 10001,
"sign_type": 2,
"course_id": 222,
"class_id": 111,
"if_refresh_ewm": false,
"special_params": {
"enc": "xxxx",
"location": {
"result": 1,
"address": "成都市郫都区xxx",
"latitude": 30.7501,
"longitude": 103.9272
}
}
}兼容说明:
- 若未传
target_uid,但传了user_ids,后端会使用user_ids的第一个 uid。 - 若都未传,默认签当前登录用户。
响应体 data:
{
"user_id": 10001,
"success": true,
"already_signed": false,
"record_source": 343479151,
"record_source_name": "张三",
"message": "签到成功"
}special_params 按签到类型:
- 普通签到(
0): 可空 - 二维码签到(
2):enc(required)c(optional, 预签到场景可用)location(optional, 二维码附加位置变种;可传对象/数组或 JSON 字符串,后端会透传给学习通)latitude+longitude+description(optional, 兼容写法;后端会自动组装为location后透传)
- 手势签到(
3):sign_code(required)
- 位置签到(
4):latitude(required)longitude(required)description(required)
- 签到码签到(
5):sign_code(required)
- Method:
POST - Path:
/api/sign/photo - Auth: 是
- Content-Type:
multipart/form-data
请求字段:
activity_id(required): 签到活动 IDcourse_id(required): 课程 IDclass_id(required): 班级 IDtarget_uid(optional): 代签目标用户;不传则默认当前登录用户if_refresh_ewm(optional): 与活动详情中的if_refresh_ewm一致file(optional): 照片文件,字段名固定为file,最大 20MBobject_id(optional): 已上传到超星云盘的图片objectId;传了object_id时可以不传file
说明:
- 后端会使用目标用户的学习通登录凭据上传照片到超星云盘,获取
objectId后再提交拍照签到。 - 如果你已经有图片
objectId,可以直接传object_id,后端会跳过上传步骤。 - 响应体与
/api/sign/execute一致。
示例:
curl -X POST http://localhost:3030/api/sign/photo \
-H "Authorization: Bearer <JWT>" \
-F "activity_id=987654" \
-F "course_id=222" \
-F "class_id=111" \
-F "target_uid=10001" \
-F "file=@photo.jpg"该组接口已重构为 RESTful 资源风格,仅管理普通用户白名单(permission 固定为 1)。
- Method:
GET - Path:
/api/admin/whitelist/users - Auth: 是(管理员)
响应体 data:
[
{
"id": 12,
"uid": 343479453,
"mobile_masked": "139****0000",
"permission": 1
}
]- Method:
POST - Path:
/api/admin/whitelist/users - Auth: 是(管理员)
请求体:
{
"mobile": "13900000000"
}响应体 data:
{
"id": 12,
"uid": 343479453,
"mobile_masked": "139****0000",
"permission": 1
}说明:
- 该接口不再接受
permission参数。 - 管理员账号不会被该接口修改。
- Method:
POST - Path:
/api/admin/whitelist/users/import - Auth: 是(管理员)
请求体:
{
"mobiles": "13900000001\n13900000002,13900000003"
}响应体 data:
{
"count": 3,
"skipped_admin": 0
}说明:
- 支持换行、逗号、空格混合文本。
- 自动提取手机号并去重。
- 若手机号是管理员白名单,会被跳过并计入
skipped_admin。
- Method:
DELETE - Path:
/api/admin/whitelist/users/:id - Auth: 是(管理员)
示例:
DELETE /api/admin/whitelist/users/12响应体 data:
{
"id": 12,
"uid": 0,
"mobile_masked": "139****0000"
}说明:
- 管理员账号不允许删除。
统一结构:
{
"code": 1,
"message": "error message",
"data": null
}常见 HTTP 状态码:
400参数错误401未登录或 token 无效403权限不足404资源不存在500服务端错误
- 先调用
/api/auth/login获取 JWT。 - 带
Authorization: Bearer <JWT>调用/api/courses/sync。 - 调用
/api/courses+/api/courses/selection选定课程。 - 调用
/api/sign/activities拿活动。 - 调用
/api/sign/check查待签状态。 - 前端过滤出未签用户后,并发调用
/api/sign/execute。