import fs from 'node:fs'; import crypto from 'node:crypto'; import assert from 'node:assert/strict'; import {login,call} from './role-permissions-live.mjs'; const dir='reports/bridge-005-new-model-20260917'; const prior='reports/bridge-005-authoring-20260916'; const read=(p)=>JSON.parse(fs.readFileSync(p,'utf8')); const fixture=read(prior+'/pilot.json'),before=read(prior+'/original-assignments.json'); const session=await login('root'); const api=async endpoint=>{const r=await call('tran/v1/'+endpoint,session.accessToken);assert.equal(r.status,200);return r.data;}; const originals=[]; for(const id of ['86','87','88','89']){const current=await api('teaching/assignments/'+id);delete current.serverNow;delete before[id].serverNow;assert.deepEqual(current,before[id]);originals.push({id,code:current.code,versionId:current.contentVersionId,unchanged:true});} const scene=await api('content/projects/'+fixture.sceneId),training=await api('content/projects/'+fixture.trainingId),task=await api('teaching/assignments/'+fixture.taskId); assert.equal(task.status,'DRAFT');assert.equal(task.contentVersionId,training.project.publishedVersionId); assert.ok(training.currentVersion.dependencies.some(d=>d.targetVersionId===scene.project.publishedVersionId)); const vehicle=scene.currentVersion.content.objects.find(o=>o.sourceName==='0916架桥车.glb'); assert.ok(vehicle);assert.equal(vehicle.modelPartTargets.length,7); assert.equal(scene.currentVersion.content.environment.useModelFloor,true); assert.equal(scene.currentVersion.content.objects.some(o=>o.type==='scene-environment'),false); const asset=scene.currentVersion.assets.find(a=>a.code===vehicle.assetCode); const source=fs.readFileSync('../../模型/0916架桥车.glb'); const installed=fs.readFileSync('../unreal_tran_web/public'+vehicle.resourceUrl); assert.deepEqual(installed,source);assert.equal(asset.sha256,crypto.createHash('sha256').update(source).digest('hex')); const steps=training.currentVersion.content.steps; assert.equal(steps.length,5);assert.equal(steps.flatMap(s=>s.action.mechanism.steps).length,6); for(const step of steps){assert.ok(vehicle.modelPartTargets.some(p=>p.interactionId===step.targetId&&p.sourceNodeName===step.targetRef.sourceNodeName));assert.ok(step.audio.narrationVideoAssetCode&&step.audio.narrationAssetCode);} const live=read(dir+'/runtime-final-lan.json'),regression=read(dir+'/original-005-regression.json'); assert.equal(live.pass,true);assert.equal(regression.pass,true); const audit={pass:true,time:new Date().toISOString(),fixture,model:asset.storageUri,sha256:asset.sha256,targets:vehicle.modelPartTargets,originals,grounding:live.checks.find(c=>c.grounding)?.grounding,media:live.checks.find(c=>c.media)?.media,steps:5,operations:6}; fs.writeFileSync(dir+'/audit.json',JSON.stringify(audit,null,2)); fs.copyFileSync(prior+'/scene-editor.png',dir+'/scene-editor.png');fs.copyFileSync(prior+'/training-editor.png',dir+'/training-editor.png'); fs.writeFileSync(dir+'/index.html',`
${audit.time} · 本地与局域网 · 管理员教员预览
已替换为 0916架桥车.glb,七个目标全部重新绑定。五步六个动作、真实鼠标拖拽、服务端预览校验、数字人视频与语音回退通过。前端构建和新模型几何测试通过。
新增场景资产保留了原运行中的平整作业区,移除重复的基础地面;车轮贴地检查通过。盖板沿新版铰链向外开启,互救手柄向上拨动。
验证任务 ${fixture.taskId} 保持草稿。原四个已发布任务的完整数据逐项对比一致,旧版 005 的完整流程回归通过。未切换原学员任务,未验证新增学员成绩。




