import fs from 'node:fs'; import assert from 'node:assert/strict'; import {createHash} from 'node:crypto'; import {login,call} from './role-permissions-live.mjs'; const dir='reports/bridge-new-models-006-008-20260917',read=name=>JSON.parse(fs.readFileSync(dir+'/'+name+'.json','utf8')); assert.equal(read('list').pass,true,'local and LAN task covers'); assert.equal(read('lan-media-and-005').pass,true,'LAN media playback and unchanged 005 entries'); const session=await login('root'); const api=async url=>{const r=await call('tran/v1/'+url,session.accessToken);assert.equal(r.status,200,r.message);return r.data;}; const tasks=[]; for(const [id,code]of [[87,'006'],[88,'007'],[89,'008']]){ const task=await api('teaching/assignments/'+id),before=read('before-task-'+id),live=read('task-'+code+'-live'); assert.equal(live.pass,true,code+' browser regression');assert.equal(task.status,'PUBLISHED');assert.equal(task.definitionSnapshot.runtime.scriptVersion,'4'); assert.equal(task.code,before.code);assert.equal(task.runCount,before.runCount);assert.deepEqual(task.members,before.members); if(id!==87)assert.deepEqual(task.definitionSnapshot.steps,before.definitionSnapshot.steps,'existing operations and scoring unchanged'); const runs=await api('teaching/runs?assignmentId='+id+'&size=100'); tasks.push({id,code:task.code,name:task.name,versionId:task.contentVersionId,steps:task.definitionSnapshot.steps.length,operations:live.checks.filter(c=>/ step \d+ operation /.test(c.name)).length,members:task.members.length,runCount:task.runCount,runs:runs.records.map(r=>({id:r.id,status:r.status})),preview:'/teaching/virtual-training/preview/'+id}); } for(const id of [86,116]){const before=read('unchanged-'+id),after=await api('teaching/assignments/'+id);delete before.serverNow;delete after.serverNow;assert.deepEqual(after,before);} const models=[]; for(const [source,file]of [['0916架桥车.glb','bridge-erector-20260916.glb'],['0916保障车.glb','bridge-support-20260916.glb'],['双车连接油管.glb','bridge-rescue-hoses-20260916.glb'],['3封口袋.glb','bridge-pump-bags-20260916.glb'],['4封口袋.glb','bridge-valve-bags-20260916.glb'],['牛皮纸.glb','bridge-paper-20260916.glb']]){ const a=fs.readFileSync('../../模型/'+source),b=fs.readFileSync('../unreal_tran_web/public/models/'+file);assert.deepEqual(a,b);models.push({source,url:'/models/'+file,bytes:b.length,sha256:createHash('sha256').update(b).digest('hex')}); } const audit={pass:true,time:new Date().toISOString(),environment:'127.0.0.1:6180 / 192.168.31.168:6180 through Gateway',role:'管理员教员预览',tasks,unchanged:[86,116],models}; fs.writeFileSync(dir+'/audit.json',JSON.stringify(audit,null,2)); const rows=tasks.map(t=>`

`).join('');
fs.writeFileSync(dir+'/index.html',`更新现有任务;005 两个入口未变。新模型来自用户提供文件,保留原始字节;旧内容版本及 v3 定义保留。现有成员、运行记录和成绩规则没有删除或重建。
| 任务 | 名称 | 步骤 | 真实交互 | 内容版本 | 结果 |
|---|
以管理员教员预览通过真实鼠标点击/拖拽、逐步服务端校验完成;预览不写入学员成绩。006 分别操作两个出口球阀,007 拆装四颗真实螺栓,008 完成两侧阀体互换。数字人视频与语音保留。
保障车取力器开关、试验手柄、清洗布、阀座结合面等没有独立精确模型的项目仍标明操作示意。
校验命令:node --test scripts/bridge-new-models.test.mjs;node scripts/virtual-training-formal-route.contract.test.mjs;pnpm run build;mvn -pl ut_tran -Dtest=ScriptedTrainingContractTest,ScriptModelRevisionTest test。数据与文件校验 · 版本迁移。测试没有新增教学任务。
${sections}`); console.log(JSON.stringify(tasks));