Appearance
AI 对练数据接口
公共约定
本文覆盖后台数据查询,不包含任务编辑、开始对练、流式对话或语音接口。以 EduSoho 为主数据源的客户,先由 EduSoho 服务端申请 SSO ticket 并兑换令牌,再请求 https://aicoach.edusoho.com。所有接口要求 Bearer JWT 和 ROLE_ADMIN,详见 接口鉴权 。
- 路径前缀为
/api/v1/admin/drill-task,数据属于当前认证租户。 - ID 按字符串接收和保存,避免 JavaScript 大整数精度丢失;时间字段为毫秒时间戳,时长字段
durationMs单位为毫秒。 - 分页请求使用
page、size、sort,页码从1开始,默认每页20条;排序示例为sort=startedAt,desc。 - 分页响应为
{ "data": [], "total": 0, "page": 1, "size": 20 },不是 PHP 的paging.offset/limit。 - 可空字段可能没有数据,不要把空分数当作零分。错误结构见本页「错误响应」。
接口一览
以下均为 GET,路径相对于上述前缀。
| 路径 | 用途 |
|---|---|
/record/search | 全局对练记录列表 |
/record/stats | 全局对练统计 |
/assignee-records/search | 任务下学员汇总 |
/user-records/search | 指定任务、学员的对练记录 |
/record/{recordId} | 单次记录、对话和报告详情 |
全局记录列表
http
GET /api/v1/admin/drill-task/record/search能查询什么数据
查询当前 AK 绑定租户下的学员单次对练记录摘要,包含学员、部门、任务、练习产品、AI 角色、时长、分数和状态。同一学员在同一任务下多次练习会返回多条记录;这不是任务列表,也不是学员完成情况汇总。
鉴权时提供的 EduSoho 管理用户名和用户 ID 仅用于确定调用身份,不会自动限制为该管理员自己的记录。未传筛选条件时,查询当前租户下的记录,默认按开始时间倒序分页。不能跨租户查询。
本接口不返回聊天正文、评分维度或辅导建议;如需这些数据,使用记录 id 调用本页「单次记录详情」接口。
入参
请求头 Authorization: Bearer <accessToken> 必填。以下参数均放在 URL 查询字符串中,不传请求体。筛选条件可组合使用。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| taskId | string | 否 | 任务 ID |
| userId | string | 否 | AI 对练系统内学员用户 ID,不是外部 LMS 用户 ID |
| status | string | 否 | 底层状态,IN_PROGRESS 或 COMPLETED |
| taskName | string | 否 | 任务名称关键词 |
| userName | string | 否 | 学员昵称关键词 |
| page | integer | 否 | 页码,默认 1 |
| size | integer | 否 | 每页数量,默认 20 |
| sort | string | 否 | 默认 startedAt,desc |
bash
curl 'https://aicoach.edusoho.com/api/v1/admin/drill-task/record/search?page=1&size=20&status=COMPLETED' \
-H "Authorization: Bearer ${ACCESS_TOKEN}"出参
| 字段 | 类型 | 说明 |
|---|---|---|
| data | array | 当前页对练记录,每项代表一次练习;无匹配数据时为空数组 |
| total | long | 满足筛选条件的记录总数,不是学员数或任务数 |
| page | integer | 当前页码,从 1 开始 |
| size | integer | 当前请求的每页条数,不等于本页实际返回条数 |
data 中每项的字段如下。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 单次对练记录 ID,用于查询详情 |
| taskId | string | 所属任务 ID |
| userId | string | AI 对练内部学员 ID,不是 EduSoho 用户 ID |
| taskName | string | 任务名称 |
| userNickname | string | 学员昵称 |
| departmentName | string | 部门名称 |
| status | string | 底层记录状态 |
| displayStatus | string | 展示状态,见下文 |
| effectiveForCompletion | boolean | 是否计入有效出分 |
| score | integer | 总分,可空 |
| startedAt | long | 开始时间 |
| durationMs | long | 练习时长 |
| dialogueRoleName | string | 本场角色名 |
| dialogueRoleInvalid | boolean | 角色是否已删除 |
| productName | string | 练款产品名 |
| productCode | string | 产品款号 |
| productGroupType | string | 开练时商品分组归属,历史记录可能为空 |
| productInvalid | boolean | 产品是否已删除 |
displayStatus 的值为 IN_PROGRESS(进行中)、VALID_SCORED(有效出分)、ABANDONED(中途退出)、ABNORMAL(异常结束)、NO_SCORE(未生成评分)。不能将这些值直接传给仅接受底层状态的 status 筛选。
effectiveForCompletion 表示该条记录可参与任务完成条件的计算:记录需已完成、非异常结束、有分数且有结束时间。它不等于本场已达标,更不等于整个任务已完成;任务完成结果应查询「任务下学员汇总」中的 completed。
响应示例
以下为虚构数据。示例表示有 42 条匹配记录,当前页大小为 1,返回 1 条;获取后续数据时递增 page,保持筛选条件一致。
json
{
"data": [
{
"id": "2119815926793768000",
"taskId": "2119801972071731000",
"taskName": "产品卖点对练",
"userId": "2074103252705742000",
"userNickname": "示例学员",
"departmentName": "示例部门",
"dialogueRoleName": "模拟客户",
"dialogueRoleInvalid": false,
"productName": "示例产品",
"productCode": "DEMO-001",
"productGroupType": "REQUIRED",
"productInvalid": false,
"status": "COMPLETED",
"displayStatus": "VALID_SCORED",
"effectiveForCompletion": true,
"score": 20,
"startedAt": 1794238475879,
"durationMs": 50126
}
],
"page": 1,
"size": 1,
"total": 42
}上述记录练习约 50.1 秒,获得 20 分且有效出分,但不能据此认定任务完成。score: null 表示暂无分数,与 score: 0 不同。
全局统计
http
GET /api/v1/admin/drill-task/record/stats支持列表中的 taskId、userId、status、taskName、userName 筛选,不分页,直接返回对象。
| 字段 | 类型 | 说明 |
|---|---|---|
| totalDrills | long | 总对练次数 |
| avgScore | double | 平均得分,可空 |
| completedCount | long | 已完成次数 |
| inProgressCount | long | 进行中次数 |
统计不返回任务完成学员数,不能把 completedCount 当作完成任务人数。
任务下学员汇总
http
GET /api/v1/admin/drill-task/assignee-records/search?taskId={taskId}| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| taskId | string | 是 | 任务 ID |
| keywords | string | 否 | 昵称、用户名或手机号关键词 |
| completed | boolean | 否 | true 已完成,false 未完成,不传则不过滤 |
| departmentId | string | 否 | 部门 ID |
| includeDescendants | boolean | 否 | 是否包含下级部门;需要下级时显式传 true |
| unassignedOnly | boolean | 否 | 仅未归属部门学员;为 true 时忽略 departmentId |
| page、size、sort | — | 否 | 公共分页参数,默认按 lastPracticedAt,desc 排序 |
返回分页对象,data 每项包含以下字段。
| 字段 | 类型 | 说明 |
|---|---|---|
| userId、nickname | string | 学员 ID、昵称 |
| departments、positions | array | 部门、岗位简要对象数组 |
| attemptCount | integer | 已完成对练次数 |
| firstScore、bestScore | integer | 首次、最好得分 |
| lastPracticedAt | long | 最近练习时间 |
| completed | boolean | 是否达到任务完成条件 |
| completedAt | long | 首次达成完成条件的时间,未完成时为空 |
| qualifiedCount | integer | 当前达标次数或达标商品数 |
| requiredCount | integer | 要求达标次数或达标商品数 |
同功能路径:GET /{id}/records/search,其中 id 为任务 ID,其余条件相同。
单学员记录
http
GET /api/v1/admin/drill-task/user-records/search?taskId={taskId}&userId={userId}taskId、userId 必填;支持公共分页参数,默认按 startedAt,desc 排序。响应为分页对象。
每项包含全局列表的 id、status、displayStatus、effectiveForCompletion、score、startedAt、durationMs 和角色/产品字段,额外包含 endedAt(结束时间)。不包含全局列表的任务、学员和部门展示字段。
同功能路径:GET /{id}/records/{userId}/search。
单次记录详情
http
GET /api/v1/admin/drill-task/record/{recordId}recordId 为必填路径参数,返回对象而非分页。字段包含单学员记录的全部字段,以及 taskId、taskName、userId、userNickname、messages、report。
对话消息
messages 为数组,每项字段如下。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 消息 ID |
| messageUuid | string | 消息 UUID |
| role | string | 消息角色,USER 表示学员 |
| content | string | 消息正文 |
| createdAt | long | 创建时间 |
| speakerLabel | string | 展示名称,学员或 AI |
评价报告
report 可能为空;存在时不代表评分已就绪,应检查 status。
| 字段 | 类型 | 说明 |
|---|---|---|
| recordId | string | 对练记录 ID |
| status | string | GENERATING 生成中、READY 已就绪、FAILED 失败 |
| totalScore、gradeLabel | integer/string | 总分、综合档位 |
| passed | boolean | 本场是否达标,无法判定时为空 |
| summary、tutorAdvice | string | 对练总结、教练辅导 |
| dimensions | array | 维度项包含 name、score、maxScore、grade、comment;档位为 GOOD、MEDIUM、POOR |
| highlights、improvements | array | 亮点、改进建议;每项包含 title、description |
| productName、productCode、productInvalid、productGroupType | — | 产品快照和失效标记 |
| dialogueRoleName | string | 对练角色名称快照 |
| createdAt、endedAt、durationMs | long | 创建时间、结束时间、时长 |
报告生成是异步过程。记录结束后,报告可能仍在生成或失败,不要据此推断接口返回数据缺失。
错误响应
接口使用真实 HTTP 状态码,不使用旧 PHP 接口的 error 包装或 envelope=true 约定。
json
{
"timestamp": 1794355200000,
"status": 401,
"code": "UNAUTHORIZED",
"message": "未认证或认证失败",
"reqId": "request-id"
}timestamp 为错误发生时间,status 为 HTTP 状态,code 为错误码,message 为说明,reqId 用于协助排查。
| HTTP 状态 | 错误码 | 处理建议 |
|---|---|---|
| 401 | UNAUTHORIZED | 检查是否携带有效访问令牌;必要时重新通过 EduSoho 申请 ticket 并兑换令牌 |
| 403 | FORBIDDEN | 当前身份没有权限,检查管理用户授权 |
不要记录完整令牌、密钥或 ticket;排查时提供错误码和请求 ID。