结论:能做。后端加 3 个考评员端接口和 1 个租户开关字段,主要改考评员端的评分页面。数据结构不动。
开工前要产品确认 5 个问题,最关键的是备注、签字和"提交全部时未打完的题怎么办",见待确认问题。
标 v4 的是本次新增或改动:加到所有题、提交全部、租户开关。
背景:在考评现场,一个考评员可能负责整个项目中的某几道题。目前考评员需要退出当前考评界面,再点击其他任务卡片来切换试题,对考生进行不同题的打分,操作重复繁琐。
需求:评阅规则为按题阅时,在考评界面左侧展示他关联的所有试题,可在考评界面直接切换。提交评分时,仍按不同的试题分别提交。
产品已确定:按"题"切换,即左边选题,顶部考生跟着这道题变。
v4 2026-11-12 补充的要求:
按题评时,系统在发布活动时会给每道题单独建一个任务。考评员小王负责第 1、3、5 题,系统里就是 3 个任务,首页就有 3 张卡片。
评分页上几乎所有东西都按"这道题 + 这个考生 + 这个考评员"各存一份。唯一的例外是新加的考生签到状态:它存在考生身上,整场活动只有一份(见第 4 节)。
这带来两个结论:
左边永远只有一题,题号也永远是"试题 1"。要换题只能返回首页点另一张卡片。
虚线框:切题时会跟着变的部分。
把评分页上能点、能看的东西都过了一遍。黄色行需要产品拍板。
| 功能 | 现在按什么存 | 切题后怎么处理 | 需确认 |
|---|---|---|---|
| 顶部考生条 | 按题 | 换成新题已领取的考生。原来选中的考生在新题里也有,就继续选中他。 | 无 |
| 添加考生 v4 | 按题 | 一次加到左边列出的所有题,只加同科目的题。"一次最多领几人"是活动级配置,各题一样;"单人最多评几人"是任务级,每道题分别算。某道题加不上(已被别人领走、超过上限、未签到)时按题列出原因,其他题照常加。 | 已定 |
| 移除考生 | 按题 | 只从当前这道题移除,其他题不受影响。弹窗写明"从第 3 题移除"。 | 无 |
| 打分、否定项 | 按题 | 每次点"保存"、勾否定项都会马上存到服务器,切走不会丢。 | 无 |
| 底部"总分" | 当前题 | 按题评时它其实就是本题得分。左边出现多道题后,容易被理解成几道题的合计,改叫"本题得分"。 | 无 |
| 提交评分 v4 | 按题 | "提交全部":提交当前考生在我负责的所有题里已打完的评分,没打完或没领取的题跳过。确认弹窗先列出"将提交第 1、3 题;第 5 题未评完不提交"。每道题仍是独立的一份提交,后台成绩、签字都不变。 | 问题 6 |
| "提交后不可修改"勾选 | 按题(各题配置一样) | 提交全部时勾一次对所有被提交的题生效。 | 无 |
| 考生签到状态 | 按考生(整场活动一份) | 切题时不变。开启考生拍照签到后,未签到的考生在任何一道题都不能添加、不能提交:添加考生页不能勾选,提交时弹"还未拍照签到"提示,和现在一样。 | 无 |
| 试卷变更重评提示 | 按评分记录(按题) | 跟着新题刷新。提示比较的是整份试卷,试卷改动后,已评过分的各题都会出现提示。 | 无 |
| 备注 | 按题 | 看到和保存的都是当前这道题的备注。第 1 题写的备注,切到第 3 题看不到。 | 问题 1 |
| 签字 | 按题 | 右上角"签字"入口和红点跟着换。签字时只能签当前这道题。 | 问题 2 |
| 人脸核验 | 从首页进入每道题时核验一次 | 页内切题不会触发核验。 | 问题 4 |
| 多科目 | 每个科目一套题和考生 | 切到别的科目的题时,顶部考生会整个换掉。"加到所有题"只加同科目的题。 | 问题 5 |
| 模式开关 v4 | 新增,租户级 | Django admin 按租户开。开着:按题评活动的评分页显示多题列表、批量添加、提交全部。关着:和现在完全一样。开关不改任何数据,活动进行中开或关都安全。 | 已定 |
| 页面标题 | 按题 | 换成新题的名称。 | 无 |
| 返回按钮 | 回任务详情页 | 回到当前切到的那道题的任务详情页。 | 无 |
| 盲评 | 按题(各题配置一样) | 跟着新题配置走,实际不会变。 | 无 |
| 仲裁员 | 按题 | 仲裁也是按题分任务的,一样可以切题。批量添加、提交全部对仲裁员同样按题循环,领取规则走仲裁那一套。 | 无 |
| 已评分列表、查看试题、首页卡片 | 按题 | 不在本次需求里,保持不变。 | 无 |
现在备注跟着"这道题的评分"一起存。管理端的成绩详情、导出的成绩 Excel、考评表 PDF 里,备注也都是按题分别显示的。
现在签字按题算。负责 3 道题就要在 3 道题里各签一次(每次可以同时签多个考生)。
现在每道题要分别领考生。现场一个考生连着考第 1、3、5 题时,考评员要在 3 道题里各领一次。
现在每次从首页点进一道题都会核验,核验记录也是按题存的。页内切题不会经过这一步。
每个科目有自己的试卷和考生。切到别的科目的题时,顶部考生条会整个换掉。
提交全部是把当前考生在我负责的每道题上的评分各提交一次。某道题没打完、或这个考生在那道题还没领取,这道题就提交不了。
以下按"每个问题都选建议项"来写。
(a) 租户开关 v4:Tenant 加布尔字段 practical_item_switch(默认关)+ migration,Django admin 可改。考评员端任务列表/详情返回 item_switch_enabled(按题评 且 开关开)。下面三个接口在后端也校验开关,关着拒绝。
(b) 关联任务查询:GET /dapi/practical/examiner/tasks/{task_id}/related/
id, item_no, item_title, full_score, subject_id, subject_name, scoring_count, completed_count, unsigned_signature_count。get_tasks_by_examiner_ids + batch_get_tasks_statistics。(c) 批量添加考生 v4:POST .../tasks/{task_id}/candidates/batch/,body 带 candidate_ids、task_ids。
add_candidates_to_task,每题各自事务、各自任务行锁,11-10 的并发锁原样复用。[{task_id, item_no, failed_candidates: [{id, reason}]}],科目不符的题标 skipped。_validate_candidate_for_add)只看活动不看科目。加到"所有题"后,多科目活动会把考生加进别科目的题。批量接口先按考生科目过滤任务。
(d) 批量提交评分 v4:POST .../tasks/{task_id}/candidates/{candidate_id}/score/submit-all/,body 带 task_ids。
submit_candidate_score(已是独立事务,含仲裁对账和完成状态同步),一题失败不影响其他。submit_candidate_score 所有失败都返回 None,区分不了"未打完 / 总分不一致 / 未领取"。要让它返回具体原因,单题提交接口对外行为不变。submitted / skipped 及原因。打分、单题提交、备注、签字、移除接口不用改。
ScoreView.vue:按 item_switch_enabled 决定是否拉关联任务。切题时依次加载题目配置(名称、盲评、备注开关、提交锁定、领取上限,现在只在任务详情页加载,要补)、签字配置、考生列表、题目内容和已打分数。原选中考生在新题里也有就保持选中;加载中禁止再切题;切题失败(如被改派走)提示并刷新列表。TeskItems.vue:开关开着时左侧改为"我负责的题"列表(题号、待评人数、当前考生状态)。文案:真实题号、"本题得分"、移除/备注弹窗写明第几题。底部"提交本题"+"提交全部",提交全部先弹确认列出将提交/跳过的题。v4AddExam.vue:列表仍按当前题查;开关开着时调批量接口,结果按题展示。v4v3 之后(11-09 至 11-11)实操考评 13 个提交已逐个对照,方案不用推翻:添加考生的任务行锁通过按题循环直接复用;签到照片、管理端性能优化、权限拆分与本需求无关;考评员前端只改了签到页一行样式。第 9 节列的方法全部仍在。
| 结论 | 位置 |
|---|---|
| 按题评时每道题建一个任务,名称"活动名-第N题" | app/practical/services/management_assessment_service.py _create_tasks / _create_item_task |
| 领取记录唯一键 (任务, 考生, 考评员) | app/practical/models/assignment.py |
| 评分记录唯一键 (任务, 考生, 考评员, 第几次);备注存在评分记录上 | app/practical/models/score.py、task_repository.update_score_record_note |
| 备注在管理端成绩详情、成绩导出、考评表 PDF 中按评分记录展示 | management_score_service.py、management_import_export_service.py、management_sheet_export_service.py |
| 签字配置、未签字数按任务统计 | task_service.get_signature_config |
| 移除考生只释放当前任务的领取记录 | task_service.remove_candidate_from_task |
| 添加考生:锁任务行 → 领取上限按活动、单人上限按任务 → 逐个考生校验单评/多评 v4 | task_service.add_candidates_to_task、_validate_add_candidates_limit、_add_single_candidate |
| 可添加列表按科目过滤,但添加校验不校验科目 v4 | task_repository.query_available_candidates_for_task vs task_service._validate_candidate_for_add |
| 提交评分按任务:锁任务+考生、签到校验、本任务评分项完整性、仲裁对账、完成状态同步;所有失败统一返回 None v4 | task_service.submit_candidate_score |
| 租户级开关先例:布尔字段 + 管理端"考试定制"页读写 v4 | app/tenant/models.py hide_course / auto_add_room、app/tenant/views.py customize 接口、client/manager/src/views/manage/ExamCustomize.vue |
| 考评员端任务配置从任务列表/详情接口取 v4 | task_service.get_examiner_tasks、client/practical-examiner/src/api/index.js mapTask |
| 人脸核验在首页点卡片时触发,记录按任务存,评分接口不校验 | client/practical-examiner/src/views/HomeView.vue toDetail、task_service.record_face_verification |
| 签到状态存在考生上;考生按 (活动, 准考证号) 唯一,整场一份 | app/practical/models/candidate.py checkin_status |
| 未签到考生不能添加、不能提交(均按当前任务调用) | task_service._validate_candidate_for_add、task_service.submit_candidate_score |
| 人脸核验支持按活动存档(task 为空),目前仅签到员使用 | task_service._record_assessment_face_verification |
| 重评提示按评分记录计算,比较整份试卷题目及满分 | task_service._calculate_rubric_changed |
| 任务配置只在任务详情页写入全局状态 | client/practical-examiner/src/views/TaskDetail.vue getData |
| 提交前检查左侧列表全部打完 | client/practical-examiner/src/components/TeskItems.vue submit |
| 打分"保存"、勾否定项立即调用保存接口 | TeskItems.vue scoreOk / checkChange → ScoreView.vue defaultData |
| 添加考生、签字页依赖全局当前任务 ID,并返回评分页 | AddExam.vue、SignList.vue、Sign.vue |