测评平台(二):多资源快照、作答、评分与报告
一次测评可能包含问卷、考试、阅读材料、多个量表和报告模板。运行期间如果每个页面都去读取资源当前版本,后台编辑会让同一参与者前后拿到不同题目。运行单位应当是一个绑定发布包的实例,所有子资源从同一份快照派生。
建立运行实例
任务启动时校验任务状态、访问模式、时间窗和提交令牌,再创建 assessment_run。实例写入任务 ID、release_id、参与者 ID、开始时间和状态。后续查询不再接受客户端选择的资源版本,服务端通过 AssessmentRelease 找到冻结的 FormDSL、题目快照和报告配置。
FormDSL 是输入,不是信任边界
运行服务从冻结快照解析题目类型、选项、必填规则和可见性条件。客户端提交的字段只作为候选值,服务端重新检查题目标识、数据类型、选项范围、矩阵结构和条件规则。题目被删除或版本不匹配时,提交失败并记录具体项,不把未知字段写入答案表。
答案保存分为原始接收和规范化结果。原始请求用于审计时应做长度和字段限制,评分只读取服务端生成的规范化对象。NUMBER 类型的答案使用 numberValue,不能依赖前端展示用的 rawValue。这个区别曾造成草稿试答提交失败,修复点在 FrozenFormAnswerResolver:它从冻结 FormDSL 解析标准值,再交给评分器。
自动保存要可重放
自动保存请求携带 runId、客户端序号和答案补丁。服务端按题目标识合并补丁,并以运行实例版本或乐观锁防止旧请求覆盖新答案。重复请求返回同一版本,网络重试不会产生重复答案记录。保存成功只表示答案落盘,不表示题目已完成或评分已通过。
提交时先锁定运行实例,检查必填项和时间窗,再生成提交答案快照。评分过程读取这份快照,不能一边评分一边继续接受写入。提交失败保留可恢复状态,客户端可以重新加载服务端答案后再次提交。正式提交成功后,运行实例进入不可编辑状态,重评分通过显式操作生成新结果。
评分和报告分离
评分器接收题目快照、规范化答案和评分器版本,输出维度分数、原始分、标准分和诊断标签。报告模板只负责展示和解释,不在模板中重新计算分数。这样可以独立替换展示文案,也能保证重评分使用同一算法版本。
SCL-90 之类的量表必须明确维度映射、题目范围和阈值。测试使用合成的低风险与高风险样本,验证维度状态和总风险标签,不能把真实人员数据写进仓库。报告接口不向浏览器返回标准答案、评分规则或内部判定阈值,客户端只拿展示所需结果。
多资源聚合的失败语义
任务包含多个资源时,每个资源实例保存自己的完成状态和错误。材料加载失败不应覆盖已完成的问卷答案;报告生成失败可以保留评分结果,等待重新生成。聚合状态由明确规则计算,例如全部必需资源完成且报告生成成功才进入 completed。部分失败要带资源 ID 和可重试动作。
管理端加载多个资源摘要时,使用并发上限和 Promise.allSettled,单个资源故障不会让整个工作区变成空白。运行端则按依赖顺序加载,题目快照未到位时不开放作答控件。两侧都要区分加载中、空结果和请求失败。
正确性验证
集成测试覆盖:发布包固定后编辑草稿;客户端篡改 rawValue;NUMBER 使用 numberValue;旧序号自动保存迟到;重复提交;评分器异常;报告重新生成;任务过期和令牌用户不匹配。前端测试要驱动真实组件,单测辅助函数不能证明快速切换资源时页面没有串数据。
这条运行链保证答案和评分读取同一发布快照,不保证评分器本身符合业务口径。业务规则变更需要新评分器版本和差异报告,不能只修改报告模板后重新解释历史结果。