Practical API(实操考评考评员端)

应用:app.practical
接口前缀:/dapi/practical/examiner/


0. 通用约定

0.1 鉴权

登录前接口无需 token:

POST /dapi/practical/examiner/auth/code
POST /dapi/practical/examiner/auth/login

登录后接口需携带:

Authorization: PracticalExaminer <token>

0.2 响应格式

成功:

{
  "data": {}
}

失败:

{
  "data": {
    "info": "错误说明",
    "code": "PR001"
  }
}

分页:

{
  "data": {
    "results": [],
    "count": 0,
    "next": null,
    "previous": null
  }
}

0.3 实操考评试题结构

接口中的试卷、试题和评分内容固定使用以下层级:

试卷 form
  -> 实操考评题 items[]
    -> 题干 question.text
    -> 参考答案 reference
    -> 考核项 assessment_items[]
      -> 考核内容描述 text
      -> 考核要求 requirements[]
        -> 考核要求描述 text
        -> 扣分类型 deduct_rule
        -> 分值 maximum_mark
        -> 评分标准 standards[]
          -> 评分标准描述 text
          -> 扣分类型 deduct_scope
          -> 扣分上限 maximum_mark
          -> 扣分单位 deduction_unit
    -> 否定项 negative
      -> 否定项描述 title
      -> 子否定项 options[]
        -> 子否定项描述 text

一份实操考评题可以有多个考核项,每个考核项包含一段考核内容描述和多条考核要求。评分标准是实际录入扣分值的最小单位;deductions[].standard_id 对应 standards[].id

每题最多配置一个 negative 对象,未配置时为 null;一个否定项内可以配置多个子否定项。命中任一子否定项后,本题得分为 0

0.4 枚举

任务状态 status

说明
not_started 未开始,不可开始评分
active 进行中,可开始评分
ended 已结束;任务列表可展示,但不可进入任务详情、添加考生或评分流程

任务内考生状态 status

说明
available 可添加到评分列表
scoring 已加入评分列表,待评分或评分中
completed 已提交评分

签字状态 signature_status

说明
unsigned 已提交评分,未签字确认
signed 已签字确认

扣分范围 deduct_scope

说明
standard 按照评分标准扣,以当前评分标准的扣分上限为限
requirement 以上层考核要求分值为扣分上限
item 以本题总分为扣分上限

性别 gender

说明
1
2

1. 登录与会话

1.1 获取验证码

POST /dapi/practical/examiner/auth/code

Request Body

参数名 必须 类型 说明
tenant_id integer 租户内部 ID,与考评地址 /practical-examiner/{tenant_id}/ 中的值一致
phone string 考评员手机号

Status Code :200
success_response: JSON

{
  "data": {
    "code_sent": true
  }
}

Response data

字段 类型 说明
code_sent boolean 是否已发送验证码

1.2 考评员登录

POST /dapi/practical/examiner/auth/login

Request Body

参数名 必须 类型 说明
tenant_id integer 租户内部 ID,与考评地址 /practical-examiner/{tenant_id}/ 中的值一致
phone string 手机号
code string 验证码

Status Code :200
success_response: JSON

{
  "data": {
    "token": "examiner-token",
    "examiner": {
      "id": "examiner-001",
      "name": "李考评",
      "phone": "13800000000",
      "identity_type": "身份证",
      "identity_number": "110101199001010011",
      "gender": 1,
      "avatar": ""
    }
  }
}

Response data

字段 类型 说明
token string 考评员端 token
examiner.id string 考评员 ID
examiner.name string 考评员姓名
examiner.phone string 手机号
examiner.identity_type string 证件类型
examiner.identity_number string 证件号码
examiner.gender integer 性别,见 0.4
examiner.avatar string 头像地址

1.3 退出登录

POST /dapi/practical/examiner/auth/logout

Status Code :200
success_response: JSON

{
  "data": {}
}

2. 考评任务

2.1 获取任务列表

GET /dapi/practical/examiner/tasks

Query

参数名 必须 类型 说明
status string activeended;不传返回当前考评员可见任务

Status Code :200
success_response: JSON

{
  "data": [
    {
      "id": "task-demo-001",
      "name": "护理无菌操作考评",
      "candidate_label": "考生",
      "subject_id": "subject-001",
      "subject_name": "无菌操作",
      "status": "active",
      "start_time": 1783388400,
      "end_time": 1783474800,
      "scoring_count": 3,
      "completed_count": 1,
      "unsigned_signature_count": 1,
      "face_verify_enabled": false,
      "hide_candidate_identity": false,
      "score_edit_locked_after_submit": true,
      "note_enabled": true,
      "candidate_limit": 3
    }
  ]
}

Response data[]

字段 类型 说明
id string 任务 ID
name string 任务名称
candidate_label string 考生称呼,例如 考生学员
subject_id string/null 当前评分任务所属科目 ID
subject_name string 当前评分任务所属科目名称
status string 任务状态,见 0.4
start_time integer 开始时间,Unix 时间戳,单位秒
end_time integer 结束时间,Unix 时间戳,单位秒
scoring_count integer 待评分或评分中的考生数量
completed_count integer 已提交评分的考生数量
unsigned_signature_count integer 已评分但未签字确认的考生数量
face_verify_enabled boolean 是否要求考评员进入任务前进行人脸核验
hide_candidate_identity boolean 是否隐藏考生姓名、证件号等身份信息
score_edit_locked_after_submit boolean 评分提交后是否禁止修改;对应管理端“改分限制”
note_enabled boolean 是否允许填写考生备注
candidate_limit integer 同一考评员在当前任务内最多同时保留的未完成评分考生数量;对应管理端“考评限制”

status=ended 的任务只用于列表展示和统计。前端不应允许点击进入;后端对已结束任务的添加考生、保存评分、提交评分、清除评分、签字等写操作应返回失败。

签字确认和提交后锁定评分互斥。同一任务启用签字确认时,score_edit_locked_after_submit 必须返回 false;需要禁止修改时,以签字确认流程作为最终确认,不再同时要求提交前勾选“提交后无法修改评分”。

2.2 获取任务详情

GET /dapi/practical/examiner/tasks/{task_id}

Status Code :200
success_response: JSON

{
  "data": {
    "id": "task-demo-001",
    "name": "护理无菌操作考评",
    "candidate_label": "考生",
    "status": "active",
    "start_time": 1783388400,
    "end_time": 1783474800,
    "scoring_count": 3,
    "completed_count": 1,
    "unsigned_signature_count": 1,
    "face_verify_enabled": false,
    "hide_candidate_identity": false,
    "score_edit_locked_after_submit": true,
    "note_enabled": true,
    "candidate_limit": 3
  }
}

响应字段同 2.1。

2.3 获取评分试题/评分表

GET /dapi/practical/examiner/tasks/{task_id}/items

Status Code :200
success_response: JSON

{
  "data": {
    "items": [
      {
        "id": "item-001",
        "current_score": null,
        "content": {
          "name": "无菌操作准备",
          "total_score": 50,
          "reference": "按现场标准流程完成准备、核对和手卫生。",
          "is_dimension_score": true,
          "question": {
            "text": "请完成无菌操作前准备。"
          },
          "rubric": {
            "maximum_mark": 50,
            "assessment_items": [
              {
                "id": "content-001",
                "text": "物品和环境准备",
                "requirements": [
                  {
                    "id": "req-001",
                    "text": "核对操作物品",
                    "minimum_mark": 0,
                    "maximum_mark": 10,
                    "deduct_rule": "standard",
                    "deducted_score": null,
                    "standards": [
                      {
                        "id": "standard-001",
                        "text": "物品缺项或不符合要求,每项扣 1 分,扣完为止。",
                        "minimum_mark": 0,
                        "maximum_mark": 10,
                        "deduction_unit": 1,
                        "deduct_scope": "standard",
                        "deducted_score": null
                      }
                    ]
                  }
                ]
              }
            ],
            "negative": {
              "title": "发生以下任一情况,本题不得分",
              "options": [
                {
                  "id": "neg-001",
                  "text": "严重违反无菌原则"
                }
              ],
              "selected_option_ids": []
            }
          }
        }
      }
    ]
  }
}

Response data.items[]

字段 类型 说明
id string 试题 ID
current_score number/null 当前考生该题得分;未评分为 null
content.name string 试题名称
content.total_score number 本题总分
content.reference string 参考答案
content.is_dimension_score boolean 是否按维度评分
content.question.text string 题干
content.rubric.maximum_mark number 本题评分总分,与 content.total_score 一致
content.rubric.assessment_items array 考核项列表
content.rubric.negative object/null 本题唯一的否定项;无否定项时为 null

assessment_items[]

字段 类型 说明
id string 考核项 ID
text string 考核内容描述
requirements array 当前考核项下的考核要求

assessment_items[].requirements[]

字段 类型 说明
id string 考核要求 ID
text string 考核要求描述
deduct_rule string 考核要求的扣分类型:standard 标准扣分,item 从本题总分扣
minimum_mark number 扣分输入下限,当前为 0
maximum_mark number 考核要求的分值;采用标准扣分时,也是该考核要求内扣分的累计上限
deducted_score number/null 当前考核要求累计扣分;未评分为 null
standards array 评分标准列表

assessment_items[].requirements[].standards[]

字段 类型 说明
id string 评分标准 ID;保存扣分时使用该 ID
text string 评分标准描述
minimum_mark number 扣分输入下限,当前为 0
deduct_scope string 评分标准的扣分类型,见 0.4。仅父级考核要求采用标准扣分时需要配置;父级从本题总分扣时由服务端按 item 处理
maximum_mark number 扣分上限;仅 deduct_scope=standard(按照评分标准扣)时返回
deduction_unit number 扣分单位,最多保留 1 位小数;实际扣分值必须是该值的整数倍
deducted_score number/null 当前评分标准已扣分;未评分为 null

negative

字段 类型 说明
title string 否定项描述
options[] array 子否定项列表
options[].id string 子否定项 ID
options[].text string 子否定项描述
selected_option_ids string[] 当前已选中的子否定项 ID;未选择为空数组

3. 考生

3.1 获取可添加考生

GET /dapi/practical/examiner/tasks/{task_id}/candidates/available

Query

参数名 必须 类型 说明
keyword string 姓名、证件号、自定义编号搜索
page integer 页码
page_size integer 每页条数

Status Code :200
success_response: JSON

{
  "data": {
    "results": [
      {
        "id": "cand-001",
        "user_id": "cand-001",
        "name": "张志",
        "custom_number": "A001",
        "phone": "13800000000",
        "identity_type": "身份证号",
        "identity_number": "510603200006109022",
        "gender": 1,
        "avatar": "",
        "score": null,
        "submitted_at": null,
        "signature_status": null
      }
    ],
    "count": 1,
    "next": null,
    "previous": null
  }
}

Candidate 字段

字段 类型 说明
id string 考生在本任务中的 ID;后续评分、移除、签字均使用此 ID
user_id string/null 底层考生用户 ID;没有独立用户对象时可与 id 相同
name string 考生姓名
custom_number string 自定义编号;隐藏身份信息时优先展示
phone string 手机号
identity_type string 证件类型
identity_number string 证件号码
gender integer 性别,见 0.4
avatar string 头像地址
score number/null 已评分总分;未评分为 null
submitted_at integer/null 评分提交时间,Unix 时间戳,单位秒;未提交为 null
signature_status string/null 签字状态;未提交评分时为 null

3.2 添加待评分考生

POST /dapi/practical/examiner/tasks/{task_id}/candidates

Request Body

参数名 必须 类型 说明
candidate_ids string[] 要加入评分列表的考生 ID

Status Code :200
success_response: JSON

{
  "data": {
    "failed_candidates": []
  }
}

Response data

字段 类型 说明
failed_candidates array 未能加入的考生及原因;全部成功时为空数组

3.3 获取任务内考生

GET /dapi/practical/examiner/tasks/{task_id}/candidates

Query

参数名 必须 类型 说明
status string scoringcompleted
keyword string 姓名、证件号、自定义编号搜索
sort string 已评分列表排序;submitted_at_ascsubmitted_at_descstatus=completed 且未传时默认 submitted_at_asc
page integer 页码
page_size integer 每页条数

Status Code :200
success_response: JSON

{
  "data": {
    "results": [
      {
        "id": "cand-006",
        "user_id": "cand-006",
        "name": "赵一",
        "custom_number": "A006",
        "phone": "13800000000",
        "identity_type": "身份证号",
        "identity_number": "510603200006109027",
        "gender": 1,
        "avatar": "",
        "score": 88.0,
        "submitted_at": 1783389000,
        "signature_status": "unsigned"
      }
    ],
    "count": 1,
    "next": null,
    "previous": null,
    "available_count": 3
  }
}

Response data

字段 类型 说明
results array Candidate 列表,字段见 3.1
count integer 当前查询总数
next string/null 下一页地址
previous string/null 上一页地址
available_count integer status=scoring 时返回;仍可添加到评分列表的未评分考生数量

3.4 移除评分中考生

DELETE /dapi/practical/examiner/tasks/{task_id}/candidates/{candidate_id}

Status Code :200
success_response: JSON

{
  "data": {}
}

4. 评分

4.1 获取考生评分详情

GET /dapi/practical/examiner/tasks/{task_id}/candidates/{candidate_id}/score

Status Code :200
success_response: JSON

{
  "data": {
    "items": [
      {
        "item_id": "item-001",
        "score": 47.0,
        "deductions": [
          {
            "standard_id": "standard-001",
            "deducted_score": 1.0
          }
        ],
        "negative_option_ids": []
      }
    ]
  }
}

Response data.items[]

字段 类型 说明
item_id string 试题 ID
score number 本题得分,不是扣分值
deductions array 各评分标准的扣分记录
deductions[].standard_id string 评分标准 ID,对应 standards[].id
deductions[].deducted_score number 该评分标准的扣分值
negative_option_ids string[] 已选择的子否定项 ID

4.2 保存单题评分

PUT /dapi/practical/examiner/tasks/{task_id}/candidates/{candidate_id}/items/{item_id}/score

Request Body

参数名 必须 类型 说明
score number 本题得分
deductions array 各评分标准的扣分记录
negative_option_ids string[] 已选择的子否定项 ID;没有则传空数组
rubric_snapshot object 当前题评分表快照;正式实现可由服务端按发布版本保存,旧 H5 过渡接入时可携带
{
  "score": 47.0,
  "deductions": [
    {
      "standard_id": "standard-001",
      "deducted_score": 1.0
    }
  ],
  "negative_option_ids": [],
  "rubric_snapshot": null
}

Status Code :200
success_response: JSON

{
  "data": {}
}

4.3 提交考生总评分

POST /dapi/practical/examiner/tasks/{task_id}/candidates/{candidate_id}/score/submit

Request Body

参数名 必须 类型 说明
total_score number 考生总分;可由服务端按单题评分汇总,客户端传入时用于一致性校验

Status Code :200
success_response: JSON

{
  "data": {
    "score": 88.0,
    "submitted_at": 1783389000,
    "signature_status": "unsigned"
  }
}

Response data

字段 类型 说明
score number 提交后的总分
submitted_at integer 提交时间,Unix 时间戳,单位秒
signature_status string 提交后的签字状态

提交评分时,服务端必须重新校验当前考生是否已完成所有必评内容。存在未完成试题、未完成评分标准或总分校验不一致时,不提交评分,返回失败:

{
  "data": {
    "info": "存在未完成评分项",
    "code": "PR_SCORE_INCOMPLETE"
  }
}

4.4 清除考生评分

DELETE /dapi/practical/examiner/tasks/{task_id}/candidates/{candidate_id}/score

Status Code :200
success_response: JSON

{
  "data": {}
}

4.5 获取考生备注

GET /dapi/practical/examiner/tasks/{task_id}/candidates/{candidate_id}/note

Status Code :200
success_response: JSON

{
  "data": {
    "note": "这是一条备注。"
  }
}

4.6 保存考生备注

PATCH /dapi/practical/examiner/tasks/{task_id}/candidates/{candidate_id}/note

Request Body

参数名 必须 类型 说明
note string 备注内容,最多 500 字;清空备注时传空字符串

Status Code :200
success_response: JSON

{
  "data": {}
}

备注超过 500 字时返回失败:

{
  "data": {
    "info": "备注不能超过500字",
    "code": "PR_NOTE_TOO_LONG"
  }
}

5. 上传与人脸核验

5.1 获取上传凭证

POST /dapi/practical/examiner/upload/credential

Request Body

参数名 必须 类型 说明
type string face 人脸照片;signature 签字图片

Status Code :200
success_response: JSON

{
  "data": {
    "access_id": "mock-access-id",
    "host": "https://oss.example.com",
    "policy": "base64-policy",
    "signature": "signature",
    "expire_at": 1783395600,
    "dir": "practical/signature/"
  }
}

Response data

字段 类型 说明
access_id string OSS AccessKeyId
host string OSS 上传地址
policy string 上传策略
signature string 上传签名
expire_at integer 过期时间,Unix 时间戳,单位秒
dir string 上传目录前缀

5.2 人脸核验

POST /dapi/practical/examiner/face/verify

Request Body

参数名 必须 类型 说明
task_id string 任务 ID
image_url string 已上传的人脸照片地址

Status Code :200
success_response: JSON

{
  "data": {
    "verified": true,
    "reason_code": null,
    "reason": "",
    "retryable": false
  }
}

Response data

字段 类型 说明
verified boolean 是否核验通过;false 表示本次照片未通过核验
reason_code string/null 未通过或跳过核验的原因编码,例如 face_not_matchno_face_detectedno_reference_photo
reason string 展示给考评员的原因说明;通过时为空字符串
retryable boolean 是否允许重新拍照核验

核验失败时,请求仍可返回 200,由 verified=false 表示业务未通过:

{
  "data": {
    "verified": false,
    "reason_code": "face_not_match",
    "reason": "人脸核验未通过,请重新拍照",
    "retryable": true
  }
}

未配置用于比对的基准照片时,不阻塞进入任务,按通过处理:

{
  "data": {
    "verified": true,
    "reason_code": "no_reference_photo",
    "reason": "未配置基准照片,已跳过核验",
    "retryable": false
  }
}

重新核验由客户端再次调用本接口完成。退出核验不需要额外接口;前端应返回任务页,并保持该任务未通过人脸核验的状态。


6. 签字确认

6.1 获取签字配置

GET /dapi/practical/examiner/signature/config

Query

参数名 必须 类型 说明
task_id string 任务 ID

Status Code :200
success_response: JSON

{
  "data": {
    "enabled": true,
    "unsigned_signature_count": 1
  }
}

Response data

字段 类型 说明
enabled boolean 当前任务是否启用签字确认
unsigned_signature_count integer 已评分但未签字确认的考生数量

6.2 获取待签字考生

GET /dapi/practical/examiner/signature/candidates

Query

参数名 必须 类型 说明
task_id string 任务 ID
page integer 页码
page_size integer 每页条数

Status Code :200
success_response: JSON

{
  "data": {
    "results": [
      {
        "id": "cand-006",
        "user_id": "cand-006",
        "name": "赵一",
        "custom_number": "A006",
        "phone": "13800000000",
        "identity_type": "身份证号",
        "identity_number": "510603200006109027",
        "gender": 1,
        "avatar": "",
        "score": 88.0,
        "submitted_at": 1783389000,
        "signature_status": "unsigned"
      }
    ],
    "count": 1,
    "next": null,
    "previous": null
  }
}

Candidate 字段见 3.1。

6.3 提交签字

POST /dapi/practical/examiner/signature

Request Body

参数名 必须 类型 说明
task_id string 任务 ID
candidate_ids string[] 要确认签字的考生 ID
image_url string 签字图片地址

Status Code :200
success_response: JSON

{
  "data": {
    "signed_count": 1
  }
}

7. 附录:迁移对照

7.1 接口对照

Kaola 新接口 远鉴原始接口 说明
POST /auth/code POST /get-code 获取验证码
POST /auth/login POST /judge-login 考评员登录
POST /auth/logout POST /judge-logout 退出登录
GET /tasks POST /get-tasks 任务列表
GET /tasks/{task_id} POST /get-task-detail 任务详情
GET /tasks/{task_id}/items POST /get-task-items 评分试题/评分表
GET /tasks/{task_id}/candidates/available POST /get-candidates 可添加考生
POST /tasks/{task_id}/candidates POST /add-candidates 添加待评分考生
GET /tasks/{task_id}/candidates?status=scoring POST /get-incomplete-candidates 评分中考生
GET /tasks/{task_id}/candidates?status=completed POST /get-completed-candidates 已评分考生
DELETE /tasks/{task_id}/candidates/{candidate_id} POST /remove-candidate 移除评分中考生
GET /tasks/{task_id}/candidates/{candidate_id}/score POST /get-candidate-score 评分详情
PUT /tasks/{task_id}/candidates/{candidate_id}/items/{item_id}/score POST /post-item-score 保存单题评分
POST /tasks/{task_id}/candidates/{candidate_id}/score/submit POST /post-candidate-score 提交考生评分
DELETE /tasks/{task_id}/candidates/{candidate_id}/score POST /clear-candidate-score 清除评分
GET /tasks/{task_id}/candidates/{candidate_id}/note POST /get-candidate-note 获取备注
PATCH /tasks/{task_id}/candidates/{candidate_id}/note POST /post-candidate-note 保存备注
POST /upload/credential POST /g-auth 获取上传凭证
POST /face/verify POST /judge-cert 人脸核验
GET /signature/config POST /get-sign-conf 签字配置
GET /signature/candidates POST /get-unsign-candidates 待签字考生
POST /signature POST /post-sign 提交签字

7.2 字段映射

7.2.1 任务

远鉴字段 Kaola 字段 说明
tuid id 任务 ID
uid examiner_id 考评员 ID;任务对象中可不返回
n name 任务名称
ct candidate_label 考生称呼
s status 任务状态:1 -> not_started2 -> active3 -> ended
st start_time 开始时间,Unix 时间戳,单位秒
et end_time 结束时间,Unix 时间戳,单位秒
inc scoring_count 待评分或评分中数量
com completed_count 已评分数量
usc unsigned_signature_count 未签字确认数量
cert face_verify_enabled 是否启用人脸核验
hide hide_candidate_identity 是否隐藏考生身份信息
limit score_edit_locked_after_submit 管理端“评分限制”:提交评分后是否禁止修改
note note_enabled 是否启用备注
cnum candidate_limit 管理端“考评限制”:同一考评员最多同时保留的未完成评分考生数量

7.2.2 考生

远鉴字段 Kaola 字段 说明
cuid id 任务内考生 ID
uid user_id 底层考生用户 ID
n name 姓名
cn custom_number 自定义编号
p phone 手机号
it identity_type 证件类型
in identity_number 证件号码
g gender 性别
po avatar 头像
s score 已评分总分
st submitted_at 评分提交时间,Unix 时间戳,单位秒
sStatus signature_status 签字状态:0 -> unsigned1 -> signed

7.2.3 评分表

远鉴字段 Kaola 字段 说明
items[].iuid items[].id 试题 ID
items[].score items[].current_score 当前题得分
items[].item.name items[].content.name 试题名称
items[].item.sore items[].content.total_score 本题总分
items[].item.reference items[].content.reference 参考答案
items[].item.isdim items[].content.is_dimension_score 是否按维度评分
items[].item.question.text items[].content.question.text 题干
items[].item.aspects.maximumMark items[].content.rubric.maximum_mark 本题评分总分
items[].item.aspects.dim[] items[].content.rubric.assessment_items[] 考核项;text 是考核内容描述
aspects.dim[].dim[] assessment_items[].requirements[] 考核要求列表
requirements[].dim[] requirements[].standards[] 评分标准,也是实际扣分记录对象
考核要求 scoringDepth requirements[].deduct_rule 0 -> standard1 -> item
评分标准 maximumMark standards[].maximum_mark 评分标准扣分上限,仅“按照评分标准扣”时使用
step deduction_unit 扣分单位
scoringDepth deduct_scope 评分标准扣分类型:0 -> standard-1 -> requirement1 -> item
aspects.neg.rubric.text rubric.negative.title 否定项描述
aspects.neg.options[] rubric.negative.options[] 子否定项列表
aspects.neg.result[] rubric.negative.selected_option_ids[] 已选中的子否定项