本文介绍远程监考服务端集成示例源码说明。
前提条件
通过源码构建及部署
根据源码的说明文档配置并执行,源码地址参见源码。
项目部署与体验
关于技术选型
基于主流的Java8 + Springboot2搭建框架
基于Mybatis plus(https://baomidou.com/)作为Repository层选型,存储可以使用MySQL
基于SpringSecurity + JWT 来实现权限控制
关于部署
理论上只要安装了Java8即可运行在各个ECS或容器上。也可以考虑使用Serverless平台来快速部署,但使用前提是要对函数计算比较清楚。
配置文件
server:
port: 8080
# mysql
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://******:3306/*****?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: "******"
password: "******"
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd'T'HH:mm:ss+08:00
default-property-inclusion: non_null
#mybatis
mybatis-plus:
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.aliyuncs.aui.entity
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
default-statement-timeout: 10
# pop配置
biz:
openapi:
access:
key: "*********"
secret: "*********"
# 融云相关配置,可不配置,需要高可用性考虑
live_rongcloud_im:
app_key: "******"
app_secret: "********"
# 旧IM相关配置,新接入的客户不用配置
live_im:
app_id: TY3****
# 新IM相关配置
new_im:
appId: "********"
appKey: "********"
appSign: "********"
# 配置推拉流信息
live_stream:
push_url: push.*****.vip
pull_url: pull.*****.vip
push_auth_key: zJl4******
pull_auth_key: mDZs********
app_name: live
auth_expires: 604800
# 配置连麦信息,需要再配置
live_mic:
app_id: 7c61********
app_key: c461b*********
# ram及upload用于考生端本地录制功能上传视频的功能,需要使用就配置
ram:
access_key_id: LTAI5*********
access_key_secret: NLgHO********
upload:
role_arn: acs:ram::*******
region: cn-shanghai
bucket: *****
base_path: /***
http:
cors:
host: "*"
参数说明
参数配置信息 | 说明 |
| 调用阿里云IM及Live相关服务API时,您需要使用AccessKey完成身份验证。AccessKey包括AccessKey ID和AccessKey Secret。具体如下:
更多信息,请参见创建AccessKey。 |
| |
| 旧版IM相关配置,新接入的客户不需要进行配置 |
| 直播动消息应用ID,请参见控制台开通与配置中的创建直播互动消息应用获取。 |
|
|
| 连麦功能如果需要可进行配置,连麦应用ID和AppKey,请参见创建互动直播应用。 |
| 请参阅使用STS临时访问凭证访问OSS或使用STS临时授权方案上传视频文档创建RAM用户、角色,配置相关参数。
|
| Web端使用该服务时请正确设置CORS,以允许跨域请求。 |
数据库配置
当配置好DB后,手工创建数据库表。以MySQL为例,建表语句为:
CREATE TABLE `cheat_config` (
`id` bigint NOT NULL AUTO_INCREMENT,
`exam_id` varchar(256) NOT NULL COMMENT '考试ID',
`data` text NOT NULL COMMENT '具体配置,JSON串',
`creator` varchar(256) DEFAULT NULL COMMENT '创建者',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` datetime NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `exam_id` (`exam_id`),
KEY `idx_create_at` (`created_at`)
) ENGINE=InnoDB;
CREATE TABLE `cheat_record` (
`id` bigint NOT NULL AUTO_INCREMENT,
`exam_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '考试ID',
`room_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '考场id',
`user_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '考生id',
`detect_type` varchar(256) NOT NULL COMMENT '检测类型',
`detect_time` datetime NOT NULL COMMENT '检测时间',
`is_main_monitor` varchar(256) NOT NULL COMMENT '考生主机位/副机位',
`extra_info` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '额外信息存储',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` datetime NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `idx_exam_id` (`exam_id`),
KEY `idx_create_at` (`created_at`),
KEY `idx_room_id` (`room_id`) USING BTREE
) ENGINE=InnoDB;
CREATE TABLE `exam_room_infos` (
`id` varchar(256) NOT NULL,
`name` varchar(256) DEFAULT NULL,
`exam_id` varchar(256) DEFAULT NULL,
`status` bigint DEFAULT NULL,
`audio_status` bigint DEFAULT NULL,
`im_group_id` varchar(256) DEFAULT NULL,
`create_teacher` varchar(256) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_create_at` (`created_at`)
) ENGINE=InnoDB
CREATE TABLE `examinations` (
`id` varchar(256) NOT NULL,
`name` varchar(256) DEFAULT NULL,
`start_time` datetime DEFAULT NULL,
`end_time` datetime DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_create_at` (`created_at`)
) ENGINE=InnoDB
参数说明
cheat_config表: 存储作弊配置
cheat_record表:记录考生的作弊信息
exam_room_infos表:存储考场信息
examinations表:存储考试信息
具体字段可参考代码对应的各个实体:
com.aliyuncs.aui.entity.CheatConfigEntity
com.aliyuncs.aui.entity.CheatRecordEntity
com.aliyuncs.aui.entity.RoomInfoEntity
com.aliyuncs.aui.entity.ExamEntity
编译运行
详细信息请参见源码中的README文件。
## 构建执行文件
```bash
go build -o main ./cmd/main.go
```
## 本地运行
```bash
export ADMIN_PASSWORD=your-secret
./main
```
所有 API 及对应文档:
浏览器打开:http://localhost:7001/swagger/index.html
用户名为 admin,密码为您自行设置
## 生成`swagger`文档
```bash
# 安装swagger工具 请具体参考:https://github.com/swaggo/swag
go install github.com/swaggo/swag/cmd/swag@latest
# 动态生成文档
swag init -g pkg/handler/*.go
```
接口
通用请求头参数
用于请求header上。
name | type | required | desc |
authorization | string | YES | 身份认证token,通过页面URL参数传入。 |
创建考场
请求协议:http/https
请求Path:/exam/v2/createRoom
是否需要授权:是
请求Method:POST
请求ContentType:JSON
请求参数
名称 | 类型 | 是否必填 | 示例值 | 描述 | |||
name | String | 是 | titlexxxx | 直播标题, 长度小于64 | |||
imServer | Json数组 | 是 | ["aliyun_old","aliyun_new"] | 配置使用哪些im服务。可多选
|
返回数据
名称 | 类型 | 示例值 | 描述 | ||||
code | Integer | 200 | 业务响应码。参考HTTP响应码 | ||||
id | String | ee487235-5f9b-4cb3-82ce-45c91c3xxxx | 考场ID。 | ||||
createdAt | String | 2023-02-27T17:34:00.592750182+08:00 | 创建时间。 | ||||
updatedAt | String | 2023-02-27T17:34:00.592750182+08:00 | 修改时间。 | ||||
name | String | titlexxx | 考场名称。 | ||||
examId | String | noticexxx | 考试ID。 | ||||
status | String | 考场状态。
| |||||
audioStatus | String | anchor_idxxx | 考场口播状态。
| ||||
imGroupId | String | anchor_nickxxx | 消息组ID。 | ||||
createTeacher | String | {"xx":"xxx"} | 考场创建者ID。 |
addRoom 时序图
示例
请求示例
{
"name": "考场名称-2024全国本科艺术考试第0001考场",
"imServer":["aliyun_new", "aliyun_old"]
}
正常返回示例
{
"success": true,
"data": {
"id": "f064a22d-9885-47ab-bce9-a058c74c****",
"createdAt": "2023-12-12T15:59:12+08:00",
"updatedAt": "2023-12-12T15:59:12+08:00",
"name": "考场名称-2024全国本科艺术考试第0001考场",
"examId": "d385e65a-8f3c-447e-9d57-01a18089****",
"status": 0,
"audioStatus": 0,
"imGroupId": "f064a22d-9885-47ab-bce9-a058c74c****",
"createTeacher": "teacher1"
}
}
获取考场信息
Path: /exam/roomInfo
Method:GET
Query:
name | type | required | desc |
roomId | string | YES | 考场ID,通过页面URL参数传入。 |
Body:
name | type | desc |
success | boolean | 是否成功。 |
errorCode | string | 异常码。 |
errorMsg | string | 异常信息。 |
data | object | 数据。 |
|─id | string | 考场ID。 |
|─name | string | 考场名称。 |
|─examId | string | 考试ID。 |
|─status | integer | 考场状态。
|
|─audioStatus | integer | 考场口播状态。
|
|─imGroupId | string | 消息组ID。 |
|─createTeacher | string | 考场创建者ID。 |
Response Demo:
{
"success": true,
"errorCode": "",
"errorMsg": "",
"data": {
"id": "roomId",
"name": "模拟考场",
"examId": "examId",
"status": 0,
"audioStatus": 0,
"imGroupId": "xxx",
"createTeacher": "teacherId"
}
}
获取考试信息
Path:/exam/examInfo
Method:GET
Query:
name | type | required | desc |
examId | string | YES | 考试ID,来源是考场信息返回。 |
Body:
name | type | desc |
success | boolean | 是否成功。 |
errorCode | string | 异常码。 |
errorMsg | string | 异常信息。 |
data | object | 数据。 |
|─id | string | 考试ID。 |
|─name | string | 考试名称。 |
|─startTime | string | 开始时间,使用YYYY/MM/DD HH:mm:ss格式化。 |
|─endTime | string | 结束时间。 |
|─radioInfo | array | 定时播放的音频。 |
|─ | object | |
|─id | string | ID。 |
|─classify | integer | 分类。 |
|─name | string | 名字。 |
|─startTime | string | 触发播放时间。 |
|─url | string | CDN地址。 |
|─ossUrl | string | OSS地址(非必需)。 |
|─audioInfo | array | 需要老师手动播放的音频。 |
|─ | object | |
|─id | string | ID。 |
|─classify | integer | 分类字典。 |
|─name | string | 音频名字。 |
|─url | string | 音频地址。 |
|─ossUrl | string | OSS地址(非必需)。 |
Response Demo:
{
"success": true,
"errorCode": "",
"errorMsg": "",
"data": {
"id": "123456",
"name": "exam",
"startTime": "2023/03/30 14:00:00",
"endTime": "2023/03/30 17:00:00",
"radioInfo": [
{
"id": "",
"classify": 0,
"name": "",
"startTime": "2023/03/30 14:30:00",
"url": ""
}
],
"audioInfo": [
{
"id": "",
"classify": 0,
"name": "",
"url": ""
}
]
}
}
获取用户信息
Path:/exam/userInfo
Method:GET
Query:
name | type | required | desc |
userId | string | YES | 用户ID,通过页面URL参数传入。 |
roomId | string | YES | 考场ID,通过页面URL参数传入。 |
Body:
name | type | desc |
success | boolean | 是否成功。 |
errorCode | string | 异常码。 |
errorMsg | string | 异常信息。 |
data | object | 数据。 |
|─id | string | 用户ID。 |
|─name | string | 昵称。 |
|─userStatus | int | 状态(目前没用上)。
|
|─rtcPushUrl | string | 推流地址。 |
|─rtcPullUrl | string | 大流地址。 |
|─rtsPullUrl | string | 小流地址(转码低清晰度的流)。 |
Response Demo:
{
"success": true,
"errorCode": "",
"errorMsg": "",
"data": {
"id": "userid",
"name": "username",
"userStatus": 0,
"rtcPushUrl": "artc://{yourPushDomain}/exam/xxx-xxx-xxx?grtn_twin_rtmp=on",
"rtcPullUrl": "artc://{yourBigPullDomain}/exam/xxx-xxx-xxx",
"rtsPullUrl": "artc://{yourSmallPullDomain}/exam/xxx-xxx-xxx_240p"
}
}
获取考场的学生列表
Path:/exam/userList
Method:GET
Query:
name | type | required | desc |
roomId | 考场id | YES | 考场ID,通过页面URL参数传入。 |
Body:
name | type | desc |
success | boolean | 是否成功。 |
errorCode | string | 异常码。 |
errorMsg | string | 异常信息。 |
data | array | 数据。 |
|─ | object | |
|─id | string | 用户ID。 |
|─name | string | 昵称。 |
|─userStatus | int | 状态(目前没有用上)。 |
|─rtcPushUrl | string | 推流地址。 |
|─rtcPullUrl | string | 大流地址。 |
|─rtsPullUrl | string | 小流地址。 |
Response Demo:
{
"success": true,
"errorCode": "",
"errorMsg": "",
"data": [
{
"id": "userid",
"name": "username",
"userStatus": 0,
"rtcPushUrl": "artc://{yourPushDomain}/exam/xxx-xxx-xxx?grtn_twin_rtmp=on",
"rtcPullUrl": "artc://{yourBigPullDomain}/exam/xxx-xxx-xxx",
"rtsPullUrl": "artc://{yourSmallPullDomain}/exam/xxx-xxx-xxx_240p"
}
]
}
获取IM建连的token
请参考建立长连接文档接入。
请求协议:http/https
请求Path:/exam/v2/getIMToken
是否需要授权:是
请求Method:POST
请求ContentType:JSON
请求参数
名称 | 类型 | 是否必填 | 示例值 | 描述 |
userId | String | 是 | useridxxxx | 用户UserId,用户自定义,在AppId下单独唯一。 |
deviceId | String | 是 | deviceidxxx | 终端设备ID,唯一代表一个用户终端设备,用户自定义。 说明 仅针对aliyun_old服务起作用。 |
deviceType | String | 是 | android | 终端设备类型,取值:
说明 仅针对aliyun_old服务起作用。 |
imServer | Json数组 | 是 | ["aliyun_old","aliyun_new"] | 配置使用哪些IM服务。可多选
|
role | String | 否 | admin | 角色,默认为空。当为admin时,表示该用户可以调用管控接口。 说明 仅针对aliyun_new服务起作用。 |
返回数据
名称 | 类型 | 示例值 | 描述 | |
code | Integer | 200 | 业务响应码。参考HTTP响应码。 | |
aliyunOldIm | accessToken | String | eyJlbmRwb2ludHMiOlsid3NzOi8vbWV0YXBhdGguYWxpeXVuY3MuY29tL3dzIl0sImFwcElkIjoiMzhXWUg3RlQiLCJ0b2tlbiI6Im1wLmV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5le******** | 旧IM的token。 |
refreshToken | String | eyJlbmRwb2ludHMiOlsid3NzOi8vbWV0YXBhdGguYWxpeXVuY3MuY29tL3dzIl0sImFwcElkIjoiMzhXWUg3RlQiLCJ0b2tlbiI6Im1wLmV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5le******** | 旧IM的refreshtoken。 | |
aliyunNewIm | appId | String | 0c826fdeb1c5 | 用户的AppID。 |
appSign | String | H4sIAAAAAAAE/wBgAJ//zguHB+lYCilkv7diSkk4GgjFw0Ka520mdSvKR1g6pC4Y3s/sSRMZR8HgSaLZHP5mcR95bdEfWhnB2Br1efpa62ZdKcr5noeaExJXc1ZVZUGP6ZY2yOklt/1uAIlPpOn7AAAA//8BAAD///ZbnP******* | 服务器生成的加密串,包含接入域名等信息。 | |
appToken | String | 42e96eeede710b738a561b9edcfac3507eae7036cf10fb7ed4b059afc6****** | 登录鉴权。 | |
auth.userId | String | 123456**** | 要登录的用户的ID。 | |
auth.nonce | String | 20d3acba-b4d4-4940-abb1-9e908813c71a | 格式:"AK-随机串",最长64字节。 | |
auth.timestamp | Long | 1702371472 | 过期时间,从1970到过期时间的秒数。 | |
auth.role | String | admin | 角色,为admin时,表示该用户可以调用管控接口。 |
示例
请求示例
{
"userId": "123456****",
"deviceId": "fwfwqeqeq****",
"deviceType":"web",
"imServer":["aliyun_new", "aliyun_old"]
}
正常返回示例
{
"success": true,
"data": {
"aliyunOldIm": {
"accessToken": "eyJlbmRwb2ludHMiOlsid3NzOi*******eXVuY3MuY29tL3dzIl0sImFwcElkIjoiMzhXWUg3RlQiLCJ0b2tlbiI6Im1wLmV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5leUoxYVdRaU9pSXhNak0wTlRZM09Ea3dJaXdpWlhod0lqb3hOekEwT1RVNU9EY3lMQ0poYVdRaU9pSXpPRmRaU0RkR1ZDSXNJbVJwWkNJNkltWjNabmR4WlhGbGNYZGxkM0VpTENKamFXUWlPaUl4TkRJNE16WTNPVGd6T0RrMU5UVTJJbjAuRTR2TlRvT3FsLVllanJfb3VlZUloblVEaDR4TzhGSUxTd09PMDl*******",
"refreshToken": "eyJlbmRwb2ludHMiOlsid3NzOi*********GguYWxpeXVuY3MuY29tL3dzIl0sImFwcElkIjoiMzhXWUg3RlQiLCJ0b2tlbiI6Im1wLmV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5leUoxYVdRaU9pSXhNak0wTlRZM09Ea3dJaXdpWlhod0lqb3hOekEwT1RVNU9EY3lMQ0poYVdRaU9pSXpPRmRaU0RkR1ZDSXNJbVJwWkNJNkltWjNabmR4WlhGbGNYZGxkM0VpTENKamFXUWlPaUl4TkRJNE16WTNPVGd6T0RrMU5UVTJJbjAuRTR2TlRvT3FsLVllanJfb3VlZUloblVEaDR4TzhGSUxTd09PMD********"
},
"aliyunNewIm": {
"auth": {
"nonce": "20d3acba-b4d4-4940-abb1-9e908813****",
"timestamp": 1702371472,
"role": "",
"userId": "123456****"
},
"appId": "0c826fdeb1c5",
"appSign": "H4sIAAAAAAAE/wBgAJ//zguHB+lYCilkv7diSkk4GgjFw0Ka520mdSvKR1g6pC4Y3s/sSRMZR8HgSaLZHP5mcR95bdEfWhnB2Br1efpa62ZdKcr5noeaExJXc1ZVZUGP6ZY2yOklt/1uAIlPpOn7AAAA//8BAAD///ZbnP*******",
"appToken": "42e96eeede710b738a561b9edcfac3507eae7036cf10fb7ed4b059a*******"
}
}
}
获取OSS配置
请参考使用STS临时访问凭证访问OSS实现,目前开源的Appserver源码中暂未实现,请自行实现。
Path:/exam/getOssConfig
Method:GET
Query:
name | type | required | desc |
examId | 考试ID | YES | 考试ID。 |
Body:
name | type | desc |
success | boolean | 是否成功。 |
errorCode | string | 异常码。 |
errorMsg | string | 异常信息。 |
data | object | 数据。 |
|─region | string | 以华东1(杭州)为例,yourRegion填写为oss-cn-hangzhou。 |
|─accessKeyId | string | 从STS服务获取的AccessKey IDAccessKey Secret。 |
|─accessKeySecret | string | 从STS服务获取的AccessKey Secret。 |
|─stsToken | string | 从STS服务获取的安全令牌(SecurityToken)。 |
|─bucket | string | 填写Bucket名称。 |
Response Demo:
{
"success": true,
"errorCode": "",
"errorMsg": "",
"data": {
"region": "",
"accessKeyId": "",
"accessKeySecret": "",
"stsToken": "",
"bucket": ""
}
}
查询系统音频列表
Path:/exam/selectAudio
Method:POST
Request Body:
name | type | desc |
pageSize | integer | 页面大小。 |
pageNum | integer | 页数。 |
name | string | 筛选音频名称(非必填)。 |
classify | integer | 分类。 |
Request Demo:
{
"pageSize": 0,
"pageNum": 0,
"name": "",
"classify": 0
}
Body:
name | type | desc |
success | boolean | 是否成功。 |
errorCode | string | 异常码。 |
errorMsg | string | 异常信息。 |
data | object | 数据。 |
|─pageNum | integer | 分页。 |
|─pageSize | integer | 一页条数。 |
|─total | integer | 总数。 |
|─list | array | |
|─ | object | |
|─id | string | ID。 |
|─classify | integer | 分类字典。 |
|─name | string | 音频名字。 |
|─url | string | 音频地址。 |
|─ossUrl | string | OSS地址(非必需)。 |
Response Demo:
{
"success": true,
"errorCode": "",
"errorMsg": "",
"data": {
"pageNum": 1,
"pageSize": 10,
"total": 14,
"list": [
{
"id": "11111",
"classify": 0,
"name": "audio",
"url": "",
"ossUrl": "",
}
]
}
}
更新考场口播状态
Path:/exam/updateRoomAudioStatus
Method:POST
Query:
name | value | required | desc |
id | string | YES | 考场ID。 |
audioStatus | int | YES | 考场口播状态。
|
Body:
name | type | desc |
success | boolean | 是否成功。 |
errorCode | string | 异常码。 |
errorMsg | string | 异常信息。 |
data | boolean | 数据。 |
Response Demo:
{
"success": true,
"errorCode": "",
"errorMsg": "",
"data": true
}
设置防作弊配置
请求协议:http/https
请求Path:/exam/cheat/setCheatConfig
是否需要授权:是
请求Method:POST
请求ContentType:JSON
请求参数
名称 | 类型 | 是否必填 | 示例值 | 描述 | |||
examId | String | 是 | useridxxxx | 考试ID | |||
data | String | 是 | xxxx | 作弊的配置信息 cheatConfig。由端自定义,服务器只管存储及返回 |
cheatConfig对象中有pc、mobile两个对象,每项的类型均为Boolean ,含义如下所示:
// 配置项为 true 时代表开启此项检测
const cheatConfig = {
pc: {
scenePersonExit: true, // 人物离开
manyPeople: true, // 多人
actionPoseStandup: true, // 起立
actionPoseHandup: true, // 举手
actionHeadUpDown: true, // 点头
headShaking: true, // 摇头/转头
objectDetect: true, // 携带电子设备
actionPersonSpeech: true, // 识别到人声
},
mobile: {
scenePersonExit: true, // 人物离开
manyPeople: true, // 多人
actionPoseStandup: true, // 起立
actionPoseHandup: true, // 举手
actionHeadUpDown: true, // 点头
headShaking: true, // 摇头/转头
objectDetect: true, // 携带电子设备
actionPersonSpeech: true, // 识别到人声
}
};
// 将 cheatConfig 对象转为 JSONString 后传给服务端保存
const data = JSON.stringify(cheatConfig);
返回数据
名称 | 类型 | 示例值 | 描述 | ||||
success | Boolean | true | 操作结果
|
示例
请求示例
{
"examId": "11111111111",
"data":"{xxxx}"
}
正常返回示例
{
"success": true
}
获取考试防作弊配置
请求协议:http/https
请求Path:/exam/cheat/getCheatConfig
是否需要授权:是
请求Method:POST
请求ContentType:JSON
请求参数
名称 | 类型 | 是否必填 | 示例值 | 描述 | |||
examId | String | 是 | useridxxxx | 考试ID |
返回数据
名称 | 类型 | 示例值 | 描述 | ||||
success | Boolean | true | 操作结果
| ||||
data | JsonObject | 结果,以JSON对象返回 | |||||
data.examId | String | 2023-02-27T17:34:00.592750182+08:00 | 考试ID | ||||
data.data | String | 2023-02-27T17:34:00.592750182+08:00 | 作弊的配置信息 | ||||
data.createdAt | String | 2024-03-18T16:56:11+08:00 | 创建时间 | ||||
data.updatedAt | String | 2024-03-18T16:56:11+08:00 | 修改时间 |
示例
请求示例
{
"examId": "11111111111"
}
返回示例
{
"success": true,
"data": {
"examId": "11111111111",
"data": "[xxxxxx]",
"createdAt": "2024-03-18T16:56:11+08:00",
"updatedAt": "2024-03-18T16:56:11+08:00"
}
}
添加考场作弊信息
请求协议:http/https
请求Path:/exam/cheat/addCheatRecord
是否需要授权:是
请求Method:POST
请求ContentType:JSON
请求参数
名称 | 类型 | 是否必填 | 示例值 | 描述 | |||
examId | String | 是 | examidxxxx | 考试ID | |||
roomId | String | 是 | roomIdxxxx | 考场ID | |||
data | String | 是 | [{\"detectTime\":232323,\"isMainMonitor\":true,\"detectType\":\"11\",\"userId\":\"userewexx\",\"extraInfo\":\"{222}2\"},{\"detectTime\":232324,\"isMainMonitor\":true,\"detectType\":\"12\",\"userId\":\"userewexx\",\"extraInfo\":\"{222}2\"}] | 学生作弊信息cheatRecord,由端自定义 |
cheatRecord对象的定义说明如下,提交至服务端前要先转为JSONString。
interface CheatRecord {
isMainMonitor: boolean; // 是否是主机位
userId:string; // 用户id
detectTime: number; // 检测时间,单位毫秒
detectType: string; // 检测类型
extraInfo: {
message: string; // 提示信息
};
}
返回数据
名称 | 类型 | 示例值 | 描述 | ||||
success | Boolean | true | 操作结果
|
示例
请求示例
{
"examId": "333333",
"roomId":"roomIdtest",
"data":"[{\"detectTime\":232323,\"isMainMonitor\":true,\"detectType\":\"11\",\"userId\":\"userewexx\",\"extraInfo\":\"{222}2\"},{\"detectTime\":232324,\"isMainMonitor\":true,\"detectType\":\"12\",\"userId\":\"userewexx\",\"extraInfo\":\"{222}2\"}]"
}
正常返回示例
{
"success": true
}
获取考场作弊信息
请求协议:http/https
请求Path:/exam/cheat/listCheatRecord
是否需要授权:是
请求Method:POST
请求ContentType:JSON
请求参数
名称 | 类型 | 是否必填 | 示例值 | 描述 | |||
examId | String | 是 | examidxxxx | 考试ID | |||
roomId | String | 是 | roomIdxxxx | 考场ID | |||
pageSize | String | 否 | 10 | 分页大小 | |||
scrollToken | String | 否 | 分页标识,用于标识下一页的起始位。通过传该标识可以检索下一页数据 |
返回数据
名称 | 类型 | 示例值 | 描述 | ||||
success | Boolean | true | 操作结果
| ||||
data | JsonObject | 结果,以JSON对象返回 | |||||
data.cheatRecordEntitys | JsonArray | 作弊信息。由端自定义,服务器结构化输出。见参考示例 |
示例
请求示例
{
"examId": "333333",
"roomId": "roomIdtest",
"pageSize":10,
"scrollToken":""
}
正常返回示例
{
"success": true,
"data": {
"cheatRecordEntitys": [
{
"id": 16666,
"examId": "333333",
"roomId": "roomIdtest",
"userId": "userewexx",
"detectType": "12",
"isMainMonitor": "true",
"detectTime": "1970-01-01T08:03:52+08:00",
"extraInfo": "{222}2",
"createdAt": "2024-03-18T17:00:53+08:00",
"updatedAt": "2024-03-18T17:00:53+08:00"
},
{
"id": 16665,
"examId": "333333",
"roomId": "roomIdtest",
"userId": "userewexx",
"detectType": "11",
"isMainMonitor": "true",
"detectTime": "1970-01-01T08:03:52+08:00",
"extraInfo": "{222}2",
"createdAt": "2024-03-18T17:00:53+08:00",
"updatedAt": "2024-03-18T17:00:53+08:00"
},
{
"id": 8346,
"examId": "333333",
"roomId": "roomIdtest",
"userId": "userewexx",
"detectType": "12",
"isMainMonitor": "true",
"detectTime": "1970-01-01T08:03:52+08:00",
"extraInfo": "{222}2",
"createdAt": "2023-11-14T15:55:25+08:00",
"updatedAt": "2023-11-14T15:55:25+08:00"
},
{
"id": 8345,
"examId": "333333",
"roomId": "roomIdtest",
"userId": "userewexx",
"detectType": "11",
"isMainMonitor": "true",
"detectTime": "1970-01-01T08:03:52+08:00",
"extraInfo": "{222}2",
"createdAt": "2023-11-14T15:55:25+08:00",
"updatedAt": "2023-11-14T15:55:25+08:00"
},
{
"id": 8342,
"examId": "333333",
"roomId": "roomIdtest",
"userId": "userewexx",
"detectType": "12",
"isMainMonitor": "true",
"detectTime": "1970-01-01T08:03:52+08:00",
"extraInfo": "{222}2",
"createdAt": "2023-11-14T15:46:48+08:00",
"updatedAt": "2023-11-14T15:46:48+08:00"
},
{
"id": 8341,
"examId": "333333",
"roomId": "roomIdtest",
"userId": "userewexx",
"detectType": "11",
"isMainMonitor": "true",
"detectTime": "1970-01-01T08:03:52+08:00",
"extraInfo": "{222}2",
"createdAt": "2023-11-14T15:46:44+08:00",
"updatedAt": "2023-11-14T15:46:44+08:00"
},
{
"id": 8340,
"examId": "333333",
"roomId": "roomIdtest",
"userId": "userewexx",
"detectType": "12",
"isMainMonitor": "true",
"detectTime": "1970-01-01T08:03:52+08:00",
"extraInfo": "{222}2",
"createdAt": "2023-11-14T15:45:40+08:00",
"updatedAt": "2023-11-14T15:45:40+08:00"
},
{
"id": 8339,
"examId": "333333",
"roomId": "roomIdtest",
"userId": "userewexx",
"detectType": "11",
"isMainMonitor": "true",
"detectTime": "1970-01-01T08:03:52+08:00",
"extraInfo": "{222}2",
"createdAt": "2023-11-14T15:45:40+08:00",
"updatedAt": "2023-11-14T15:45:40+08:00"
}
],
"scrollToken": "8339"
}
}
结束考场
Path:/exam/endRoom
Method:GET
Query:
name | type | required | desc |
roomId | string | YES | 考场ID,通过页面URL参数传入。 |
Body:
name | type | desc |
success | boolean | 是否成功。 |
errorCode | string | 异常码。 |
errorMsg | string | 异常信息。 |
data | boolean | 数据。 |
Response Demo:
{
"success": true,
"errorCode": "",
"errorMsg": "",
"data": true
}