Quellcode durchsuchen

test: add bridge content authoring fixtures and acceptance reports

main
leiyun vor 10 Stunden
Ursprung
Commit
73305b8faa
97 geänderte Dateien mit 168050 neuen und 0 gelöschten Zeilen
  1. +7
    -0
      reports/bridge-content-authoring-20260924/README.md
  2. BIN
     
  3. BIN
     
  4. BIN
     
  5. BIN
     
  6. BIN
     
  7. BIN
     
  8. BIN
     
  9. BIN
     
  10. BIN
     
  11. BIN
     
  12. BIN
     
  13. BIN
     
  14. BIN
     
  15. BIN
     
  16. BIN
     
  17. BIN
     
  18. BIN
     
  19. BIN
     
  20. BIN
     
  21. BIN
     
  22. BIN
     
  23. BIN
     
  24. BIN
     
  25. BIN
     
  26. BIN
     
  27. BIN
     
  28. BIN
     
  29. BIN
     
  30. +352
    -0
      reports/bridge-content-authoring-20260924/actions-TASK-VIRTUAL-005.json
  31. +901
    -0
      reports/bridge-content-authoring-20260924/actions-TASK-VIRTUAL-006.json
  32. +1710
    -0
      reports/bridge-content-authoring-20260924/actions-TASK-VIRTUAL-007.json
  33. +3212
    -0
      reports/bridge-content-authoring-20260924/actions-TASK-VIRTUAL-008.json
  34. +6837
    -0
      reports/bridge-content-authoring-20260924/actions-result.json
  35. +2864
    -0
      reports/bridge-content-authoring-20260924/after-model-TASK-VIRTUAL-005.json
  36. +3015
    -0
      reports/bridge-content-authoring-20260924/after-model-TASK-VIRTUAL-006.json
  37. +4360
    -0
      reports/bridge-content-authoring-20260924/after-model-TASK-VIRTUAL-007.json
  38. +6460
    -0
      reports/bridge-content-authoring-20260924/after-model-TASK-VIRTUAL-008.json
  39. +2580
    -0
      reports/bridge-content-authoring-20260924/after-scene-TASK-VIRTUAL-005.json
  40. +2881
    -0
      reports/bridge-content-authoring-20260924/after-scene-TASK-VIRTUAL-006.json
  41. +4226
    -0
      reports/bridge-content-authoring-20260924/after-scene-TASK-VIRTUAL-007.json
  42. +6323
    -0
      reports/bridge-content-authoring-20260924/after-scene-TASK-VIRTUAL-008.json
  43. +3957
    -0
      reports/bridge-content-authoring-20260924/after-training-TASK-VIRTUAL-005.json
  44. +5424
    -0
      reports/bridge-content-authoring-20260924/after-training-TASK-VIRTUAL-006.json
  45. +8434
    -0
      reports/bridge-content-authoring-20260924/after-training-TASK-VIRTUAL-007.json
  46. +13352
    -0
      reports/bridge-content-authoring-20260924/after-training-TASK-VIRTUAL-008.json
  47. +322
    -0
      reports/bridge-content-authoring-20260924/before-list-MODEL.json
  48. +347
    -0
      reports/bridge-content-authoring-20260924/before-list-SCENE.json
  49. +1329
    -0
      reports/bridge-content-authoring-20260924/before-list-TRAINING.json
  50. +1377
    -0
      reports/bridge-content-authoring-20260924/before-project-438.json
  51. +1686
    -0
      reports/bridge-content-authoring-20260924/before-project-439.json
  52. +2299
    -0
      reports/bridge-content-authoring-20260924/before-project-440.json
  53. +3058
    -0
      reports/bridge-content-authoring-20260924/before-project-441.json
  54. +2449
    -0
      reports/bridge-content-authoring-20260924/before-project-458.json
  55. +2255
    -0
      reports/bridge-content-authoring-20260924/before-task-116.json
  56. +1293
    -0
      reports/bridge-content-authoring-20260924/before-task-86.json
  57. +1507
    -0
      reports/bridge-content-authoring-20260924/before-task-87.json
  58. +2045
    -0
      reports/bridge-content-authoring-20260924/before-task-88.json
  59. +2689
    -0
      reports/bridge-content-authoring-20260924/before-task-89.json
  60. BIN
     
  61. +92
    -0
      reports/bridge-content-authoring-20260924/failure.json
  62. BIN
     
  63. +3244
    -0
      reports/bridge-content-authoring-20260924/final-model-TASK-VIRTUAL-005.json
  64. +3304
    -0
      reports/bridge-content-authoring-20260924/final-model-TASK-VIRTUAL-006.json
  65. +4649
    -0
      reports/bridge-content-authoring-20260924/final-model-TASK-VIRTUAL-007.json
  66. +6704
    -0
      reports/bridge-content-authoring-20260924/final-model-TASK-VIRTUAL-008.json
  67. +2966
    -0
      reports/bridge-content-authoring-20260924/final-scene-TASK-VIRTUAL-005.json
  68. +3194
    -0
      reports/bridge-content-authoring-20260924/final-scene-TASK-VIRTUAL-006.json
  69. +4539
    -0
      reports/bridge-content-authoring-20260924/final-scene-TASK-VIRTUAL-007.json
  70. +6585
    -0
      reports/bridge-content-authoring-20260924/final-scene-TASK-VIRTUAL-008.json
  71. +4345
    -0
      reports/bridge-content-authoring-20260924/final-training-TASK-VIRTUAL-005.json
  72. +5737
    -0
      reports/bridge-content-authoring-20260924/final-training-TASK-VIRTUAL-006.json
  73. +8747
    -0
      reports/bridge-content-authoring-20260924/final-training-TASK-VIRTUAL-007.json
  74. +13614
    -0
      reports/bridge-content-authoring-20260924/final-training-TASK-VIRTUAL-008.json
  75. +56
    -0
      reports/bridge-content-authoring-20260924/fixture.json
  76. +22
    -0
      reports/bridge-content-authoring-20260924/health.json
  77. +10
    -0
      reports/bridge-content-authoring-20260924/index.html
  78. +207
    -0
      reports/bridge-content-authoring-20260924/integration-result.json
  79. BIN
     
  80. BIN
     
  81. BIN
     
  82. BIN
     
  83. BIN
     
  84. BIN
     
  85. BIN
     
  86. BIN
     
  87. BIN
     
  88. BIN
     
  89. +65
    -0
      reports/bridge-content-authoring-20260924/registration.json
  90. BIN
     
  91. +29
    -0
      reports/bridge-content-authoring-20260924/ui-lan-result.json
  92. +104
    -0
      reports/bridge-content-authoring-20260924/ui-result.json
  93. +49
    -0
      tools/bridge-content-actions-live.mjs
  94. +80
    -0
      tools/bridge-content-authoring-live.mjs
  95. +24
    -0
      tools/bridge-content-authoring-report.mjs
  96. +91
    -0
      tools/bridge-content-authoring.mjs
  97. +42
    -0
      tools/bridge-content-integration-live.mjs

+ 7
- 0
reports/bridge-content-authoring-20260924/README.md Datei anzeigen

@@ -0,0 +1,7 @@
# 005~008 内容制作全流程

打开 [验收报告](index.html) 查看入口、演示步骤和截图。

已建立 4 个模型、4 个场景,关联 4 个编排;38 步、98 个动作。模型/场景/编排发布不更新现有教学任务。005 采用新模型制作,原任务与独立入口保留。

验证:25 项前端测试、440 项后端测试、四套发布链、98 个动作和局域网 1366×768 页面通过。详细字段与回退说明见 API Wiki《41-桥梁内容制作关联》。





























+ 352
- 0
reports/bridge-content-authoring-20260924/actions-TASK-VIRTUAL-005.json Datei anzeigen

@@ -0,0 +1,352 @@
[
{
"id": "front-pump-step-01:1",
"stepCode": "front-pump-step-01",
"stepIndex": 0,
"operationIndex": 0,
"stepTitle": "根据故障现象确定是哪一个液压泵发生故障",
"title": "点击并确认发生故障的液压泵",
"targetId": "front-pump-hydraulic-pump",
"operation": {
"targetId": "front-pump-hydraulic-pump",
"axis": [
0,
1,
0
],
"toleranceDegrees": 6,
"directionHint": "点击并确认发生故障的液压泵"
},
"motion": {
"kind": "state",
"axis": [
0,
1,
0
],
"pivot": [
0.000013113021848809584,
1.5521701574325566,
2.9428284168243413
],
"pivotMode": "hinge",
"angle": 0,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "液压泵",
"sourceNode": "1_24柱塞泵3_05前泵3_08新泵",
"origin": "model",
"pivot": [
0.000013113021848809584,
1.5521701574325566,
2.9428284168243413
],
"center": [
0.000013113021848809584,
1.5521701574325566,
2.9428284168243413
]
}
},
{
"id": "front-pump-step-02:1",
"stepCode": "front-pump-step-02",
"stepIndex": 1,
"operationIndex": 0,
"stepTitle": "断开架桥车取力器",
"title": "向断开方向摇动",
"targetId": "front-pump-pto-switch",
"operation": {
"targetId": "front-pump-pto-switch",
"axis": [
1,
0,
0
],
"deltaDegrees": -30,
"toleranceDegrees": 6,
"directionHint": "向断开方向摇动"
},
"motion": {
"kind": "rotate",
"axis": [
1,
0,
0
],
"pivot": [
0.000015258789048289145,
-4.159996032714844,
9.5367431640625e-7
],
"pivotMode": "hinge",
"angle": -30,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "取力器开关",
"sourceNode": "1_25和1_27和2_03和2_08取力器开关",
"origin": "model",
"pivot": [
0.000015258789048289145,
-4.159996032714844,
9.5367431640625e-7
],
"center": [
0.000015258789055394573,
0.0000209808349609375,
9.5367431640625e-7
]
}
},
{
"id": "front-pump-step-03:1",
"stepCode": "front-pump-step-03",
"stepIndex": 2,
"operationIndex": 0,
"stepTitle": "将架桥车备胎斜上方链接两个红色滤油器的球阀打开",
"title": "由垂直与官道方向旋转至平行于管道方向",
"targetId": "front-pump-ball-valve-switch",
"operation": {
"targetId": "front-pump-ball-valve-switch",
"axis": [
0,
1,
0
],
"deltaDegrees": 90,
"toleranceDegrees": 8,
"directionHint": "由垂直与官道方向旋转至平行于管道方向"
},
"motion": {
"kind": "rotate",
"axis": [
0,
1,
0
],
"pivot": [
-13599.449218749998,
86314.99218750001,
7118.238037109375
],
"pivotMode": "hinge",
"angle": 90,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "球阀开关",
"sourceNode": "1_26球阀开关",
"origin": "model",
"pivot": [
-13599.449218749998,
86314.99218750001,
7118.238037109375
],
"center": [
-12266.56787109375,
86314.99218750001,
7118.238037109375
]
}
},
{
"id": "front-pump-step-04:1",
"stepCode": "front-pump-step-04",
"stepIndex": 3,
"operationIndex": 0,
"stepTitle": "重新联通取力器",
"title": "按步骤2的相反方向摇回去",
"targetId": "front-pump-pto-switch",
"operation": {
"targetId": "front-pump-pto-switch",
"axis": [
1,
0,
0
],
"deltaDegrees": 30,
"toleranceDegrees": 6,
"directionHint": "按步骤2的相反方向摇回去"
},
"motion": {
"kind": "rotate",
"axis": [
1,
0,
0
],
"pivot": [
0.000015258789048289145,
-4.159996032714844,
9.5367431640625e-7
],
"pivotMode": "hinge",
"angle": 30,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "取力器开关",
"sourceNode": "1_25和1_27和2_03和2_08取力器开关",
"origin": "model",
"pivot": [
0.000015258789048289145,
-4.159996032714844,
9.5367431640625e-7
],
"center": [
0.000015258789055394573,
0.0000209808349609375,
9.5367431640625e-7
]
}
},
{
"id": "front-pump-step-05:1",
"stepCode": "front-pump-step-05",
"stepIndex": 4,
"operationIndex": 0,
"stepTitle": "打开辅助控制盒内的“互救”开关",
"title": "沿控制盒盖边缘铰链打开",
"targetId": "front-pump-aux-control-box-cover",
"operation": {
"targetId": "front-pump-aux-control-box-cover",
"axis": [
0,
1,
0
],
"deltaDegrees": 100,
"toleranceDegrees": 8,
"directionHint": "沿控制盒盖边缘铰链打开",
"pivotEdge": {
"space": "world",
"axis": [
0,
1,
0
],
"edge": "nearest-vertical-edge"
}
},
"motion": {
"kind": "rotate",
"axis": [
-2.568113739801058e-16,
-1,
-2.5291018817332963e-16
],
"pivot": [
0,
0,
0
],
"pivotMode": "hinge",
"angle": 100,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "辅助控制盒盖",
"sourceNode": "1_28辅助控制盒盖",
"origin": "model",
"pivot": [
0,
0,
0
],
"center": [
-0.9138641357421875,
-9.23793029785157,
-9.5367431640625e-7
]
}
},
{
"id": "front-pump-step-05:2",
"stepCode": "front-pump-step-05",
"stepIndex": 4,
"operationIndex": 1,
"stepTitle": "打开辅助控制盒内的“互救”开关",
"title": "往上搬",
"targetId": "front-pump-rescue-switch",
"operation": {
"targetId": "front-pump-rescue-switch",
"axis": [
0,
0,
1
],
"deltaDegrees": 30,
"toleranceDegrees": 6,
"directionHint": "往上搬"
},
"motion": {
"kind": "rotate",
"axis": [
6.098689649701348e-32,
2.5291018817332963e-16,
-1
],
"pivot": [
-0.000002145767204808635,
-0.000002145767204808635,
4.76837158203125e-7
],
"pivotMode": "hinge",
"angle": 30,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "互救开关",
"sourceNode": "1_28和2_06互救开关",
"origin": "model",
"pivot": [
-0.000002145767204808635,
-0.000002145767204808635,
4.76837158203125e-7
],
"center": [
-0.5494954586028982,
-0.000002145767204808635,
4.76837158203125e-7
]
}
}
]

+ 901
- 0
reports/bridge-content-authoring-20260924/actions-TASK-VIRTUAL-006.json Datei anzeigen

@@ -0,0 +1,901 @@
[
{
"id": "BRIDGE-STEP-1:1",
"stepCode": "BRIDGE-STEP-1",
"stepIndex": 0,
"operationIndex": 0,
"stepTitle": "保持架桥车运行并断开取力器",
"title": "保持架桥车发动机运行",
"targetId": "bridge-engine",
"operation": {
"targetId": "bridge-engine",
"kind": "identify",
"directionHint": "保持架桥车发动机运行",
"effects": [
{
"targetId": "bridge-engine",
"color": 2209160
}
]
},
"motion": {
"kind": "state",
"axis": [
0,
1,
0
],
"pivot": [
1.4901161221603232e-8,
0,
-5.960464477539063e-8
],
"pivotMode": "hinge",
"angle": 0,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "架桥车发动机",
"sourceNode": "发动机",
"origin": "model",
"pivot": [
1.4901161221603232e-8,
0,
-5.960464477539063e-8
],
"center": [
1.4901161221603232e-8,
0,
-5.960464477539063e-8
]
}
},
{
"id": "BRIDGE-STEP-1:2",
"stepCode": "BRIDGE-STEP-1",
"stepIndex": 0,
"operationIndex": 1,
"stepTitle": "保持架桥车运行并断开取力器",
"title": "断开架桥车取力器",
"targetId": "front-pump-pto-switch",
"operation": {
"targetId": "front-pump-pto-switch",
"kind": "rotate",
"axis": [
1,
0,
0
],
"deltaDegrees": -30,
"toleranceDegrees": 8,
"directionHint": "断开架桥车取力器"
},
"motion": {
"kind": "rotate",
"axis": [
1,
0,
0
],
"pivot": [
0.0000152587890625,
-4.159996032714844,
9.536743448279594e-7
],
"pivotMode": "hinge",
"angle": -30,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "取力器开关",
"sourceNode": "1_25和1_27和2_03和2_08取力器开关",
"origin": "model",
"pivot": [
0.0000152587890625,
-4.159996032714844,
9.536743448279594e-7
],
"center": [
0.0000152587890625,
0.0000209808349609375,
9.536743306171047e-7
]
}
},
{
"id": "BRIDGE-STEP-2:1",
"stepCode": "BRIDGE-STEP-2",
"stepIndex": 1,
"operationIndex": 0,
"stepTitle": "连接两车液压软管",
"title": "打开架桥车快速接口防尘盖",
"targetId": "bridge-hose-a",
"operation": {
"targetId": "bridge-hose-a",
"kind": "identify",
"directionHint": "打开架桥车快速接口防尘盖",
"effects": [
{
"targetId": "bridge-hose-a",
"offset": [
0,
0,
-0.7
]
}
]
},
"motion": {
"kind": "translate",
"axis": [
0,
1,
0
],
"pivot": [
1.1175870895385742e-8,
3.7252904094842165e-9,
-3.725290298461914e-9
],
"pivotMode": "hinge",
"angle": 0,
"offset": [
-0.06,
0,
0.09
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "架桥车快速接口盖",
"sourceNode": "2_04架桥车接口盖子",
"origin": "model",
"pivot": [
1.1175870895385742e-8,
3.7252904094842165e-9,
-3.725290298461914e-9
],
"center": [
1.1175870895385742e-8,
3.7252904094842165e-9,
-3.725290298461914e-9
]
}
},
{
"id": "BRIDGE-STEP-2:2",
"stepCode": "BRIDGE-STEP-2",
"stepIndex": 1,
"operationIndex": 1,
"stepTitle": "连接两车液压软管",
"title": "打开保障车快速接口防尘盖",
"targetId": "bridge-hose-b",
"operation": {
"targetId": "bridge-hose-b",
"kind": "identify",
"directionHint": "打开保障车快速接口防尘盖",
"effects": [
{
"targetId": "bridge-hose-b",
"offset": [
0,
0,
-0.7
]
}
]
},
"motion": {
"kind": "translate",
"axis": [
0,
1,
0
],
"pivot": [
-1.1548399925231934e-7,
1.862645149230957e-8,
-7.450580596923828e-9
],
"pivotMode": "hinge",
"angle": 0,
"offset": [
-0.06,
0,
0.09
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "保障车快速接口盖",
"sourceNode": "2_04保障车接口盖子",
"origin": "model",
"pivot": [
-1.1548399925231934e-7,
1.862645149230957e-8,
-7.450580596923828e-9
],
"center": [
-1.1548399925231934e-7,
1.862645149230957e-8,
-7.450580596923828e-9
]
}
},
{
"id": "BRIDGE-STEP-2:3",
"stepCode": "BRIDGE-STEP-2",
"stepIndex": 1,
"operationIndex": 2,
"stepTitle": "连接两车液压软管",
"title": "连接供油软管架桥车端接头",
"targetId": "bridge-hose-line-a",
"operation": {
"targetId": "bridge-hose-line-a",
"kind": "identify",
"directionHint": "连接供油软管架桥车端接头",
"effects": [
{
"targetId": "bridge-hose-line-a",
"offset": [
0,
0,
0
]
}
]
},
"motion": {
"kind": "translate",
"axis": [
0,
1,
0
],
"pivot": [
0,
0,
0
],
"pivotMode": "hinge",
"angle": 0,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "供油软管 · 架桥车端接头",
"sourceNode": "2_04油罐连接线大",
"origin": "model",
"pivot": [
0,
0,
0
],
"center": [
0,
0,
0
]
}
},
{
"id": "BRIDGE-STEP-2:4",
"stepCode": "BRIDGE-STEP-2",
"stepIndex": 1,
"operationIndex": 3,
"stepTitle": "连接两车液压软管",
"title": "连接供油软管保障车端接头",
"targetId": "bridge-hose-line-b",
"operation": {
"targetId": "bridge-hose-line-b",
"kind": "identify",
"directionHint": "连接供油软管保障车端接头",
"effects": [
{
"targetId": "bridge-hose-line-b",
"offset": [
0,
0,
0
]
}
]
},
"motion": {
"kind": "translate",
"axis": [
0,
1,
0
],
"pivot": [
0,
0,
0
],
"pivotMode": "hinge",
"angle": 0,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "供油软管 · 保障车端接头",
"sourceNode": "2_04油罐连接线大",
"origin": "model",
"pivot": [
0,
0,
0
],
"center": [
0,
0,
0
]
}
},
{
"id": "BRIDGE-STEP-2:5",
"stepCode": "BRIDGE-STEP-2",
"stepIndex": 1,
"operationIndex": 4,
"stepTitle": "连接两车液压软管",
"title": "连接回油软管架桥车端接头",
"targetId": "bridge-return-line-a",
"operation": {
"targetId": "bridge-return-line-a",
"kind": "identify",
"directionHint": "连接回油软管架桥车端接头",
"effects": [
{
"targetId": "bridge-return-line-a",
"offset": [
0,
0,
0
]
}
]
},
"motion": {
"kind": "translate",
"axis": [
0,
1,
0
],
"pivot": [
0,
0,
0
],
"pivotMode": "hinge",
"angle": 0,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "回油软管 · 架桥车端接头",
"sourceNode": "2_04油罐连接线小",
"origin": "model",
"pivot": [
0,
0,
0
],
"center": [
0,
0,
0
]
}
},
{
"id": "BRIDGE-STEP-2:6",
"stepCode": "BRIDGE-STEP-2",
"stepIndex": 1,
"operationIndex": 5,
"stepTitle": "连接两车液压软管",
"title": "连接回油软管保障车端接头",
"targetId": "bridge-return-line-b",
"operation": {
"targetId": "bridge-return-line-b",
"kind": "identify",
"directionHint": "连接回油软管保障车端接头",
"effects": [
{
"targetId": "bridge-return-line-b",
"offset": [
0,
0,
0
]
}
]
},
"motion": {
"kind": "translate",
"axis": [
0,
1,
0
],
"pivot": [
0,
0,
0
],
"pivotMode": "hinge",
"angle": 0,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "回油软管 · 保障车端接头",
"sourceNode": "2_04油罐连接线小",
"origin": "model",
"pivot": [
0,
0,
0
],
"center": [
0,
0,
0
]
}
},
{
"id": "BRIDGE-STEP-3:1",
"stepCode": "BRIDGE-STEP-3",
"stepIndex": 2,
"operationIndex": 0,
"stepTitle": "打开支援车回油截止阀",
"title": "打开保障车回油截止阀",
"targetId": "bridge-return-valve",
"operation": {
"targetId": "bridge-return-valve",
"kind": "rotate",
"axis": [
0,
1,
0
],
"deltaDegrees": 90,
"toleranceDegrees": 8,
"directionHint": "打开保障车回油截止阀"
},
"motion": {
"kind": "rotate",
"axis": [
0,
1,
0
],
"pivot": [
5.541369318962097e-8,
-1.3969838619232178e-9,
-0.031092071905732155
],
"pivotMode": "hinge",
"angle": 90,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "保障车回油截止阀",
"sourceNode": "2_05回油截止阀",
"origin": "model",
"pivot": [
5.541369318962097e-8,
-1.3969838619232178e-9,
-0.031092071905732155
],
"center": [
5.541369318962097e-8,
-1.3969838619232178e-9,
5.587935447692871e-9
]
}
},
{
"id": "BRIDGE-STEP-4:1",
"stepCode": "BRIDGE-STEP-4",
"stepIndex": 3,
"operationIndex": 0,
"stepTitle": "打开互救作业开关",
"title": "打开辅助控制盒盖",
"targetId": "front-pump-aux-control-box-cover",
"operation": {
"targetId": "front-pump-aux-control-box-cover",
"kind": "rotate",
"axis": [
0,
1,
0
],
"pivotEdge": {
"space": "world",
"axis": [
0,
1,
0
],
"edge": "nearest-vertical-edge"
},
"deltaDegrees": 100,
"toleranceDegrees": 8,
"directionHint": "打开辅助控制盒盖"
},
"motion": {
"kind": "rotate",
"axis": [
-1.691878294677667e-18,
-1,
-4.505224149471667e-16
],
"pivot": [
-7.105427357601002e-15,
0,
0
],
"pivotMode": "hinge",
"angle": 100,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "辅助控制盒盖",
"sourceNode": "1_28辅助控制盒盖",
"origin": "model",
"pivot": [
-7.105427357601002e-15,
0,
0
],
"center": [
-0.9138641357421875,
-9.237930297851566,
-9.5367431640625e-7
]
}
},
{
"id": "BRIDGE-STEP-4:2",
"stepCode": "BRIDGE-STEP-4",
"stepIndex": 3,
"operationIndex": 1,
"stepTitle": "打开互救作业开关",
"title": "打开互救作业开关",
"targetId": "front-pump-rescue-switch",
"operation": {
"targetId": "front-pump-rescue-switch",
"kind": "rotate",
"axis": [
0,
0,
1
],
"deltaDegrees": 30,
"toleranceDegrees": 8,
"directionHint": "打开互救作业开关"
},
"motion": {
"kind": "rotate",
"axis": [
5.689507713189075e-34,
4.505224149471667e-16,
-1
],
"pivot": [
-0.0000021457672261249172,
-0.00000214576721901949,
4.76837158203125e-7
],
"pivotMode": "hinge",
"angle": 30,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "互救开关",
"sourceNode": "1_28和2_06互救开关",
"origin": "model",
"pivot": [
-0.0000021457672261249172,
-0.00000214576721901949,
4.76837158203125e-7
],
"center": [
-0.5494954586029053,
-0.0000021457672119140625,
4.76837158203125e-7
]
}
},
{
"id": "BRIDGE-STEP-5:1",
"stepCode": "BRIDGE-STEP-5",
"stepIndex": 4,
"operationIndex": 0,
"stepTitle": "打开滤油器出口连通球阀",
"title": "打开第 1 个滤油器出口球阀",
"targetId": "bridge-filter-valve-1",
"operation": {
"targetId": "bridge-filter-valve-1",
"kind": "rotate",
"axis": [
0,
1,
0
],
"deltaDegrees": 90,
"toleranceDegrees": 8,
"directionHint": "打开第 1 个滤油器出口球阀"
},
"motion": {
"kind": "rotate",
"axis": [
0,
1,
0
],
"pivot": [
2.7939677238464355e-9,
-3.725290298461914e-9,
-0.05009720101952553
],
"pivotMode": "hinge",
"angle": 90,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "滤油器出口球阀 1",
"sourceNode": "2_07球阀开关01",
"origin": "model",
"pivot": [
2.7939677238464355e-9,
-3.725290298461914e-9,
-0.05009720101952553
],
"center": [
2.7939677238464355e-9,
-3.725290298461914e-9,
1.862645149230957e-9
]
}
},
{
"id": "BRIDGE-STEP-5:2",
"stepCode": "BRIDGE-STEP-5",
"stepIndex": 4,
"operationIndex": 1,
"stepTitle": "打开滤油器出口连通球阀",
"title": "打开第 2 个滤油器出口球阀",
"targetId": "bridge-filter-valve-2",
"operation": {
"targetId": "bridge-filter-valve-2",
"kind": "rotate",
"axis": [
0,
1,
0
],
"deltaDegrees": 90,
"toleranceDegrees": 8,
"directionHint": "打开第 2 个滤油器出口球阀"
},
"motion": {
"kind": "rotate",
"axis": [
0,
1,
0
],
"pivot": [
9.313225746154785e-9,
1.862645149230957e-8,
-0.05009720101952553
],
"pivotMode": "hinge",
"angle": 90,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "滤油器出口球阀 2",
"sourceNode": "2_07球阀开关02和3-02球阀开关",
"origin": "model",
"pivot": [
9.313225746154785e-9,
1.862645149230957e-8,
-0.05009720101952553
],
"center": [
9.313225746154785e-9,
1.862645149230957e-8,
0
]
}
},
{
"id": "BRIDGE-STEP-6:1",
"stepCode": "BRIDGE-STEP-6",
"stepIndex": 5,
"operationIndex": 0,
"stepTitle": "接通支援车液压取力器",
"title": "接通保障车取力器",
"targetId": "bridge-support-pto",
"operation": {
"targetId": "bridge-support-pto",
"kind": "rotate",
"axis": [
0,
1,
0
],
"deltaDegrees": 30,
"toleranceDegrees": 8,
"directionHint": "接通保障车取力器"
},
"motion": {
"kind": "rotate",
"axis": [
0,
1,
0
],
"pivot": [
0,
0,
0
],
"pivotMode": "hinge",
"angle": 30,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "保障车取力器开关(操作示意)",
"sourceNode": "保障车取力器开关(操作示意)",
"origin": "supplement",
"pivot": [
0,
0,
0
],
"center": [
0,
-0.015000000189989815,
0.00474999985098834
]
}
},
{
"id": "BRIDGE-STEP-6:2",
"stepCode": "BRIDGE-STEP-6",
"stepIndex": 5,
"operationIndex": 1,
"stepTitle": "接通支援车液压取力器",
"title": "观察供油示意",
"targetId": "bridge-supply",
"operation": {
"targetId": "bridge-supply",
"kind": "identify",
"directionHint": "观察供油示意",
"effects": [
{
"targetId": "bridge-hose-line",
"color": 15312947
},
{
"targetId": "bridge-return-line",
"color": 3325905
}
]
},
"motion": {
"kind": "state",
"axis": [
0,
1,
0
],
"pivot": [
0,
0,
0
],
"pivotMode": "hinge",
"angle": 0,
"offset": [
0,
0,
0
],
"durationMs": 900,
"easing": "easeInOut"
},
"binding": {
"name": "供油回路观察点",
"sourceNode": "供油回路观察点",
"origin": "supplement",
"pivot": [
0,
0,
0
],
"center": [
0,
0,
0
]
}
}
]

+ 1710
- 0
reports/bridge-content-authoring-20260924/actions-TASK-VIRTUAL-007.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 3212
- 0
reports/bridge-content-authoring-20260924/actions-TASK-VIRTUAL-008.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 6837
- 0
reports/bridge-content-authoring-20260924/actions-result.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 2864
- 0
reports/bridge-content-authoring-20260924/after-model-TASK-VIRTUAL-005.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 3015
- 0
reports/bridge-content-authoring-20260924/after-model-TASK-VIRTUAL-006.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 4360
- 0
reports/bridge-content-authoring-20260924/after-model-TASK-VIRTUAL-007.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 6460
- 0
reports/bridge-content-authoring-20260924/after-model-TASK-VIRTUAL-008.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 2580
- 0
reports/bridge-content-authoring-20260924/after-scene-TASK-VIRTUAL-005.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 2881
- 0
reports/bridge-content-authoring-20260924/after-scene-TASK-VIRTUAL-006.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 4226
- 0
reports/bridge-content-authoring-20260924/after-scene-TASK-VIRTUAL-007.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 6323
- 0
reports/bridge-content-authoring-20260924/after-scene-TASK-VIRTUAL-008.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 3957
- 0
reports/bridge-content-authoring-20260924/after-training-TASK-VIRTUAL-005.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 5424
- 0
reports/bridge-content-authoring-20260924/after-training-TASK-VIRTUAL-006.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 8434
- 0
reports/bridge-content-authoring-20260924/after-training-TASK-VIRTUAL-007.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 13352
- 0
reports/bridge-content-authoring-20260924/after-training-TASK-VIRTUAL-008.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 322
- 0
reports/bridge-content-authoring-20260924/before-list-MODEL.json Datei anzeigen

@@ -0,0 +1,322 @@
{
"records": [
{
"id": "416",
"type": "MODEL",
"code": "MODEL-DA3BF394D66E48A3",
"name": "动画能力验收-组合动作与流向-20260912",
"description": "轻量 GLB 实测:车辆多部件、节点编辑、车轮动画、保存重开和发布。保留供后续模型/场景测试使用。",
"categoryCode": "EQUIPMENT",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/12/e1169bfa34a99afa9b1e84ad9523efd1.jpg",
"status": "DRAFT",
"ownerUserId": "99",
"ownerName": "内部维护",
"departmentId": "1",
"currentVersionId": "504",
"currentVersionNo": 2,
"currentVersionCode": "V2.0.0",
"publishedVersionId": "479",
"publishedTime": 1789202700,
"usageCount": 14,
"version": 10,
"addTime": 1789202691,
"updateTime": "2026-09-23T22:48:47",
"contentSummary": {
"schema": "digital-twin-editor-project",
"resourceUri": "/models/bridge-erector-vehicle.glb",
"nodeCount": 186,
"materialCount": 114,
"animationCount": 12
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 0,
"assetCount": 3,
"readCount": 0,
"downloadCount": 0
},
{
"id": "407",
"type": "MODEL",
"code": "MODEL-84E00933755E4D75",
"name": "ZDS-模型1",
"description": "ZDS-模型1",
"categoryCode": "EQUIPMENT",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/06/df6453b974c51518a1f78ee1dca67c0d.jpg",
"status": "DRAFT",
"ownerUserId": "68",
"ownerName": "沈君瑶",
"departmentId": "5",
"currentVersionId": "462",
"currentVersionNo": 2,
"currentVersionCode": "V2.0.0",
"publishedVersionId": "461",
"publishedTime": 1788699921,
"usageCount": 3,
"version": 36,
"addTime": 1788699642,
"updateTime": "2026-09-23T22:48:47",
"contentSummary": {
"schema": "digital-twin-editor-project",
"resourceUri": "fsvc://unreal-tran/model/2026/09/06/8f9816c2e66e7d64ff98abb35accb245.glb",
"nodeCount": 3,
"materialCount": 1,
"animationCount": 1
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 0,
"assetCount": 12,
"readCount": 0,
"downloadCount": 0
},
{
"id": "157",
"type": "MODEL",
"code": "MODEL-70875AA932B94685",
"name": "实测模型-狐狸动画-已验证-20260905",
"description": "轻量 GLB 实测:骨骼蒙皮、Survey/Walk/Run 动画、节点编辑、保存重开和发布。保留供后续模型/场景测试使用。",
"categoryCode": "EQUIPMENT",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/10/5d4501f4d8a2f32a8105a697cf2d0cf1.jpg",
"status": "DRAFT",
"ownerUserId": "1",
"ownerName": "系统管理员",
"departmentId": "2",
"currentVersionId": "202",
"currentVersionNo": 3,
"currentVersionCode": "V3.0.0",
"publishedVersionId": "201",
"publishedTime": 1788601684,
"usageCount": 2,
"version": 26,
"addTime": 1788600787,
"updateTime": "2026-09-23T22:48:47",
"contentSummary": {
"schema": "digital-twin-editor-project",
"resourceUri": "fsvc://unreal-tran/model/2026/09/05/3fc69ae0d435f869afca1fcd943486d1.glb",
"nodeCount": 27,
"materialCount": 1,
"animationCount": 1
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 0,
"assetCount": 3,
"readCount": 0,
"downloadCount": 0
},
{
"id": "156",
"type": "MODEL",
"code": "MODEL-3FA24629D59E4509",
"name": "实测模型-牛奶卡车-已验证-20260905",
"description": "轻量 GLB 实测:车辆多部件、节点编辑、车轮动画、保存重开和发布。保留供后续模型/场景测试使用。",
"categoryCode": "EQUIPMENT",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/05/67b2a5d15f9c1fc47a0a760f28fd2e2e.jpg",
"status": "PUBLISHED",
"ownerUserId": "1",
"ownerName": "系统管理员",
"departmentId": "2",
"currentVersionId": "204",
"currentVersionNo": 4,
"currentVersionCode": "V4.0.0",
"publishedVersionId": "204",
"publishedTime": 1788603046,
"usageCount": 2,
"version": 20,
"addTime": 1788600271,
"updateTime": "2026-09-23T22:48:47",
"contentSummary": {
"schema": "digital-twin-editor-project",
"resourceUri": "fsvc://unreal-tran/model/2026/09/05/a75de903d95b0590307632d7bed44111.glb",
"nodeCount": 10,
"materialCount": 5,
"animationCount": 1
},
"currentVersionStatus": "PUBLISHED",
"dependencyCount": 0,
"assetCount": 3,
"readCount": 0,
"downloadCount": 0
},
{
"id": "94",
"type": "MODEL",
"code": "MODEL-BRIDGE-ERECTOR",
"name": "桥梁架设装备整机模型",
"description": "现场正式桥梁架设装备,44 个命名部件与 12 组机构联动时间轴",
"categoryCode": "EQUIPMENT",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/11/e559b1e73e5e0fc7a3fabb96ef380c93.jpg",
"status": "DRAFT",
"ownerUserId": "2",
"ownerName": "周教员",
"departmentId": "3",
"currentVersionId": "503",
"currentVersionNo": 5,
"currentVersionCode": "V5.0.0",
"publishedVersionId": "474",
"publishedTime": 1789139384,
"usageCount": 4,
"version": 270,
"addTime": 1788074748,
"updateTime": "2026-09-23T22:48:47",
"contentSummary": {
"schema": "digital-twin-editor-project",
"resourceUri": "/models/bridge-erector-vehicle.glb",
"nodeCount": 186,
"materialCount": 114,
"animationCount": 12
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 0,
"assetCount": 4,
"readCount": 0,
"downloadCount": 0
},
{
"id": "48",
"type": "MODEL",
"code": "UAT-CONTENT-MODEL-EQUIPMENT",
"name": "UAT 挖掘机装备模型",
"description": "教学实施验收专用模型元数据,不依赖或更新业务模型工程。",
"categoryCode": "EQUIPMENT",
"trainingMode": "",
"coverUri": "/models/excavator-a-thumb.png",
"status": "DRAFT",
"ownerUserId": "1",
"ownerName": "系统管理员",
"departmentId": "3",
"currentVersionId": "54",
"currentVersionNo": 9002,
"currentVersionCode": "V9002.0.0",
"publishedVersionId": "48",
"publishedTime": 1786413935,
"usageCount": 3,
"version": 556,
"addTime": 1786413935,
"updateTime": "2026-09-23T22:48:47",
"contentSummary": {
"schema": "digital-twin-editor-project",
"resourceUri": "/models/excavator-a.glb",
"nodeCount": 5,
"materialCount": 4,
"animationCount": 6
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 0,
"assetCount": 1,
"readCount": 0,
"downloadCount": 0
},
{
"id": "436",
"type": "MODEL",
"code": "MODEL-EB2011E5BFC94A52",
"name": "CL-mode1",
"description": "这是测试数据",
"categoryCode": "PART",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/13/e17e3797f33ab4dd72dafbb2efbd94e6.jpg",
"status": "DRAFT",
"ownerUserId": "68",
"ownerName": "沈君瑶",
"departmentId": "5",
"currentVersionId": "505",
"currentVersionNo": 1,
"currentVersionCode": "V1.0.0",
"publishedVersionId": null,
"publishedTime": null,
"usageCount": 0,
"version": 36,
"addTime": 1789304132,
"updateTime": "2026-09-13T21:23:41",
"contentSummary": {
"schema": "digital-twin-editor-project",
"resourceUri": "fsvc://unreal-tran/model/2026/09/13/ae06b79910fa31172ccf42ad0a963815.glb",
"nodeCount": 3,
"materialCount": 1,
"animationCount": 2
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 0,
"assetCount": 4,
"readCount": 0,
"downloadCount": 0
},
{
"id": "155",
"type": "MODEL",
"code": "MODEL-07082542E60B482D",
"name": "测试",
"description": "",
"categoryCode": "TOOL",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/11/c40d90e85bdede8e3f7bb00d6a7c1520.jpg",
"status": "DRAFT",
"ownerUserId": "1",
"ownerName": "系统管理员",
"departmentId": "2",
"currentVersionId": "197",
"currentVersionNo": 1,
"currentVersionCode": "V1.0.0",
"publishedVersionId": null,
"publishedTime": null,
"usageCount": 0,
"version": 57,
"addTime": 1788596734,
"updateTime": "2026-09-11T23:02:03",
"contentSummary": {
"schema": "digital-twin-editor-project",
"resourceUri": "/models/bridge-erector-vehicle.glb",
"nodeCount": 186,
"materialCount": 114,
"animationCount": 14
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 0,
"assetCount": 2,
"readCount": 0,
"downloadCount": 0
},
{
"id": "151",
"type": "MODEL",
"code": "MODEL-FIELD-TERRAIN-BRIDGE",
"name": "野外山地场景与桥梁架设装备",
"description": "车.glb 整机与真实地形.glb 外部环境组成的模型编辑工程",
"categoryCode": "EQUIPMENT",
"trainingMode": "",
"coverUri": "/models/scene-field-terrain-vehicle-thumb.png",
"status": "PUBLISHED",
"ownerUserId": "2",
"ownerName": "周教员",
"departmentId": "3",
"currentVersionId": "193",
"currentVersionNo": 1,
"currentVersionCode": "V1.0.0",
"publishedVersionId": "193",
"publishedTime": 1788316127,
"usageCount": 1,
"version": 1,
"addTime": 1788316127,
"updateTime": "2026-09-02T11:02:50",
"contentSummary": {
"schema": "digital-twin-editor-project",
"resourceUri": "/models/车.glb",
"nodeCount": 88,
"materialCount": 18,
"animationCount": 12
},
"currentVersionStatus": "PUBLISHED",
"dependencyCount": 0,
"assetCount": 2,
"readCount": 0,
"downloadCount": 0
}
],
"total": 9,
"page": 1,
"size": 100
}

+ 347
- 0
reports/bridge-content-authoring-20260924/before-list-SCENE.json Datei anzeigen

@@ -0,0 +1,347 @@
{
"records": [
{
"id": "160",
"type": "SCENE",
"code": "SCENE-DE5C246982A34F8B",
"name": "实测场景-狐狸野外训练区-20260905",
"description": "使用狐狸模型的已发布版本,验证野外模板、模型引用、保存截图封面、刷新恢复和发布。",
"categoryCode": "WORKSHOP",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/06/6c5a19adbfba0ee118353c8826c45bda.jpg",
"status": "PUBLISHED",
"ownerUserId": "1",
"ownerName": "系统管理员",
"departmentId": "2",
"currentVersionId": "463",
"currentVersionNo": 4,
"currentVersionCode": "V4.0.0",
"publishedVersionId": "463",
"publishedTime": 1788700649,
"usageCount": 4,
"version": 13,
"addTime": 1788605625,
"updateTime": "2026-09-19T18:04:51",
"contentSummary": {
"schema": "edit3dv4.scene",
"sceneType": "WORKSHOP",
"environmentName": "演示 GLB · 独立部件编辑",
"objectCount": 75
},
"currentVersionStatus": "PUBLISHED",
"dependencyCount": 1,
"assetCount": 1,
"readCount": 0,
"downloadCount": 0
},
{
"id": "159",
"type": "SCENE",
"code": "SCENE-7ADDD6B5C3344C5F",
"name": "实测场景-牛奶卡车检验区-20260905",
"description": "使用已发布牛奶卡车模型,验证场景资源引用、变换、保存恢复、场景截图封面及发布。",
"categoryCode": "WORKSHOP",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/05/d29810b273d75e43dff07622424f333f.jpg",
"status": "PUBLISHED",
"ownerUserId": "1",
"ownerName": "系统管理员",
"departmentId": "2",
"currentVersionId": "209",
"currentVersionNo": 2,
"currentVersionCode": "V2.0.0",
"publishedVersionId": "209",
"publishedTime": 1788607948,
"usageCount": 7,
"version": 7,
"addTime": 1788603808,
"updateTime": "2026-09-19T18:04:51",
"contentSummary": {
"schema": "edit3dv4.scene",
"sceneType": "WORKSHOP",
"environmentName": "正式车库建筑体量 · 整机停放与进出场",
"objectCount": 2
},
"currentVersionStatus": "PUBLISHED",
"dependencyCount": 1,
"assetCount": 1,
"readCount": 0,
"downloadCount": 0
},
{
"id": "457",
"type": "SCENE",
"code": "SCENE-EBC79290EA954BBC",
"name": "005 液压泵应急切换(编排验证)",
"description": "独立验证工程;不替换 TASK-VIRTUAL-005 及其发布版本。",
"categoryCode": "MAINTENANCE",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/17/8c59a21c01de3867f2cc4c343e25a8ec.jpg",
"status": "PUBLISHED",
"ownerUserId": "99",
"ownerName": "内部维护",
"departmentId": "1",
"currentVersionId": "551",
"currentVersionNo": 7,
"currentVersionCode": "V7.0.0",
"publishedVersionId": "551",
"publishedTime": 1789655621,
"usageCount": 1,
"version": 20,
"addTime": 1789566525,
"updateTime": "2026-09-18T00:54:09",
"contentSummary": {
"schema": "edit3dv4.scene",
"sceneType": "OUTDOOR",
"environmentName": "正式起伏地形 · 机动架设与野外协同",
"objectCount": 2
},
"currentVersionStatus": "PUBLISHED",
"dependencyCount": 0,
"assetCount": 5,
"readCount": 0,
"downloadCount": 0
},
{
"id": "418",
"type": "SCENE",
"code": "SCENE-CE481C28E3494AC6",
"name": "动画能力验收场景-20260912",
"description": "",
"categoryCode": "WORKSHOP",
"trainingMode": "",
"coverUri": "",
"status": "PUBLISHED",
"ownerUserId": "99",
"ownerName": "内部维护",
"departmentId": "1",
"currentVersionId": "481",
"currentVersionNo": 1,
"currentVersionCode": "V1.0.0",
"publishedVersionId": "481",
"publishedTime": 1789204589,
"usageCount": 12,
"version": 3,
"addTime": 1789204560,
"updateTime": "2026-09-13T09:22:11",
"contentSummary": {
"schema": "unreal-tran.scene",
"sceneType": "workshop",
"environmentName": "动画能力验收场景-20260912",
"objectCount": 1
},
"currentVersionStatus": "PUBLISHED",
"dependencyCount": 1,
"assetCount": 0,
"readCount": 0,
"downloadCount": 0
},
{
"id": "408",
"type": "SCENE",
"code": "SCENE-60BB81B779274E01",
"name": "ZDS-场景1",
"description": "ZDS-场景1ZDS-场景1",
"categoryCode": "WORKSHOP",
"trainingMode": "",
"coverUri": "fsvc://unreal-tran/image/2026/09/12/7a7c3070a4b3e0f6edbae142e7afb417.jpg",
"status": "PUBLISHED",
"ownerUserId": "68",
"ownerName": "沈君瑶",
"departmentId": "5",
"currentVersionId": "477",
"currentVersionNo": 3,
"currentVersionCode": "V3.0.0",
"publishedVersionId": "477",
"publishedTime": 1789183050,
"usageCount": 1,
"version": 8,
"addTime": 1788700908,
"updateTime": "2026-09-12T11:19:09",
"contentSummary": {
"schema": "edit3dv4.scene",
"sceneType": "WORKSHOP",
"environmentName": "正式车库建筑体量 · 整机停放与进出场",
"objectCount": 2
},
"currentVersionStatus": "PUBLISHED",
"dependencyCount": 1,
"assetCount": 1,
"readCount": 0,
"downloadCount": 0
},
{
"id": "131",
"type": "SCENE",
"code": "SCENE-VEHICLE-DEPOT-INSPECTION",
"name": "真实装备车库整机进场点检场景",
"description": "真实车库与桥梁架设装备整机组合场景,包含 12 个可操作部件",
"categoryCode": "WORKSHOP",
"trainingMode": "",
"coverUri": "/models/scene-depot-vehicle-thumb.png",
"status": "DRAFT",
"ownerUserId": "2",
"ownerName": "周教员",
"departmentId": "3",
"currentVersionId": "174",
"currentVersionNo": 2,
"currentVersionCode": "V2.0.0",
"publishedVersionId": "165",
"publishedTime": 1788139208,
"usageCount": 1,
"version": 4,
"addTime": 1788139208,
"updateTime": "2026-09-02T14:48:53",
"contentSummary": {
"schema": "unreal-tran.scene",
"sceneType": "workshop",
"environmentName": "演示维修车间",
"objectCount": 445
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 0,
"assetCount": 2,
"readCount": 0,
"downloadCount": 0
},
{
"id": "152",
"type": "SCENE",
"code": "SCENE-FIELD-TERRAIN-BRIDGE-ERECTOR",
"name": "真实野外山地桥梁架设训练场",
"description": "真实野外山地环境、同一桥梁架设装备整机与 12 个可操作部件",
"categoryCode": "OUTDOOR",
"trainingMode": "",
"coverUri": "/models/scene-field-terrain-vehicle-thumb.png",
"status": "PUBLISHED",
"ownerUserId": "2",
"ownerName": "周教员",
"departmentId": "3",
"currentVersionId": "194",
"currentVersionNo": 1,
"currentVersionCode": "V1.0.0",
"publishedVersionId": "194",
"publishedTime": 1788316127,
"usageCount": 2,
"version": 1,
"addTime": 1788316127,
"updateTime": "2026-09-02T11:02:50",
"contentSummary": {
"schema": "unreal-tran.scene",
"sceneType": "outdoor",
"environmentName": "真实野外山地桥梁架设训练场",
"objectCount": 14
},
"currentVersionStatus": "PUBLISHED",
"dependencyCount": 1,
"assetCount": 0,
"readCount": 0,
"downloadCount": 0
},
{
"id": "49",
"type": "SCENE",
"code": "UAT-CONTENT-SCENE-WORKSHOP",
"name": "UAT 综合检验车间",
"description": "教学实施验收专用车间场景,不覆盖业务场景。",
"categoryCode": "WORKSHOP",
"trainingMode": "",
"coverUri": "/images/outdoor-training-yard.png",
"status": "DRAFT",
"ownerUserId": "1",
"ownerName": "系统管理员",
"departmentId": "3",
"currentVersionId": "67",
"currentVersionNo": 9002,
"currentVersionCode": "V9002.0.0",
"publishedVersionId": "49",
"publishedTime": 1786413935,
"usageCount": 2,
"version": 3,
"addTime": 1786413935,
"updateTime": "2026-08-31T10:42:12",
"contentSummary": {
"schema": "unreal-tran.scene",
"sceneType": "workshop",
"environmentName": "UAT 综合检验车间",
"objectCount": 3
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 0,
"assetCount": 0,
"readCount": 0,
"downloadCount": 0
},
{
"id": "95",
"type": "SCENE",
"code": "SCENE-BRIDGE-ERECTOR-YARD",
"name": "桥梁架设装备架桥作业训练场",
"description": "整机停放位、变幅油缸拆检架与编号零件盘",
"categoryCode": "OUTDOOR",
"trainingMode": "",
"coverUri": "/images/outdoor-training-yard.png",
"status": "DRAFT",
"ownerUserId": "2",
"ownerName": "周教员",
"departmentId": "3",
"currentVersionId": "119",
"currentVersionNo": 2,
"currentVersionCode": "V2.0.0",
"publishedVersionId": "103",
"publishedTime": 1788074748,
"usageCount": 2,
"version": 1,
"addTime": 1788074748,
"updateTime": "2026-08-31T10:34:44",
"contentSummary": {
"schema": "unreal-tran.scene",
"sceneType": "workshop",
"environmentName": "标准维修车间",
"objectCount": 4
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 0,
"assetCount": 1,
"readCount": 0,
"downloadCount": 0
},
{
"id": "50",
"type": "SCENE",
"code": "UAT-CONTENT-SCENE-OUTDOOR",
"name": "UAT 室外对抗训练场",
"description": "教学实施验收专用红蓝对抗场景,不覆盖业务场景。",
"categoryCode": "OUTDOOR",
"trainingMode": "",
"coverUri": "/images/outdoor-training-yard.png",
"status": "DRAFT",
"ownerUserId": "1",
"ownerName": "系统管理员",
"departmentId": "3",
"currentVersionId": "56",
"currentVersionNo": 9002,
"currentVersionCode": "V9002.0.0",
"publishedVersionId": "50",
"publishedTime": 1786413935,
"usageCount": 1,
"version": 5,
"addTime": 1786413935,
"updateTime": "2026-08-11T02:05:35",
"contentSummary": {
"schema": "unreal-tran.scene",
"sceneType": "workshop",
"environmentName": "综合训练车间",
"objectCount": 12
},
"currentVersionStatus": "DRAFT",
"dependencyCount": 1,
"assetCount": 0,
"readCount": 0,
"downloadCount": 0
}
],
"total": 10,
"page": 1,
"size": 100
}

+ 1329
- 0
reports/bridge-content-authoring-20260924/before-list-TRAINING.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 1377
- 0
reports/bridge-content-authoring-20260924/before-project-438.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 1686
- 0
reports/bridge-content-authoring-20260924/before-project-439.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 2299
- 0
reports/bridge-content-authoring-20260924/before-project-440.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 3058
- 0
reports/bridge-content-authoring-20260924/before-project-441.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 2449
- 0
reports/bridge-content-authoring-20260924/before-project-458.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 2255
- 0
reports/bridge-content-authoring-20260924/before-task-116.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 1293
- 0
reports/bridge-content-authoring-20260924/before-task-86.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 1507
- 0
reports/bridge-content-authoring-20260924/before-task-87.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 2045
- 0
reports/bridge-content-authoring-20260924/before-task-88.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 2689
- 0
reports/bridge-content-authoring-20260924/before-task-89.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen



+ 92
- 0
reports/bridge-content-authoring-20260924/failure.json Datei anzeigen

@@ -0,0 +1,92 @@
{
"pass": false,
"message": "expect(page).toHaveURL(expected) failed\n\nExpected pattern: /\\/training-projects\\/438\\/production/\nReceived string: \"http://127.0.0.1:6180/content/scenes/467/production\"\nTimeout: 5000ms\n\nCall log:\n\u001b[2m - Expect \"to.have.url\" with timeout 5000ms\u001b[22m\n\u001b[2m 9 × unexpected value \"http://127.0.0.1:6180/content/scenes/467/production\"\u001b[22m\n",
"checks": [
{
"task": "TASK-VIRTUAL-005",
"stage": "model",
"actions": 6,
"played": [
2,
3,
5,
6
],
"pass": true
},
{
"task": "TASK-VIRTUAL-005",
"stage": "scene",
"pass": true
},
{
"task": "TASK-VIRTUAL-005",
"stage": "training",
"pass": true
},
{
"task": "TASK-VIRTUAL-006",
"stage": "model",
"actions": 15,
"played": [
3,
5,
9
],
"pass": true
},
{
"task": "TASK-VIRTUAL-006",
"stage": "scene",
"pass": true
},
{
"task": "TASK-VIRTUAL-006",
"stage": "training",
"pass": true
},
{
"task": "TASK-VIRTUAL-007",
"stage": "model",
"actions": 27,
"played": [
4,
8,
12
],
"pass": true
},
{
"task": "TASK-VIRTUAL-007",
"stage": "scene",
"pass": true
},
{
"task": "TASK-VIRTUAL-007",
"stage": "training",
"pass": true
},
{
"task": "TASK-VIRTUAL-008",
"stage": "model",
"actions": 50,
"played": [
3,
6,
36
],
"pass": true
},
{
"task": "TASK-VIRTUAL-008",
"stage": "scene",
"pass": true
},
{
"task": "TASK-VIRTUAL-008",
"stage": "training",
"pass": true
}
],
"errors": []
}


+ 3244
- 0
reports/bridge-content-authoring-20260924/final-model-TASK-VIRTUAL-005.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 3304
- 0
reports/bridge-content-authoring-20260924/final-model-TASK-VIRTUAL-006.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 4649
- 0
reports/bridge-content-authoring-20260924/final-model-TASK-VIRTUAL-007.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 6704
- 0
reports/bridge-content-authoring-20260924/final-model-TASK-VIRTUAL-008.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 2966
- 0
reports/bridge-content-authoring-20260924/final-scene-TASK-VIRTUAL-005.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 3194
- 0
reports/bridge-content-authoring-20260924/final-scene-TASK-VIRTUAL-006.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 4539
- 0
reports/bridge-content-authoring-20260924/final-scene-TASK-VIRTUAL-007.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 6585
- 0
reports/bridge-content-authoring-20260924/final-scene-TASK-VIRTUAL-008.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 4345
- 0
reports/bridge-content-authoring-20260924/final-training-TASK-VIRTUAL-005.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 5737
- 0
reports/bridge-content-authoring-20260924/final-training-TASK-VIRTUAL-006.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 8747
- 0
reports/bridge-content-authoring-20260924/final-training-TASK-VIRTUAL-007.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 13614
- 0
reports/bridge-content-authoring-20260924/final-training-TASK-VIRTUAL-008.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 56
- 0
reports/bridge-content-authoring-20260924/fixture.json Datei anzeigen

@@ -0,0 +1,56 @@
{
"items": [
{
"taskId": "86",
"taskCode": "TASK-VIRTUAL-005",
"trainingProjectId": "438",
"modelProjectId": "466",
"sceneProjectId": "467",
"modelVersionId": "575",
"sceneVersionId": "576",
"trainingVersionId": "577",
"steps": 5,
"actions": 6,
"complete": true
},
{
"taskId": "87",
"taskCode": "TASK-VIRTUAL-006",
"trainingProjectId": "439",
"modelProjectId": "468",
"sceneProjectId": "469",
"modelVersionId": "578",
"sceneVersionId": "579",
"trainingVersionId": "580",
"steps": 6,
"actions": 15,
"complete": true
},
{
"taskId": "88",
"taskCode": "TASK-VIRTUAL-007",
"trainingProjectId": "440",
"modelProjectId": "470",
"sceneProjectId": "471",
"modelVersionId": "581",
"sceneVersionId": "582",
"trainingVersionId": "583",
"steps": 11,
"actions": 27,
"complete": true
},
{
"taskId": "89",
"taskCode": "TASK-VIRTUAL-008",
"trainingProjectId": "441",
"modelProjectId": "472",
"sceneProjectId": "473",
"modelVersionId": "584",
"sceneVersionId": "585",
"trainingVersionId": "586",
"steps": 16,
"actions": 50,
"complete": true
}
]
}

+ 22
- 0
reports/bridge-content-authoring-20260924/health.json Datei anzeigen

@@ -0,0 +1,22 @@
[
{
"endpoint": "auth/ping",
"status": 200
},
{
"endpoint": "tran/ping",
"status": 200
},
{
"url": "http://127.0.0.1:6100/actuator/health",
"status": 200
},
{
"url": "http://127.0.0.1:6180/",
"status": 200
},
{
"url": "http://192.168.31.168:6180/",
"status": 200
}
]

+ 10
- 0
reports/bridge-content-authoring-20260924/index.html Datei anzeigen

@@ -0,0 +1,10 @@
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>005~008 内容制作全流程验收</title><style>
*{box-sizing:border-box}body{margin:0;background:#f2f6f5;color:#163c38;font:15px/1.7 "Microsoft YaHei",sans-serif}main{max-width:1480px;margin:auto;padding:36px}header{background:#0c594e;color:white;padding:32px;border-radius:14px}h1{font-size:28px;margin:0}h2{font-size:21px}header p{margin:8px 0 0;color:#d4e9e2}.stats{display:flex;gap:12px;flex-wrap:wrap;margin:22px 0}.stats span{padding:13px 22px;background:white;border:1px solid #ccded8;border-radius:9px}.stats b{font-size:24px;color:#078c70;margin-right:8px}section{background:white;padding:24px;margin:20px 0;border:1px solid #d4e3de;border-radius:12px}table{width:100%;border-collapse:collapse}td,th{border-bottom:1px solid #e0eae7;padding:14px;text-align:left}th{background:#edf5f2}a{color:#087c67;text-decoration:none}a:hover{text-decoration:underline}.gallery{display:grid;grid-template-columns:1fr 1fr;gap:18px}figure{margin:0}figure img{display:block;width:100%;border:1px solid #d3e2dd;border-radius:7px}figcaption{padding:8px 0;color:#607f77}.note{border-left:4px solid #e7af51;padding-left:16px}.checks{columns:2}.checks p{margin:6px 0}.pass{color:#058565}code{background:#eff4f2;padding:2px 5px}li{margin:8px 0}@media(max-width:900px){main{padding:15px}.gallery{grid-template-columns:1fr}.table{overflow:auto}.checks{columns:1}}
</style></head><body><main><header><h1>005~008 内容制作全流程验收</h1><p>2026-09-24 · 模型动作 → 场景组成 → 训练编排 → 现有虚拟仿真</p></header>
<div class="stats"><span><b>4</b>机构模型</span><span><b>4</b>训练场景</span><span><b>38</b>操作步骤</span><span><b>98</b>部件子动作</span><span><b>5</b>原任务快照未变</span></div>
<section><h2>演示入口</h2><p>登录数字车间后,从“内容制作 → 模型制作”进入对应机构模型,或打开下表链接。四套名称均采用实际训练名称。</p><div class="table"><table><thead><tr><th>训练内容</th><th>模型制作</th><th>场景制作</th><th>训练编排</th><th>规模</th><th>现有入口</th></tr></thead><tbody><tr><td><b>TASK-VIRTUAL-005</b><br>重型支援桥液压泵应急切换</td><td><a href="http://127.0.0.1:6180/content/models/466/production">机构模型 · V5.0.0</a></td><td><a href="http://127.0.0.1:6180/content/scenes/467/production">训练场景 · V3.0.0</a></td><td><a href="http://127.0.0.1:6180/content/training-projects/438/production">训练编排 · V7.0.0</a></td><td>5 步 / 6 动作</td><td><a href="http://127.0.0.1:6180/teaching/virtual-training/preview/86">进入仿真</a></td></tr><tr><td><b>TASK-VIRTUAL-006</b><br>重型支援桥液压泵应急替代</td><td><a href="http://127.0.0.1:6180/content/models/468/production">机构模型 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/scenes/469/production">训练场景 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/training-projects/439/production">训练编排 · V10.0.0</a></td><td>6 步 / 15 动作</td><td><a href="http://127.0.0.1:6180/teaching/virtual-training/preview/87">进入仿真</a></td></tr><tr><td><b>TASK-VIRTUAL-007</b><br>重型支援桥前泵换件修复</td><td><a href="http://127.0.0.1:6180/content/models/470/production">机构模型 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/scenes/471/production">训练场景 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/training-projects/440/production">训练编排 · V8.0.0</a></td><td>11 步 / 27 动作</td><td><a href="http://127.0.0.1:6180/teaching/virtual-training/preview/88">进入仿真</a></td></tr><tr><td><b>TASK-VIRTUAL-008</b><br>重型支援桥平衡阀拆拼互换</td><td><a href="http://127.0.0.1:6180/content/models/472/production">机构模型 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/scenes/473/production">训练场景 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/training-projects/441/production">训练编排 · V8.0.0</a></td><td>16 步 / 50 动作</td><td><a href="http://127.0.0.1:6180/teaching/virtual-training/preview/89">进入仿真</a></td></tr></tbody></table></div></section>
<section><h2>建议演示顺序</h2><ol><li>打开 005 机构模型,选择球阀或盒盖动作;调整旋转轴、方向、中心点、角度和时长,点击“播放动作”。</li><li>保存、刷新确认参数,再点击“发布并进入场景”。场景显示实际装备、环境和已绑定模型版本。</li><li>修改场景对象位置、旋转或缩放,应用预览;发布并进入编排。</li><li>查看对应步骤、模型动作和数字教员讲解,切到“背景与讲解”编辑图文或视频内容。</li><li>发布训练内容,再点击“虚拟仿真”查看原任务的背景、讲解和操作流程。</li></ol><p class="note">本次建立可保存、可发布的制作链。现有虚拟仿真继续使用固定静态脚本,新参数不会即时改写已发布任务。005 制作链使用已走通的新模型,原 005 与独立新模型入口都保留。</p></section>
<section><h2>重型支援桥液压泵应急切换</h2><div class="gallery"><figure><a href="TASK-VIRTUAL-005-model.png"><img loading="lazy" src="TASK-VIRTUAL-005-model.png" alt="机构模型与动作库"></a><figcaption>机构模型与动作库</figcaption></figure><figure><a href="TASK-VIRTUAL-005-action-5.png"><img loading="lazy" src="TASK-VIRTUAL-005-action-5.png" alt="部件操作动画"></a><figcaption>部件操作动画</figcaption></figure><figure><a href="TASK-VIRTUAL-005-scene.png"><img loading="lazy" src="TASK-VIRTUAL-005-scene.png" alt="场景组成与位置配置"></a><figcaption>场景组成与位置配置</figcaption></figure><figure><a href="TASK-VIRTUAL-005-training.png"><img loading="lazy" src="TASK-VIRTUAL-005-training.png" alt="背景与讲解编排"></a><figcaption>背景与讲解编排</figcaption></figure></div></section><section><h2>重型支援桥液压泵应急替代</h2><div class="gallery"><figure><a href="TASK-VIRTUAL-006-model.png"><img loading="lazy" src="TASK-VIRTUAL-006-model.png" alt="机构模型与动作库"></a><figcaption>机构模型与动作库</figcaption></figure><figure><a href="TASK-VIRTUAL-006-action-5.png"><img loading="lazy" src="TASK-VIRTUAL-006-action-5.png" alt="部件操作动画"></a><figcaption>部件操作动画</figcaption></figure><figure><a href="TASK-VIRTUAL-006-scene.png"><img loading="lazy" src="TASK-VIRTUAL-006-scene.png" alt="场景组成与位置配置"></a><figcaption>场景组成与位置配置</figcaption></figure><figure><a href="TASK-VIRTUAL-006-training.png"><img loading="lazy" src="TASK-VIRTUAL-006-training.png" alt="背景与讲解编排"></a><figcaption>背景与讲解编排</figcaption></figure></div></section><section><h2>重型支援桥前泵换件修复</h2><div class="gallery"><figure><a href="TASK-VIRTUAL-007-model.png"><img loading="lazy" src="TASK-VIRTUAL-007-model.png" alt="机构模型与动作库"></a><figcaption>机构模型与动作库</figcaption></figure><figure><a href="TASK-VIRTUAL-007-action-8.png"><img loading="lazy" src="TASK-VIRTUAL-007-action-8.png" alt="部件操作动画"></a><figcaption>部件操作动画</figcaption></figure><figure><a href="TASK-VIRTUAL-007-scene.png"><img loading="lazy" src="TASK-VIRTUAL-007-scene.png" alt="场景组成与位置配置"></a><figcaption>场景组成与位置配置</figcaption></figure><figure><a href="TASK-VIRTUAL-007-training.png"><img loading="lazy" src="TASK-VIRTUAL-007-training.png" alt="背景与讲解编排"></a><figcaption>背景与讲解编排</figcaption></figure></div></section><section><h2>重型支援桥平衡阀拆拼互换</h2><div class="gallery"><figure><a href="TASK-VIRTUAL-008-model.png"><img loading="lazy" src="TASK-VIRTUAL-008-model.png" alt="机构模型与动作库"></a><figcaption>机构模型与动作库</figcaption></figure><figure><a href="TASK-VIRTUAL-008-action-36.png"><img loading="lazy" src="TASK-VIRTUAL-008-action-36.png" alt="部件操作动画"></a><figcaption>部件操作动画</figcaption></figure><figure><a href="TASK-VIRTUAL-008-scene.png"><img loading="lazy" src="TASK-VIRTUAL-008-scene.png" alt="场景组成与位置配置"></a><figcaption>场景组成与位置配置</figcaption></figure><figure><a href="TASK-VIRTUAL-008-training.png"><img loading="lazy" src="TASK-VIRTUAL-008-training.png" alt="背景与讲解编排"></a><figcaption>背景与讲解编排</figcaption></figure></div></section>
<section><h2>笔记本与局域网</h2><p>通过 192.168.31.168:6180 在 1366×768 验证,动作参数面板独立滚动,画布按可用宽度取景。</p><figure><a href="lan-TASK-VIRTUAL-005-action-3.png"><img src="lan-TASK-VIRTUAL-005-action-3.png" alt="小屏球阀动作参数"></a></figure></section>
<section><h2>验证结果</h2><p class="pass">Web 构建及后处理通过;25 项相关前端测试通过;Java 17 下 ut-tran 与依赖模块 package 通过,440 项测试无失败。</p><div class="checks"><p>✓ TASK-VIRTUAL-005 · model</p><p>✓ TASK-VIRTUAL-005 · scene</p><p>✓ TASK-VIRTUAL-005 · training</p><p>✓ TASK-VIRTUAL-006 · model</p><p>✓ TASK-VIRTUAL-006 · scene</p><p>✓ TASK-VIRTUAL-006 · training</p><p>✓ TASK-VIRTUAL-007 · model</p><p>✓ TASK-VIRTUAL-007 · scene</p><p>✓ TASK-VIRTUAL-007 · training</p><p>✓ TASK-VIRTUAL-008 · model</p><p>✓ TASK-VIRTUAL-008 · scene</p><p>✓ TASK-VIRTUAL-008 · training</p><p>✓ 005 90→75 保存刷新→90 发布模型→场景→编排</p><p>✓ 制作链进入原005背景与讲解</p><p>✓ 原 005~008 及 005 独立入口固定定义完全未变</p><p>✓ 局域网 1366×768 · model</p><p>✓ 局域网 1366×768 · scene</p><p>✓ 局域网 1366×768 · training</p><p>✓ 006 场景位置修改保存刷新回读并恢复</p><p>✓ TASK-VIRTUAL-006 模型→场景→编排发布→原虚拟仿真</p><p>✓ TASK-VIRTUAL-007 模型→场景→编排发布→原虚拟仿真</p><p>✓ TASK-VIRTUAL-008 模型→场景→编排发布→原虚拟仿真</p><p>✓ 五个原任务完整快照未变</p></div><p>98 个动作逐一验证目标存在、聚焦居中、变换有限;旋转/移动动作确实改变姿态,并检查暂停、恢复、取消动画及场景位置增量。少量目标沿用原有补充示意,页面明确标注来源。</p><p>构建仍有现有的大包体积提示,不影响通过。验收使用本地管理员业务账号、Edge headless、真实 API;内置浏览器当前不可用。变更未部署测试环境,也未提交推送。</p><p><a href="ui-result.json">页面验收 JSON</a> · <a href="ui-lan-result.json">局域网结果</a> · <a href="actions-result.json">98 个动作记录</a> · <a href="integration-result.json">四套版本依赖</a> · <a href="health.json">服务健康检查</a></p></section>
<section><h2>数据与回退</h2><p>保留 4 个新模型、4 个新场景和 4 个原编排的内容版本。测试角度、场景位置已恢复。原任务 86、87、88、89、116 完整快照未改;备份位于本目录的 before-task-*.json 与 before-project-*.json。历史失败截图保留供排查,以上最终结果均已重跑通过。</p><p>后续部署需要更新 Web、ut-tran,并在目标环境登记业务工程。当前工程 ID 为本环境专用,不应直接当作另一环境的固定 ID。</p></section></main></body></html>

+ 207
- 0
reports/bridge-content-authoring-20260924/integration-result.json Datei anzeigen

@@ -0,0 +1,207 @@
{
"pass": true,
"checks": [
{
"name": "006 场景位置修改保存刷新回读并恢复",
"pass": true
},
{
"name": "TASK-VIRTUAL-006 模型→场景→编排发布→原虚拟仿真",
"pass": true
},
{
"name": "TASK-VIRTUAL-007 模型→场景→编排发布→原虚拟仿真",
"pass": true
},
{
"name": "TASK-VIRTUAL-008 模型→场景→编排发布→原虚拟仿真",
"pass": true
},
{
"name": "五个原任务完整快照未变",
"pass": true
}
],
"current": [
{
"taskCode": "TASK-VIRTUAL-005",
"taskId": "86",
"steps": 5,
"actions": 6,
"model": {
"id": "466",
"name": "重型支援桥液压泵应急切换机构模型",
"versionId": "592",
"versionCode": "V5.0.0",
"dependencies": []
},
"scene": {
"id": "467",
"name": "重型支援桥液压泵应急切换训练场景",
"versionId": "593",
"versionCode": "V3.0.0",
"dependencies": [
{
"id": "783",
"targetProjectId": "466",
"targetVersionId": "592",
"relationType": "SCENE_MODEL",
"required": true,
"sortOrder": 0
}
]
},
"training": {
"id": "438",
"name": "重型支援桥液压泵应急切换",
"versionId": "594",
"versionCode": "V7.0.0",
"dependencies": [
{
"id": "786",
"targetProjectId": "467",
"targetVersionId": "593",
"relationType": "TRAINING_SCENE",
"required": true,
"sortOrder": 0
}
]
}
},
{
"taskCode": "TASK-VIRTUAL-006",
"taskId": "87",
"steps": 6,
"actions": 15,
"model": {
"id": "468",
"name": "重型支援桥液压泵应急替代机构模型",
"versionId": "595",
"versionCode": "V2.0.0",
"dependencies": []
},
"scene": {
"id": "469",
"name": "重型支援桥液压泵应急替代训练场景",
"versionId": "596",
"versionCode": "V2.0.0",
"dependencies": [
{
"id": "790",
"targetProjectId": "468",
"targetVersionId": "595",
"relationType": "SCENE_MODEL",
"required": true,
"sortOrder": 0
}
]
},
"training": {
"id": "439",
"name": "重型支援桥液压泵应急替代",
"versionId": "597",
"versionCode": "V10.0.0",
"dependencies": [
{
"id": "793",
"targetProjectId": "469",
"targetVersionId": "596",
"relationType": "TRAINING_SCENE",
"required": true,
"sortOrder": 0
}
]
}
},
{
"taskCode": "TASK-VIRTUAL-007",
"taskId": "88",
"steps": 11,
"actions": 27,
"model": {
"id": "470",
"name": "重型支援桥前泵换件修复机构模型",
"versionId": "598",
"versionCode": "V2.0.0",
"dependencies": []
},
"scene": {
"id": "471",
"name": "重型支援桥前泵换件修复训练场景",
"versionId": "599",
"versionCode": "V2.0.0",
"dependencies": [
{
"id": "796",
"targetProjectId": "470",
"targetVersionId": "598",
"relationType": "SCENE_MODEL",
"required": true,
"sortOrder": 0
}
]
},
"training": {
"id": "440",
"name": "重型支援桥前泵换件修复",
"versionId": "600",
"versionCode": "V8.0.0",
"dependencies": [
{
"id": "799",
"targetProjectId": "471",
"targetVersionId": "599",
"relationType": "TRAINING_SCENE",
"required": true,
"sortOrder": 0
}
]
}
},
{
"taskCode": "TASK-VIRTUAL-008",
"taskId": "89",
"steps": 16,
"actions": 50,
"model": {
"id": "472",
"name": "重型支援桥平衡阀拆拼互换机构模型",
"versionId": "601",
"versionCode": "V2.0.0",
"dependencies": []
},
"scene": {
"id": "473",
"name": "重型支援桥平衡阀拆拼互换训练场景",
"versionId": "602",
"versionCode": "V2.0.0",
"dependencies": [
{
"id": "802",
"targetProjectId": "472",
"targetVersionId": "601",
"relationType": "SCENE_MODEL",
"required": true,
"sortOrder": 0
}
]
},
"training": {
"id": "441",
"name": "重型支援桥平衡阀拆拼互换",
"versionId": "603",
"versionCode": "V8.0.0",
"dependencies": [
{
"id": "805",
"targetProjectId": "473",
"targetVersionId": "602",
"relationType": "TRAINING_SCENE",
"required": true,
"sortOrder": 0
}
]
}
}
]
}











+ 65
- 0
reports/bridge-content-authoring-20260924/registration.json Datei anzeigen

@@ -0,0 +1,65 @@
{
"pass": true,
"items": [
{
"taskId": "86",
"taskCode": "TASK-VIRTUAL-005",
"trainingProjectId": "438",
"modelProjectId": "466",
"sceneProjectId": "467",
"modelVersionId": "575",
"sceneVersionId": "576",
"trainingVersionId": "577",
"steps": 5,
"actions": 6,
"complete": true
},
{
"taskId": "87",
"taskCode": "TASK-VIRTUAL-006",
"trainingProjectId": "439",
"modelProjectId": "468",
"sceneProjectId": "469",
"modelVersionId": "578",
"sceneVersionId": "579",
"trainingVersionId": "580",
"steps": 6,
"actions": 15,
"complete": true
},
{
"taskId": "88",
"taskCode": "TASK-VIRTUAL-007",
"trainingProjectId": "440",
"modelProjectId": "470",
"sceneProjectId": "471",
"modelVersionId": "581",
"sceneVersionId": "582",
"trainingVersionId": "583",
"steps": 11,
"actions": 27,
"complete": true
},
{
"taskId": "89",
"taskCode": "TASK-VIRTUAL-008",
"trainingProjectId": "441",
"modelProjectId": "472",
"sceneProjectId": "473",
"modelVersionId": "584",
"sceneVersionId": "585",
"trainingVersionId": "586",
"steps": 16,
"actions": 50,
"complete": true
}
],
"unchangedAssignments": [
86,
87,
88,
89,
116
],
"checkedAt": "2026-09-24T08:11:46.156Z"
}


+ 29
- 0
reports/bridge-content-authoring-20260924/ui-lan-result.json Datei anzeigen

@@ -0,0 +1,29 @@
{
"pass": true,
"base": "http://192.168.31.168:6180",
"checks": [
{
"task": "TASK-VIRTUAL-005",
"stage": "model",
"actions": 6,
"played": [
2,
3,
5,
6
],
"pass": true
},
{
"task": "TASK-VIRTUAL-005",
"stage": "scene",
"pass": true
},
{
"task": "TASK-VIRTUAL-005",
"stage": "training",
"pass": true
}
],
"errors": []
}

+ 104
- 0
reports/bridge-content-authoring-20260924/ui-result.json Datei anzeigen

@@ -0,0 +1,104 @@
{
"pass": true,
"base": "http://127.0.0.1:6180",
"checks": [
{
"task": "TASK-VIRTUAL-005",
"stage": "model",
"actions": 6,
"played": [
2,
3,
5,
6
],
"pass": true
},
{
"task": "TASK-VIRTUAL-005",
"stage": "scene",
"pass": true
},
{
"task": "TASK-VIRTUAL-005",
"stage": "training",
"pass": true
},
{
"task": "TASK-VIRTUAL-006",
"stage": "model",
"actions": 15,
"played": [
3,
5,
9
],
"pass": true
},
{
"task": "TASK-VIRTUAL-006",
"stage": "scene",
"pass": true
},
{
"task": "TASK-VIRTUAL-006",
"stage": "training",
"pass": true
},
{
"task": "TASK-VIRTUAL-007",
"stage": "model",
"actions": 27,
"played": [
4,
8,
12
],
"pass": true
},
{
"task": "TASK-VIRTUAL-007",
"stage": "scene",
"pass": true
},
{
"task": "TASK-VIRTUAL-007",
"stage": "training",
"pass": true
},
{
"task": "TASK-VIRTUAL-008",
"stage": "model",
"actions": 50,
"played": [
3,
6,
36
],
"pass": true
},
{
"task": "TASK-VIRTUAL-008",
"stage": "scene",
"pass": true
},
{
"task": "TASK-VIRTUAL-008",
"stage": "training",
"pass": true
},
{
"name": "005 90→75 保存刷新→90 发布模型→场景→编排",
"pass": true
},
{
"name": "制作链进入原005背景与讲解",
"pass": true
},
{
"name": "原 005~008 及 005 独立入口固定定义完全未变",
"pass": true
}
],
"errors": []
}

+ 49
- 0
tools/bridge-content-actions-live.mjs Datei anzeigen

@@ -0,0 +1,49 @@
import fs from 'node:fs';
import assert from 'node:assert/strict';
import {chromium} from '@playwright/test';
import {login,call} from './role-permissions-live.mjs';
const dir='reports/bridge-content-authoring-20260924',fixture=JSON.parse(fs.readFileSync(dir+'/fixture.json','utf8'));
const signed=await login('root'),checks=[];
const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']});
try{const page=await browser.newPage({viewport:{width:1440,height:1000}});
await page.goto('http://127.0.0.1:6180/');
for(const item of fixture.items){
const response=await call('tran/v1/content/projects/'+item.modelProjectId,signed.accessToken);assert.equal(response.status,200);
const result=await page.evaluate(async content=>{
const style=document.createElement('style');style.textContent='.authoring-test .training-shell,.authoring-test .training-main,.authoring-test .training-viewport{width:100%!important;height:100%!important;min-height:0!important;min-width:0!important;display:block!important;padding:0!important}.authoring-test .training-shell>:not(.training-main),.authoring-test .training-main>:not(.training-viewport),.authoring-test .training-viewport>:not(canvas){display:none!important}';document.head.append(style);
const host=document.createElement('div');host.className='authoring-test';host.style.cssText='position:fixed;inset:0;width:700px;height:500px;z-index:999999';document.body.append(host);
const {createProductionPreview}=await import('/src/features/content-production/preview.js');
const viewer=await createProductionPreview(host,content.productionDefinition),checks=[];
try{const actions=viewer.configure(content.productionActions);viewer.overview();
const overview=viewer.inspect();
for(let i=0;i<actions.length;i++){
viewer.select(i);const before=viewer.inspect().targets[i];
await viewer.play(i,{...actions[i],motion:{...actions[i].motion,durationMs:100}});
const after=viewer.inspect().targets[i];
checks.push({id:actions[i].id,target:actions[i].targetId,kind:actions[i].motion.kind,before,after});
}
const pending=viewer.play(0,{...actions[0],motion:{...actions[0].motion,durationMs:900}});
viewer.pause();const cancelled=await pending,paused=viewer.inspect().paused;
const changed=structuredClone(content.productionDefinition),items=changed.scenario.sceneResource.snapshot.objects;
changed.authoringBaseObjects ||= structuredClone(items);
const item=items.find(item=>item.assetRole!=='environment'&&!/terrain|地形/i.test(item.sourceName));
item.position[0]+=0.25;
const secondHost=host.cloneNode(false);document.body.append(secondHost);
const second=await createProductionPreview(secondHost,changed);
let placement;
try{const after=second.inspect().sceneObjects.find(o=>o.sceneId===item.sceneId),before=overview.sceneObjects.find(o=>o.sceneId===item.sceneId);placement={sceneId:item.sceneId,deltaX:after.position[0]-before.position[0]};}
finally{second.destroy();secondHost.remove();}
viewer.resume();return {overview,checks,paused,cancelled,resumed:!viewer.inspect().paused,placement};
}finally{viewer.destroy();host.remove();style.remove();}
},response.data.currentVersion.content);
assert.ok(result.paused&&result.resumed);
assert.equal(result.cancelled,false);
assert.ok(Math.abs(result.placement.deltaX-0.25)<1e-6,JSON.stringify(result.placement));
for(const action of result.checks){assert.ok([...action.after.position,...action.after.quaternion,...action.after.projected].every(Number.isFinite),action.id);
assert.ok(Math.abs(action.before.projected[0])<0.01&&Math.abs(action.before.projected[1])<0.01,action.id+' focus');
if(action.kind!=='state')assert.notDeepEqual([action.after.position,action.after.quaternion],[action.before.position,action.before.quaternion],action.id+' must move');
}
assert.equal(result.checks.length,item.actions);checks.push({task:item.taskCode,...result});console.log(item.taskCode+': '+result.checks.length+' actions');
}
fs.writeFileSync(dir+'/actions-result.json',JSON.stringify({pass:true,checks},null,2));
}finally{await browser.close();}

+ 80
- 0
tools/bridge-content-authoring-live.mjs Datei anzeigen

@@ -0,0 +1,80 @@
import fs from 'node:fs';
import path from 'node:path';
import assert from 'node:assert/strict';
import {chromium,expect} from '@playwright/test';
import {login,call} from './role-permissions-live.mjs';
const dir='reports/bridge-content-authoring-20260924';
const fixture=JSON.parse(fs.readFileSync(dir+'/fixture.json','utf8'));
const signed=await login('root'),checks=[],errors=[];
const api=async ep=>{const r=await call('tran/v1/'+ep,signed.accessToken);assert.equal(r.status,200,ep);return r.data;};
const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']});
const page=await browser.newPage({viewport:{width:Number(process.env.VIEW_WIDTH)||1440,height:Number(process.env.VIEW_HEIGHT)||1000}});
page.on('pageerror',e=>errors.push(e.message));
await page.addInitScript(({s,theme})=>{sessionStorage.setItem('unreal-tran:web:access-token:v1',s.accessToken);sessionStorage.setItem('unreal-tran:web:refresh-token:v1',s.refreshToken);if(theme)localStorage.setItem('unreal-tran:web:preferences:v1',JSON.stringify({themeMode:theme}));},{s:signed,theme:process.env.THEME});
const base=process.env.BASE_URL||'http://127.0.0.1:6180';
const screenshot=name=>page.screenshot({path:`${dir}/${process.env.IMAGE_PREFIX||''}${name}.png`});
const ready=async()=>{await expect(page.getByRole('button',{name:'播放动作',exact:true})).toBeEnabled({timeout:120000});};
try{
for(const item of process.env.FLOW_ONLY==='1'?[]:fixture.items){
await page.goto(base+'/content/models/'+item.modelProjectId+'/production');await ready();
await expect(page.locator('.production-action-item')).toHaveCount(item.actions);
const dimensions=await page.locator('.production-preview').evaluate(host=>({width:host.clientWidth,canvas:host.querySelector('canvas').clientWidth,height:host.clientHeight}));
assert.ok(Math.abs(dimensions.width-dimensions.canvas)<5,JSON.stringify(dimensions));
await screenshot(`${item.taskCode}-model`);
const candidates=item.taskCode.endsWith('005')?[1,2,4,5]:item.taskCode.endsWith('006')?[2,4,8]:item.taskCode.endsWith('007')?[3,7,11]:[2,5,35];
for(const index of candidates){
await page.locator('.production-action-item').nth(index).click();
await page.getByRole('button',{name:'播放动作',exact:true}).click();await ready();
await screenshot(`${item.taskCode}-action-${index+1}`);
}
checks.push({task:item.taskCode,stage:'model',actions:item.actions,played:candidates.map(i=>i+1),pass:true});
await page.locator('.production-chain button').nth(1).click();await ready();
await expect(page.locator('.production-object')).toHaveCount(item.taskCode.endsWith('005')?2:item.taskCode.endsWith('008')?3:4);
await screenshot(`${item.taskCode}-scene`);
checks.push({task:item.taskCode,stage:'scene',pass:true});
await page.locator('.production-chain button').nth(2).click();await ready();
await page.getByRole('tab',{name:'背景与讲解'}).click();
await screenshot(`${item.taskCode}-training`);
checks.push({task:item.taskCode,stage:'training',pass:true});
if(process.env.CAPTURE_ONLY==='1')break;
}
if(process.env.CAPTURE_ONLY!=='1'){
const item=fixture.items[0];
await page.goto(base+'/content/models/'+item.modelProjectId+'/production');await ready();
await page.locator('.production-action-item').nth(2).click();
const angle=page.locator('.el-form-item').filter({hasText:'旋转角度(°)'}).getByRole('spinbutton');
await angle.fill('75');await angle.press('Tab');
await page.getByRole('button',{name:'播放动作',exact:true}).click();await ready();
await page.getByRole('button',{name:'保存',exact:true}).click();
await expect(page.getByText('已保存,可刷新继续编辑')).toBeVisible();
await page.reload();await ready();await page.locator('.production-action-item').nth(2).click();await expect(angle).toHaveValue('75');
const changed=await api('content/projects/'+item.modelProjectId);assert.equal(changed.currentVersion.content.productionActions[2].motion.angle,75);
await angle.fill('90');await angle.press('Tab');
await page.getByRole('button',{name:'发布并进入场景'}).click();await expect(page).toHaveURL(new RegExp('/scenes/'+item.sceneProjectId+'/production'));await ready();
await page.getByRole('button',{name:'发布并进入编排'}).click();await expect(page).toHaveURL(new RegExp('/training-projects/'+item.trainingProjectId+'/production'));await ready();
await page.getByRole('button',{name:'发布训练内容'}).click();await expect(page.getByText('训练内容已发布;现有仿真仍保持固定版本')).toBeVisible();
checks.push({name:'005 90→75 保存刷新→90 发布模型→场景→编排',pass:true});
const model=await api('content/projects/'+item.modelProjectId),scene=await api('content/projects/'+item.sceneProjectId),training=await api('content/projects/'+item.trainingProjectId);
assert.equal(scene.currentVersion.dependencies[0].targetVersionId,model.project.publishedVersionId);
assert.equal(training.currentVersion.dependencies[0].targetVersionId,scene.project.publishedVersionId);
assert.equal(training.currentVersion.content.steps[2].modelActions[0].motion.angle,90);
assert.equal(training.currentVersion.content.steps[2].modelActions[0].modelVersionId,model.project.publishedVersionId);
await page.locator('.production-chain button').nth(3).click();
await expect(page).toHaveURL(new RegExp('/virtual-training/preview/'+item.taskId));
await expect(page.getByRole('button',{name:'跳过讲解',exact:true})).toBeVisible({timeout:120000});
await screenshot('existing-005-preview');
checks.push({name:'制作链进入原005背景与讲解',pass:true});
for(const list of ['models','scenes','training-projects']){
await page.goto(base+'/content/'+list);await expect(page.getByText(/重型支援桥液压泵应急切换/).first()).toBeVisible({timeout:30000});
await screenshot(`list-${list}`);
const row=page.locator(list==='training-projects'?'.training-project-card':'.asset-row-list article').filter({hasText:'重型支援桥液压泵应急切换'}).first();
await row.getByRole('button',{name:list==='training-projects'?'编辑':'进入编辑',exact:true}).click();await ready();
await expect(page).toHaveURL(/\/production$/);
}
for(const id of [86,87,88,89,116]){const before=JSON.parse(fs.readFileSync(`${dir}/before-task-${id}.json`,'utf8')),after=await api('teaching/assignments/'+id);delete before.serverNow;delete after.serverNow;assert.deepEqual(after,before);}
checks.push({name:'原 005~008 及 005 独立入口固定定义完全未变',pass:true});
}
assert.deepEqual(errors,[]);
fs.writeFileSync(dir+'/'+(process.env.RESULT_FILE||'ui-result.json'),JSON.stringify({pass:true,base,checks,errors},null,2));
console.log(JSON.stringify({pass:true,checks:checks.length}));
}catch(e){await page.screenshot({path:dir+'/failure.png'}).catch(()=>{});fs.writeFileSync(dir+'/failure.json',JSON.stringify({pass:false,message:e.message,checks,errors},null,2));throw e;}finally{await browser.close();}

+ 24
- 0
tools/bridge-content-authoring-report.mjs Datei anzeigen

@@ -0,0 +1,24 @@
import fs from 'node:fs';
import assert from 'node:assert/strict';
const dir='reports/bridge-content-authoring-20260924';
const read=file=>JSON.parse(fs.readFileSync(`${dir}/${file}.json`,'utf8'));
const ui=read('ui-result'),lan=read('ui-lan-result'),actions=read('actions-result'),integration=read('integration-result');
assert.ok(ui.pass&&lan.pass&&actions.pass&&integration.pass);
const escape=value=>String(value).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const url=(stage,id)=>`http://127.0.0.1:6180/content/${stage}/${id}/production`;
const rows=integration.current.map(item=>`<tr><td><b>${escape(item.taskCode)}</b><br>${escape(item.training.name)}</td><td><a href="${url('models',item.model.id)}">机构模型 · ${escape(item.model.versionCode)}</a></td><td><a href="${url('scenes',item.scene.id)}">训练场景 · ${escape(item.scene.versionCode)}</a></td><td><a href="${url('training-projects',item.training.id)}">训练编排 · ${escape(item.training.versionCode)}</a></td><td>${item.steps} 步 / ${item.actions} 动作</td><td><a href="http://127.0.0.1:6180/teaching/virtual-training/preview/${item.taskId}">进入仿真</a></td></tr>`).join('');
const panels=integration.current.map(item=>`<section><h2>${escape(item.training.name)}</h2><div class="gallery">${[['model','机构模型与动作库'],['action-'+(item.taskCode.endsWith('005')?'5':item.taskCode.endsWith('006')?'5':item.taskCode.endsWith('007')?'8':'36'),'部件操作动画'],['scene','场景组成与位置配置'],['training','背景与讲解编排']].map(([suffix,title])=>`<figure><a href="${item.taskCode}-${suffix}.png"><img loading="lazy" src="${item.taskCode}-${suffix}.png" alt="${title}"></a><figcaption>${title}</figcaption></figure>`).join('')}</div></section>`).join('');
const checks=[...ui.checks,...lan.checks.map(x=>({...x,name:'局域网 1366×768 · '+x.stage})),...integration.checks];
const html=`<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>005~008 内容制作全流程验收</title><style>
*{box-sizing:border-box}body{margin:0;background:#f2f6f5;color:#163c38;font:15px/1.7 "Microsoft YaHei",sans-serif}main{max-width:1480px;margin:auto;padding:36px}header{background:#0c594e;color:white;padding:32px;border-radius:14px}h1{font-size:28px;margin:0}h2{font-size:21px}header p{margin:8px 0 0;color:#d4e9e2}.stats{display:flex;gap:12px;flex-wrap:wrap;margin:22px 0}.stats span{padding:13px 22px;background:white;border:1px solid #ccded8;border-radius:9px}.stats b{font-size:24px;color:#078c70;margin-right:8px}section{background:white;padding:24px;margin:20px 0;border:1px solid #d4e3de;border-radius:12px}table{width:100%;border-collapse:collapse}td,th{border-bottom:1px solid #e0eae7;padding:14px;text-align:left}th{background:#edf5f2}a{color:#087c67;text-decoration:none}a:hover{text-decoration:underline}.gallery{display:grid;grid-template-columns:1fr 1fr;gap:18px}figure{margin:0}figure img{display:block;width:100%;border:1px solid #d3e2dd;border-radius:7px}figcaption{padding:8px 0;color:#607f77}.note{border-left:4px solid #e7af51;padding-left:16px}.checks{columns:2}.checks p{margin:6px 0}.pass{color:#058565}code{background:#eff4f2;padding:2px 5px}li{margin:8px 0}@media(max-width:900px){main{padding:15px}.gallery{grid-template-columns:1fr}.table{overflow:auto}.checks{columns:1}}
</style></head><body><main><header><h1>005~008 内容制作全流程验收</h1><p>2026-09-24 · 模型动作 → 场景组成 → 训练编排 → 现有虚拟仿真</p></header>
<div class="stats"><span><b>4</b>机构模型</span><span><b>4</b>训练场景</span><span><b>38</b>操作步骤</span><span><b>98</b>部件子动作</span><span><b>5</b>原任务快照未变</span></div>
<section><h2>演示入口</h2><p>登录数字车间后,从“内容制作 → 模型制作”进入对应机构模型,或打开下表链接。四套名称均采用实际训练名称。</p><div class="table"><table><thead><tr><th>训练内容</th><th>模型制作</th><th>场景制作</th><th>训练编排</th><th>规模</th><th>现有入口</th></tr></thead><tbody>${rows}</tbody></table></div></section>
<section><h2>建议演示顺序</h2><ol><li>打开 005 机构模型,选择球阀或盒盖动作;调整旋转轴、方向、中心点、角度和时长,点击“播放动作”。</li><li>保存、刷新确认参数,再点击“发布并进入场景”。场景显示实际装备、环境和已绑定模型版本。</li><li>修改场景对象位置、旋转或缩放,应用预览;发布并进入编排。</li><li>查看对应步骤、模型动作和数字教员讲解,切到“背景与讲解”编辑图文或视频内容。</li><li>发布训练内容,再点击“虚拟仿真”查看原任务的背景、讲解和操作流程。</li></ol><p class="note">本次建立可保存、可发布的制作链。现有虚拟仿真继续使用固定静态脚本,新参数不会即时改写已发布任务。005 制作链使用已走通的新模型,原 005 与独立新模型入口都保留。</p></section>
${panels}
<section><h2>笔记本与局域网</h2><p>通过 192.168.31.168:6180 在 1366×768 验证,动作参数面板独立滚动,画布按可用宽度取景。</p><figure><a href="lan-TASK-VIRTUAL-005-action-3.png"><img src="lan-TASK-VIRTUAL-005-action-3.png" alt="小屏球阀动作参数"></a></figure></section>
<section><h2>验证结果</h2><p class="pass">Web 构建及后处理通过;25 项相关前端测试通过;Java 17 下 ut-tran 与依赖模块 package 通过,440 项测试无失败。</p><div class="checks">${checks.map(x=>`<p>✓ ${escape(x.name||x.task+' · '+x.stage)}</p>`).join('')}</div><p>98 个动作逐一验证目标存在、聚焦居中、变换有限;旋转/移动动作确实改变姿态,并检查暂停、恢复、取消动画及场景位置增量。少量目标沿用原有补充示意,页面明确标注来源。</p><p>构建仍有现有的大包体积提示,不影响通过。验收使用本地管理员业务账号、Edge headless、真实 API;内置浏览器当前不可用。变更未部署测试环境,也未提交推送。</p><p><a href="ui-result.json">页面验收 JSON</a> · <a href="ui-lan-result.json">局域网结果</a> · <a href="actions-result.json">98 个动作记录</a> · <a href="integration-result.json">四套版本依赖</a> · <a href="health.json">服务健康检查</a></p></section>
<section><h2>数据与回退</h2><p>保留 4 个新模型、4 个新场景和 4 个原编排的内容版本。测试角度、场景位置已恢复。原任务 86、87、88、89、116 完整快照未改;备份位于本目录的 before-task-*.json 与 before-project-*.json。历史失败截图保留供排查,以上最终结果均已重跑通过。</p><p>后续部署需要更新 Web、ut-tran,并在目标环境登记业务工程。当前工程 ID 为本环境专用,不应直接当作另一环境的固定 ID。</p></section></main></body></html>`;
fs.writeFileSync(dir+'/index.html',html);
fs.writeFileSync(dir+'/README.md',`# 005~008 内容制作全流程\n\n打开 [验收报告](index.html) 查看入口、演示步骤和截图。\n\n已建立 4 个模型、4 个场景,关联 4 个编排;38 步、98 个动作。模型/场景/编排发布不更新现有教学任务。005 采用新模型制作,原任务与独立入口保留。\n\n验证:25 项前端测试、440 项后端测试、四套发布链、98 个动作和局域网 1366×768 页面通过。详细字段与回退说明见 API Wiki《41-桥梁内容制作关联》。\n`);
console.log(dir+'/index.html');

+ 91
- 0
tools/bridge-content-authoring.mjs Datei anzeigen

@@ -0,0 +1,91 @@
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import assert from 'node:assert/strict';
import {chromium} from '@playwright/test';
import {login,call} from './role-permissions-live.mjs';
import {scriptTrainingDefinition} from '../../unreal_tran_web/src/features/virtual-training-scripts/registry.js';
import {collectActions,applyActionBindings} from '../../unreal_tran_web/src/features/content-production/document.js';

const dir='reports/bridge-content-authoring-20260924';fs.mkdirSync(dir,{recursive:true});
const session=await login('root');
const api=async(ep,body,method)=>{const r=await call('tran/v1/'+ep,session.accessToken,body,method);assert.equal(r.status,200,ep+': '+r.message);return r.data;};
const write=(name,value)=>fs.writeFileSync(`${dir}/${name}.json`,JSON.stringify(value,null,2));
const read=name=>JSON.parse(fs.readFileSync(`${dir}/${name}.json`,'utf8'));
const backup=(name,value)=>{if(!fs.existsSync(`${dir}/${name}.json`))write(name,value);};
const fixture=fs.existsSync(`${dir}/fixture.json`)?read('fixture'):{items:[]};
const stripVolatile=t=>{const d=structuredClone(t);delete d.serverNow;return d;};
const bytesAsset=(object,index)=>{const bytes=fs.readFileSync(path.resolve('../unreal_tran_web/public',object.resourceUrl.slice(1)));return {code:`PRODUCTION-MODEL-${index+1}`,name:object.sourceName||object.name,type:'MODEL_FILE',storageUri:object.resourceUrl,mimeType:'model/gltf-binary',sizeBytes:bytes.length,sha256:crypto.createHash('sha256').update(bytes).digest('hex'),metadata:{builtin:true},status:'READY',sortOrder:index};};
const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']});
try{
for(const taskId of [86,87,88,89]){
const task=await api('teaching/assignments/'+taskId);backup('before-task-'+taskId,task);
const original=await api('content/projects/'+task.contentProjectId);backup('before-project-'+task.contentProjectId,original);
let item=fixture.items.find(i=>i.taskId===String(taskId));
if(item?.complete){console.log('Already prepared',item.taskCode);continue;}
const definition=scriptTrainingDefinition(task);
if(taskId===86){const pilot=await api('teaching/assignments/116');backup('before-task-116',pilot);definition.scenario.sceneResource=structuredClone(pilot.definitionSnapshot.scenario.sceneResource);delete definition.runtime.definitionSource;delete definition.runtime.scriptId;delete definition.runtime.scriptVersion;}
definition.name=task.name;definition.task.title=task.name;definition.introduction=structuredClone(task.definitionSnapshot.introduction);
for(const step of definition.steps)step.interaction||='rule-check';
let actions=collectActions(definition);
const page=await browser.newPage({viewport:{width:1500,height:1000}});
await page.goto('http://127.0.0.1:6180/');
const captured=await page.evaluate(async({definition,actions})=>{
const host=document.createElement('div');host.style.cssText='position:fixed;inset:0;background:white;z-index:100000';document.body.append(host);
const {createProductionPreview}=await import('/src/features/content-production/preview.js');
const viewer=await createProductionPreview(host,definition);
try{return {actions:viewer.configure(actions)};}finally{viewer.destroy();host.remove();}
},{definition,actions});
actions=captured.actions;await page.close();
assert.equal(actions.length,definition.steps.reduce((n,s)=>n+s.action.mechanism.steps.length,0));
assert.ok(actions.every(a=>a.binding?.sourceNode&&a.motion));
write('actions-'+task.code,actions);
const snapshot=structuredClone(definition.scenario.sceneResource.snapshot);
const assets=snapshot.objects.map(bytesAsset);
const resource=snapshot.objects.find(o=>o.assetRole!=='environment'&&!/地形|terrain/i.test(o.sourceName));assert.ok(resource);
const primaryAsset=assets[snapshot.objects.indexOf(resource)];
const production={schemaVersion:1,taskCode:task.code,taskId:String(taskId),trainingProjectId:task.contentProjectId,
sourceContentVersionId:task.contentVersionId,scriptId:task.definitionSnapshot.runtime.scriptId,scriptVersion:task.definitionSnapshot.runtime.scriptVersion};
const modelName=task.name+'机构模型',sceneName=task.name+'训练场景';
if(!item){item={taskId:String(taskId),taskCode:task.code,trainingProjectId:task.contentProjectId};fixture.items.push(item);write('fixture',fixture);}
const modelContent={schema:'digital-twin-editor-project',version:4,name:modelName,modelId:resource.modelId||resource.id,
modelResource:{...structuredClone(resource),id:resource.modelId||resource.id,assetCode:primaryAsset.code,storageUri:primaryAsset.storageUri,kind:'builtin'},
camera:{position:[4,3,5],target:[0,1,0]},model:null,environment:null,
timeline:{version:1,duration:Math.max(1,actions.length),currentTime:0,loop:false,zoom:1,tracks:[],clips:[]},blueprint:{version:1,nodes:[],edges:[]},
production:{...production},productionDefinition:definition,productionActions:actions,assemblyObjects:snapshot.objects};
const getOrCreate=async(type,name,content,projectId)=>{
if(projectId)return api('content/projects/'+projectId);
const list=await api(`content/projects?type=${type}&keyword=${encodeURIComponent(name)}&size=100`);
const found=list.records.find(p=>p.name===name);if(found)return api('content/projects/'+found.id);
return api('content/projects',{type,name,description:'重型支援桥装备训练内容',categoryCode:type==='MODEL'?'EQUIPMENT':'OUTDOOR',coverUri:task.coverUri||'/models/step-model-front-pump-thumb.png',content,assets,dependencies:[]});
};
let model=await getOrCreate('MODEL',modelName,modelContent,item.modelProjectId);item.modelProjectId=model.project.id;write('fixture',fixture);
let scene=await getOrCreate('SCENE',sceneName,{...snapshot,schema:'edit3dv4.scene',version:'1.5',name:sceneName},item.sceneProjectId);item.sceneProjectId=scene.project.id;write('fixture',fixture);
Object.assign(production,{modelProjectId:model.project.id,sceneProjectId:scene.project.id});
modelContent.production={...production};
model=await api('content/projects/'+model.project.id,{...model.project,name:modelName,content:modelContent,assets,dependencies:[],version:model.project.version,changeSummary:'建立部件动作库、旋转轴心与场景制作关联'},'PUT');
model=await api('content/projects/'+model.project.id+'/publish',{version:model.project.version});
item.modelVersionId=model.project.publishedVersionId;production.modelVersionId=item.modelVersionId;
const sceneObjects=snapshot.objects.map((o,i)=>({...o,assetCode:assets[i].code,targetProjectId:model.project.id,targetVersionId:item.modelVersionId}));
const sceneContent={...snapshot,schema:'edit3dv4.scene',version:'1.5',name:sceneName,objects:sceneObjects,
production:{...production},productionDefinition:structuredClone(definition),productionActions:actions};
sceneContent.productionDefinition.scenario.sceneResource.snapshot.objects=structuredClone(sceneObjects);
scene=await api('content/projects/'+scene.project.id,{...scene.project,name:sceneName,content:sceneContent,assets,
dependencies:[{targetProjectId:model.project.id,targetVersionId:item.modelVersionId,relationType:'SCENE_MODEL',required:true}],version:scene.project.version,changeSummary:'通过模型固定版本组成训练场景'},'PUT');
scene=await api('content/projects/'+scene.project.id+'/publish',{version:scene.project.version});
item.sceneVersionId=scene.project.publishedVersionId;production.sceneVersionId=item.sceneVersionId;
const content=structuredClone(original.currentVersion.content);
Object.assign(content,{production:{...production},productionDefinition:applyActionBindings(definition,actions,model.project.id,item.modelVersionId),productionActions:actions});
content.steps=content.steps.map((s,i)=>({...s,modelActions:content.productionDefinition.steps[i].modelActions}));
content.scenario.sceneProjectId=scene.project.id;content.scenario.sceneVersionId=item.sceneVersionId;
let training=await api('content/projects/'+original.project.id,{...original.project,description:'重型支援桥装备操作训练',content,assets:original.currentVersion.assets,
dependencies:[{targetProjectId:scene.project.id,targetVersionId:item.sceneVersionId,relationType:'TRAINING_SCENE',required:true}],version:original.project.version,changeSummary:'关联模型动作和场景固定版本,保持现有教学任务不变'},'PUT');
training=await api('content/projects/'+training.project.id+'/publish',{version:training.project.version});
item.trainingVersionId=training.project.publishedVersionId;item.steps=definition.steps.length;item.actions=actions.length;item.complete=true;write('fixture',fixture);
for(const [stage,data] of [['model',model],['scene',scene],['training',training]])write('after-'+stage+'-'+task.code,data);
assert.deepEqual(stripVolatile(await api('teaching/assignments/'+taskId)),stripVolatile(read('before-task-'+taskId)));
console.log(JSON.stringify(item));
}
for(const id of [86,87,88,89,116])assert.deepEqual(stripVolatile(await api('teaching/assignments/'+id)),stripVolatile(read('before-task-'+id)));
write('registration',{pass:true,items:fixture.items,unchangedAssignments:[86,87,88,89,116],checkedAt:new Date().toISOString()});
}finally{await browser.close();}

+ 42
- 0
tools/bridge-content-integration-live.mjs Datei anzeigen

@@ -0,0 +1,42 @@
import fs from 'node:fs';
import assert from 'node:assert/strict';
import {chromium,expect} from '@playwright/test';
import {login,call} from './role-permissions-live.mjs';
const dir='reports/bridge-content-authoring-20260924',fixture=JSON.parse(fs.readFileSync(dir+'/fixture.json','utf8'));
const signed=await login('root'),checks=[];
const api=async path=>{const r=await call('tran/v1/'+path,signed.accessToken);assert.equal(r.status,200,r.message);return r.data;};
const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']});
try{
const page=await browser.newPage({viewport:{width:1440,height:1000}});
await page.addInitScript(s=>{sessionStorage.setItem('unreal-tran:web:access-token:v1',s.accessToken);sessionStorage.setItem('unreal-tran:web:refresh-token:v1',s.refreshToken);localStorage.setItem('unreal-tran:web:preferences:v1',JSON.stringify({themeMode:'military'}));},signed);
const ready=()=>expect(page.getByRole('button',{name:'播放动作',exact:true})).toBeEnabled({timeout:120000});
for(const item of fixture.items.slice(1)){
await page.goto('http://127.0.0.1:6180/content/models/'+item.modelProjectId+'/production');await ready();
await page.getByRole('button',{name:'发布并进入场景'}).click();await expect(page).toHaveURL(new RegExp('/scenes/'+item.sceneProjectId+'/production'));await ready();
if(item.taskCode.endsWith('006')){
const x=page.locator('.production-object').nth(1).getByRole('spinbutton').first();const original=await x.inputValue();
await x.fill(String(Number(original)+0.25));await x.press('Tab');await page.getByRole('button',{name:'应用场景预览'}).click();await ready();
await page.getByRole('button',{name:'保存',exact:true}).click();await expect(page.getByText('已保存,可刷新继续编辑')).toBeVisible();
await page.reload();await ready();assert.ok(Math.abs(Number(await x.inputValue())-Number(original)-0.25)<0.001);
await x.fill(original);await x.press('Tab');await page.getByRole('button',{name:'应用场景预览'}).click();await ready();
checks.push({name:'006 场景位置修改保存刷新回读并恢复',pass:true});
}
await page.getByRole('button',{name:'发布并进入编排'}).click();await expect(page).toHaveURL(new RegExp('/training-projects/'+item.trainingProjectId+'/production'));await ready();
await page.getByRole('button',{name:'发布训练内容'}).click();await expect(page.getByText('训练内容已发布;现有仿真仍保持固定版本')).toBeVisible();
const model=await api('content/projects/'+item.modelProjectId),scene=await api('content/projects/'+item.sceneProjectId),training=await api('content/projects/'+item.trainingProjectId);
assert.equal(scene.currentVersion.dependencies[0].targetVersionId,model.project.publishedVersionId);
assert.equal(training.currentVersion.dependencies[0].targetVersionId,scene.project.publishedVersionId);
assert.equal(training.currentVersion.content.steps.flatMap(s=>s.modelActions).length,item.actions);
await page.locator('.production-chain button').nth(3).click();await expect(page.getByRole('button',{name:'跳过讲解',exact:true})).toBeVisible({timeout:120000});
await page.screenshot({path:dir+'/'+item.taskCode+'-simulation.png'});
checks.push({name:item.taskCode+' 模型→场景→编排发布→原虚拟仿真',pass:true});
}
const current=[];
for(const item of fixture.items){
const stages={};for(const [stage,id] of [['model',item.modelProjectId],['scene',item.sceneProjectId],['training',item.trainingProjectId]]){const data=await api('content/projects/'+id);stages[stage]={id,name:data.project.name,versionId:data.project.publishedVersionId,versionCode:data.currentVersion.versionCode,dependencies:data.currentVersion.dependencies};fs.writeFileSync(dir+'/final-'+stage+'-'+item.taskCode+'.json',JSON.stringify(data,null,2));}
current.push({taskCode:item.taskCode,taskId:item.taskId,steps:item.steps,actions:item.actions,...stages});
}
for(const id of [86,87,88,89,116]){const before=JSON.parse(fs.readFileSync(dir+'/before-task-'+id+'.json')),after=await api('teaching/assignments/'+id);delete before.serverNow;delete after.serverNow;assert.deepEqual(after,before);}
checks.push({name:'五个原任务完整快照未变',pass:true});
fs.writeFileSync(dir+'/integration-result.json',JSON.stringify({pass:true,checks,current},null,2));console.log(JSON.stringify({pass:true,checks:checks.length}));
}catch(e){fs.writeFileSync(dir+'/integration-failure.json',JSON.stringify({pass:false,message:e.message,checks},null,2));throw e;}finally{await browser.close();}

Laden…
Abbrechen
Speichern