Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 

84 KiB

实装与对抗训练实测报告 · 2026-09-05

状态:部分链路已验证,页面闭环受限。证据组 2/2 已完成;检查通过 26,受限 2,失败 0,待验证 0;已校验截图 25 张。

编排与 API 已验证,教学页面闭环受限。需完成默认教学页面与正式任务运行的接线,才能从页面走完新模型对应的训练。

范围:编排 UI、默认教学入口、正式教学 API 分开验证。

1 · 资料

  • 实际查看《维修训练内容设计.pdf》第 1–3、5–6 页扫描件。第 1–2 页将虚拟、混合现实、AI 指导下的实装组织为递进训练,流程包含构建场景、编排任务、组班下发、学员训练与数据报告。
  • 本次使用狐狸与牛奶卡车测试素材验证软件链路,不将其视为原文装备维修内容或真实维修能力考核。原文已查看页面没有单列红蓝对抗规则;对抗依据现有教学实施、渠道合同、命令幂等及步骤事实文档验证。
  • 合同来源:unreal_tran_api/wiki/14-教学实施模块.md、19-训练定义渠道合同.md、20-教学命令幂等.md、21-教学步骤运行事实.md。

2 · 产品原型

  • 沿用现有训练编排工作台与模式配置表单,分别建立实装和对抗内容;保存、发布后重新打开检查步骤、分值、封面与模式参数。
  • 实装默认运行入口为离线识别回放;对抗默认入口为本地工作台。分别记录界面实际行为,再通过正式 API 验证服务端教学闭环。

3 · 设计

  • 实装配置三步人工确认,分值 30 / 30 / 40;完成进度按步骤推导为 33 / 67 / 100。验证失败重试、顺序约束、事件身份及重复事件处理。
  • 对抗配置红蓝两队、每队两人,步骤和提交均由 COMMANDER 执行;OPERATOR 用于岗位拒绝测试。验证同队共享运行、跨队隔离、服务器时限、提交与教员评定。
  • 任务固定引用已发布训练版本;只创建本次开发测试数据。人工确认和客户端模拟参数均明确标识为测试,不连接真实工位、PLC、摄像头或视觉服务。

4 · 开发

  • 修复模式配置被旧channelProfiles覆盖,保持默认profile与modeConfig一致;对抗分钟/秒及旧roundDuration兼容。
  • 保留显式空配置文本;每队人数范围与服务端1–200一致,无效数字阻止发布,不静默夹断。
  • 保留步骤设备事件、人工确认、岗位与运行编码;增删排序后重新计算严格递增进度。
  • 修复复制步骤编码冲突(含大小写),复制动作岗位映射随新动作编码迁移。
  • 显式清空allowedPositions不再补回默认三岗位,保持权限边界并交由正式发布校验阻断。
  • 修复instruction/summary双界面操作说明丢失及显式空提示恢复演示内容;用户实际编辑时同步。
  • 正式targetId优先,目标选择同步语义引用、节点与动作,防止旧targetRef把新目标回退。
  • 沿用上轮训练3D视口截图方案:受控TRAINING_COVER上传、同次保存资产与内容,保存/发布后列表正确显示截图。
  • 本次均为正式工程合同适配与bugfix,未新增数据库字段、执行DDL或更改原型业务流程;测试数据通过开发API创建。

两种模式的验证边界

模式 正式渠道 本轮 API 验证方法 必检项 默认界面边界
实装实训 PHYSICAL 正式 API 的 MANUAL 人工事件 三步顺序、失败重试、来源权限、事件幂等、提交与评定 默认离线识别页;设备 / 视觉接入另行联调
对抗训练 CONFRONTATION 正式 API 的红蓝两队共享运行 岗位权限、两队隔离、顺序、时限、命令幂等、提交与评定 默认本地工作台;不混同正式 API 运行

保留开发测试数据

训练 ID 名称 模式 状态 当前发布版本 API 已测版本 步骤数
163 实测训练-牛奶卡车实装检查-20260905 PHYSICAL PUBLISHED 217 215 3
164 实测训练-狐狸红蓝协同巡检-20260905 CONFRONTATION PUBLISHED 216 216 2

训练 163:API任务50固定215;说明字段兼容修复后在217做UI及封面回归,未改写任务快照。

正式教学任务与运行

任务 ID 运行 ID 渠道 队伍 状态 完成度 评定分
50 44 PHYSICAL NEUTRAL REVIEWED 100 100
51 45 CONFRONTATION RED REVIEWED 100 100
51 46 CONFRONTATION BLUE REVIEWED 100 100

随机测试密码与登录会话不写入报告;保留新建数据用于追溯。

5 · 测试

实装实训与对抗训练真实流程测试 · 部分链路已验证,页面闭环受限

证据类型:UI。

  • 实装:创建、绑定牛奶卡车、3步配置、保存重开与发布:通过
  • 对抗:每队人数为小数时阻止发布:通过
  • 对抗:显式清空岗位后保存重开,不恢复默认授权:通过
  • 对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布:通过
  • 实装:说明兼容修复回写,重新截图封面及发布:通过
  • 实装默认页面:离线播放与候选复核可操作,无法执行新卡车正式步骤:受限(需功能接线)
  • 对抗默认页面:新建下拉仅有本地液压预设,无法选择已发布狐狸训练:受限(需功能接线)
  • 训练列表两种模式展示各自真实截图封面:通过
  • 牛奶卡车实装检查:发布重开、模型实际可见、截图封面:通过
  • 狐狸红蓝协同巡检:发布重开、模型实际可见、截图封面:通过
evidence.json · 脱敏完整记录
{
  "title": "实装实训与对抗训练真实流程测试",
  "startedAt": "2026-09-05T15:37:32.805Z",
  "status": "PASS_WITH_LIMITATIONS",
  "checks": [
    {
      "title": "实装:创建、绑定牛奶卡车、3步配置、保存重开与发布",
      "status": "PASS",
      "screenshots": [
        "screenshots/physical-settings-reopen.png",
        "screenshots/physical-authoring.png",
        "screenshots/physical-preflight.png",
        "screenshots/physical-published.png"
      ],
      "details": {
        "id": "163",
        "version": "215",
        "progress": [
          33,
          67,
          100
        ],
        "profile": {
          "type": "PHYSICAL",
          "stationName": "牛奶卡车测试工位 A",
          "equipmentCode": "E2E-TRUCK-163",
          "gatewayProtocol": "HTTP 测试网关",
          "sensorChannels": "人工外观确认;测试状态点位",
          "safetyInterlocks": "测试工位隔离确认"
        }
      }
    },
    {
      "title": "对抗:每队人数为小数时阻止发布",
      "status": "PASS",
      "screenshots": [
        "screenshots/confrontation-invalid-team-size.png"
      ],
      "details": {
        "input": 2.5,
        "message": "发布校验未通过\n×\n发现 1 个必须处理的问题\n\n2 个步骤 · 1 个目标 · 0 个故障 · 及格线 80 分\n\n!\n每队人数必须是 1 到 200 的整数\nmodeConfig.teamSize\ni\n本步骤尚未配置必选安全前置条件\nsteps[0].safetyRules\ni\n本步骤尚未配置必选安全前置条件\nsteps[1].safetyRules\ni\n尚未绑定离线讲解文件,运行时将保留字幕并使用默认本地语音\nsteps.audio\n关闭"
      }
    },
    {
      "title": "对抗:显式清空岗位后保存重开,不恢复默认授权",
      "status": "PASS",
      "screenshots": [
        "screenshots/confrontation-empty-positions.png"
      ],
      "details": {
        "allowedPositions": [],
        "published": false
      }
    },
    {
      "title": "对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布",
      "status": "PASS",
      "screenshots": [
        "screenshots/confrontation-positions.png",
        "screenshots/confrontation-settings-reopen.png",
        "screenshots/confrontation-authoring.png",
        "screenshots/confrontation-preflight.png",
        "screenshots/confrontation-published.png"
      ],
      "details": {
        "id": "164",
        "version": "216",
        "profile": {
          "type": "CONFRONTATION",
          "redTeamName": "狐狸巡检红队",
          "blueTeamName": "狐狸巡检蓝队",
          "teamSize": 2,
          "roundDurationMinutes": 7,
          "objective": "两队分别完成狐狸模型巡检并确认状态",
          "winRule": "按已完成步骤与协同检查结果评分",
          "submitPositions": [
            "COMMANDER"
          ],
          "roundDuration": 7,
          "eventInjections": ""
        },
        "steps": [
          {
            "title": "识别狐狸巡检目标",
            "summary": "红蓝两队分别识别场景中的狐狸模型,由指挥员确认目标。",
            "allowedPositions": [
              "COMMANDER"
            ]
          },
          {
            "title": "完成狐狸状态检查",
            "summary": "由指挥员完成状态确认,操作员协同观察;提交团队检查结果。",
            "allowedPositions": [
              "COMMANDER"
            ]
          }
        ]
      }
    },
    {
      "title": "实装:说明兼容修复回写,重新截图封面及发布",
      "status": "PASS",
      "screenshots": [
        "screenshots/physical-final-published.png"
      ],
      "details": {
        "projectId": "163",
        "publishedVersionId": "217",
        "apiTestVersionId": "215",
        "note": "既有任务50固定215;本次仅回填说明与更新截图封面,未改写任务快照。",
        "steps": [
          {
            "stepCode": "PHYSICAL-S01",
            "actionCode": "PHYSICAL-A01",
            "summary": "核对测试工位与牛奶卡车编号。",
            "physicalEventRule": {
              "source": "MANUAL",
              "eventType": "step.physical-s01.confirmed",
              "progressPercent": 33,
              "manualConfirmAllowed": true
            }
          },
          {
            "stepCode": "PHYSICAL-S02",
            "actionCode": "PHYSICAL-A02",
            "summary": "人工确认卡车外观状态,使用受控事件记录检查。",
            "physicalEventRule": {
              "source": "MANUAL",
              "eventType": "step.physical-s02.confirmed",
              "progressPercent": 67,
              "manualConfirmAllowed": true
            }
          },
          {
            "stepCode": "PHYSICAL-S03",
            "actionCode": "PHYSICAL-A03",
            "summary": "核实步骤记录并提交检查结果。",
            "physicalEventRule": {
              "source": "MANUAL",
              "eventType": "step.physical-s03.confirmed",
              "progressPercent": 100,
              "manualConfirmAllowed": true
            }
          }
        ]
      }
    },
    {
      "title": "实装默认页面:离线播放与候选复核可操作,无法执行新卡车正式步骤",
      "status": "BLOCKED",
      "screenshots": [
        "screenshots/physical-teaching-list.png",
        "screenshots/physical-offline-preview.png",
        "screenshots/physical-offline-confirmed.png"
      ],
      "details": {
        "assignmentId": "50",
        "previewUrl": "/teaching/physical-training/preview/50",
        "observed": "进入任务后仍显示固定45秒维修识别录像,确认只改变本次页面候选,未加载卡车三步正式运行。",
        "businessWrites": [],
        "required": "需将正式任务快照、场景、人工/设备事件及运行记录接入工作台。"
      }
    },
    {
      "title": "对抗默认页面:新建下拉仅有本地液压预设,无法选择已发布狐狸训练",
      "status": "BLOCKED",
      "screenshots": [
        "screenshots/confrontation-final-entry.png",
        "screenshots/confrontation-final-local-create.png"
      ],
      "details": {
        "options": [
          "桥梁架设装备液压系统检测与维修训练"
        ],
        "formalAssignmentId": "51",
        "formalAssignmentExists": true,
        "required": "需将默认任务清单、创建表单和执行入口接入正式对抗任务服务,并确认通用巡检与五阶段排故的适用范围。"
      }
    },
    {
      "title": "训练列表两种模式展示各自真实截图封面",
      "status": "PASS",
      "screenshots": [
        "screenshots/physical-list-cover.png",
        "screenshots/confrontation-list-cover.png"
      ],
      "details": {
        "projects": [
          "163",
          "164"
        ],
        "authenticatedImagesLoaded": true
      }
    },
    {
      "title": "牛奶卡车实装检查:发布重开、模型实际可见、截图封面",
      "status": "PASS",
      "screenshots": [
        "screenshots/physical-independent-reopen.png",
        "screenshots/physical-independent-settings.png",
        "screenshots/physical-final-cover.jpg"
      ],
      "details": {
        "state": "ready",
        "meshes": 5,
        "visibleMeshes": 5,
        "skinnedMeshes": 0,
        "position": [
          4.2,
          0.20617168998977797,
          -0.4356763541543951
        ],
        "scale": [
          1.2323086149245381,
          1.2323086149245381,
          1.2323086149245381
        ],
        "finiteBounds": true,
        "size": [
          3.4406057210321332,
          3.390913184034896,
          6.000000000000001
        ],
        "camera": {
          "position": [
            10.565439077211714,
            5.0456876852867705,
            7.37050840519251
          ],
          "target": [
            4.2,
            1.695456592017448,
            0
          ]
        },
        "sample": {
          "width": 320,
          "height": 180,
          "changedPixels": 11669
        },
        "scene": {
          "projectId": "159",
          "versionId": "209"
        },
        "targets": [
          {
            "id": "scene:OBJ-1788605199171-2",
            "name": "牛奶卡车检验主装备",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605199171-2/part/interaction-1ba581e6",
            "name": "Yup2Zup",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605199171-2/part/interaction-004dec41",
            "name": "Cesium_Milk_Truck",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605199171-2/part/interaction-5f7e4bee",
            "name": "Cesium_Milk_Truck_1",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605199171-2/part/interaction-607e4d81",
            "name": "Cesium_Milk_Truck_2",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605199171-2/part/interaction-5d7e48c8",
            "name": "Cesium_Milk_Truck_3",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605199171-2/part/interaction-5e7e4a5b",
            "name": "Node",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605199171-2/part/interaction-ce2feac4",
            "name": "Wheels",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605199171-2/part/interaction-637e523a",
            "name": "Node001",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605199171-2/part/interaction-f417f82d",
            "name": "Wheels001",
            "source": "scene"
          }
        ],
        "steps": [
          {
            "title": "核对卡车工位与装备",
            "targetId": "scene:OBJ-1788605199171-2",
            "weight": 30
          },
          {
            "title": "检查卡车外观状态",
            "targetId": "scene:OBJ-1788605199171-2",
            "weight": 30
          },
          {
            "title": "确认检查完成",
            "targetId": "scene:OBJ-1788605199171-2",
            "weight": 40
          }
        ],
        "cover": {
          "assetId": "2111",
          "bytes": 42163,
          "sha256": "efb7060464f840bde78744d89567c609dc7b7922216d1c948fe22c1eed3d41a4",
          "width": 628,
          "height": 640
        },
        "pageErrors": [],
        "blockedWrites": []
      }
    },
    {
      "title": "狐狸红蓝协同巡检:发布重开、模型实际可见、截图封面",
      "status": "PASS",
      "screenshots": [
        "screenshots/confrontation-independent-reopen.png",
        "screenshots/confrontation-independent-settings.png",
        "screenshots/confrontation-final-cover.jpg"
      ],
      "details": {
        "state": "ready",
        "meshes": 1,
        "visibleMeshes": 1,
        "skinnedMeshes": 1,
        "position": [
          -7.5,
          0.10930174852949677,
          0.3440109341705351
        ],
        "scale": [
          0.03616375932570189,
          0.03616375932570189,
          0.03616375932570189
        ],
        "finiteBounds": true,
        "size": [
          0.9658796100519353,
          2.811899945787403,
          6
        ],
        "camera": {
          "position": [
            -0.04179225073787585,
            6.099634646023559,
            17.355036987378313
          ],
          "target": [
            -7.512232860974557,
            1.4059499759307126,
            -2.2204460492503126e-16
          ]
        },
        "sample": {
          "width": 320,
          "height": 180,
          "changedPixels": 827
        },
        "scene": {
          "projectId": "160",
          "versionId": "210"
        },
        "targets": [
          {
            "id": "scene:OBJ-1788605873329-2",
            "name": "维修装备主体 02",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605916565-6",
            "name": "狐狸野外巡检目标",
            "source": "scene"
          },
          {
            "id": "scene:OBJ-1788605916565-6/part/interaction-a3d35c26",
            "name": "fox",
            "source": "scene"
          }
        ],
        "steps": [
          {
            "title": "识别狐狸巡检目标",
            "targetId": "scene:OBJ-1788605916565-6",
            "weight": 50
          },
          {
            "title": "完成狐狸状态检查",
            "targetId": "scene:OBJ-1788605916565-6",
            "weight": 50
          }
        ],
        "cover": {
          "assetId": "2108",
          "bytes": 51257,
          "sha256": "79779a9d1e40c0c160fa3dab25e83931639942f53e2ee3a5da12f9f86ad70081",
          "width": 628,
          "height": 640
        },
        "pageErrors": [],
        "blockedWrites": []
      }
    }
  ],
  "projects": [
    {
      "id": "163",
      "name": "实测训练-牛奶卡车实装检查-20260905",
      "mode": "PHYSICAL",
      "status": "PUBLISHED",
      "currentVersionId": "217",
      "publishedVersionId": "217",
      "sceneProjectId": "159",
      "sceneVersionId": "209",
      "stepCount": 3,
      "totalScore": 100,
      "cover": {
        "assetId": "2111",
        "image": "screenshots/physical-final-cover.jpg"
      },
      "trainingMode": "PHYSICAL",
      "modeConfig": {
        "type": "PHYSICAL",
        "stationName": "牛奶卡车测试工位 A",
        "equipmentCode": "E2E-TRUCK-163",
        "gatewayProtocol": "HTTP 测试网关",
        "sensorChannels": "人工外观确认;测试状态点位",
        "safetyInterlocks": "测试工位隔离确认"
      },
      "steps": [
        {
          "stepCode": "PHYSICAL-S01",
          "actionCode": "PHYSICAL-A01",
          "title": "核对卡车工位与装备",
          "summary": "核对测试工位与牛奶卡车编号。",
          "targetId": "scene:OBJ-1788605199171-2",
          "allowedPositions": [],
          "physicalEventRule": {
            "source": "MANUAL",
            "eventType": "step.physical-s01.confirmed",
            "progressPercent": 33,
            "manualConfirmAllowed": true
          },
          "score": 30
        },
        {
          "stepCode": "PHYSICAL-S02",
          "actionCode": "PHYSICAL-A02",
          "title": "检查卡车外观状态",
          "summary": "人工确认卡车外观状态,使用受控事件记录检查。",
          "targetId": "scene:OBJ-1788605199171-2",
          "allowedPositions": [],
          "physicalEventRule": {
            "source": "MANUAL",
            "eventType": "step.physical-s02.confirmed",
            "progressPercent": 67,
            "manualConfirmAllowed": true
          },
          "score": 30
        },
        {
          "stepCode": "PHYSICAL-S03",
          "actionCode": "PHYSICAL-A03",
          "title": "确认检查完成",
          "summary": "核实步骤记录并提交检查结果。",
          "targetId": "scene:OBJ-1788605199171-2",
          "allowedPositions": [],
          "physicalEventRule": {
            "source": "MANUAL",
            "eventType": "step.physical-s03.confirmed",
            "progressPercent": 100,
            "manualConfirmAllowed": true
          },
          "score": 40
        }
      ],
      "apiTestVersionId": "215",
      "apiVersionNote": "API任务50固定215;说明字段兼容修复后在217做UI及封面回归,未改写任务快照。"
    },
    {
      "id": "164",
      "name": "实测训练-狐狸红蓝协同巡检-20260905",
      "mode": "CONFRONTATION",
      "status": "PUBLISHED",
      "currentVersionId": "216",
      "publishedVersionId": "216",
      "sceneProjectId": "160",
      "sceneVersionId": "210",
      "stepCount": 2,
      "totalScore": 100,
      "cover": {
        "assetId": "2108",
        "image": "screenshots/confrontation-final-cover.jpg"
      },
      "trainingMode": "CONFRONTATION",
      "modeConfig": {
        "type": "CONFRONTATION",
        "redTeamName": "狐狸巡检红队",
        "blueTeamName": "狐狸巡检蓝队",
        "teamSize": 2,
        "roundDurationMinutes": 7,
        "objective": "两队分别完成狐狸模型巡检并确认状态",
        "winRule": "按已完成步骤与协同检查结果评分",
        "submitPositions": [
          "COMMANDER"
        ],
        "roundDuration": 7,
        "eventInjections": ""
      },
      "steps": [
        {
          "stepCode": "CONFRONTATION-S01",
          "actionCode": "CONFRONTATION-A01",
          "title": "识别狐狸巡检目标",
          "summary": "红蓝两队分别识别场景中的狐狸模型,由指挥员确认目标。",
          "targetId": "scene:OBJ-1788605916565-6",
          "allowedPositions": [
            "COMMANDER"
          ],
          "physicalEventRule": {},
          "score": 50
        },
        {
          "stepCode": "CONFRONTATION-S02",
          "actionCode": "CONFRONTATION-A02",
          "title": "完成狐狸状态检查",
          "summary": "由指挥员完成状态确认,操作员协同观察;提交团队检查结果。",
          "targetId": "scene:OBJ-1788605916565-6",
          "allowedPositions": [
            "COMMANDER"
          ],
          "physicalEventRule": {},
          "score": 50
        }
      ],
      "apiTestVersionId": "216"
    }
  ],
  "bugs": [],
  "limitations": [
    "实装为MANUAL人工事件合同测试,没有接真实PLC、摄像头、视觉识别服务或安全联锁设备。",
    "两步狐狸巡检用于通用步骤与红蓝岗位合同,不是需要CONF-INSPECT等五个固定编码的完整排故作业;未宣称排故工作台阶段回写已打通。",
    "对抗验证服务端设置了7分钟截止时刻,未等待自然超时触发;未把API账号请求视为多人跨浏览器实时协同UI测试。",
    "牛奶卡车沿用原发布场景的视角,车间立柱遮挡部分车身;真实模型已渲染且封面来自该视口。",
    "实装当前发布217仅回填兼容说明并更新封面;已完成教学任务50仍固定不可变版本215。",
    "历史仅positionActions设置动作岗位的定义在教学读取侧与当前发布预检存在口径差异;本次按现有发布规则配置非空allowedPositions,未改后端岗位合同。",
    "开发环境列表切换曾出现一次15秒超时,刷新后恢复;独立复测两种模式共6次查询均HTTP 200、耗时247–403ms,未复现稳定慢查询。详见list-api-latency.json。"
  ],
  "pageErrors": [],
  "development": [
    "修复模式配置被旧channelProfiles覆盖,保持默认profile与modeConfig一致;对抗分钟/秒及旧roundDuration兼容。",
    "保留显式空配置文本;每队人数范围与服务端1–200一致,无效数字阻止发布,不静默夹断。",
    "保留步骤设备事件、人工确认、岗位与运行编码;增删排序后重新计算严格递增进度。",
    "修复复制步骤编码冲突(含大小写),复制动作岗位映射随新动作编码迁移。",
    "显式清空allowedPositions不再补回默认三岗位,保持权限边界并交由正式发布校验阻断。",
    "修复instruction/summary双界面操作说明丢失及显式空提示恢复演示内容;用户实际编辑时同步。",
    "正式targetId优先,目标选择同步语义引用、节点与动作,防止旧targetRef把新目标回退。",
    "沿用上轮训练3D视口截图方案:受控TRAINING_COVER上传、同次保存资产与内容,保存/发布后列表正确显示截图。",
    "本次均为正式工程合同适配与bugfix,未新增数据库字段、执行DDL或更改原型业务流程;测试数据通过开发API创建。"
  ],
  "commands": [
    {
      "command": "pnpm run build",
      "status": "PASS",
      "details": "vue-tsc、Vite构建和legacy iframe分发校验均通过;仅已有包体积提示。"
    },
    {
      "command": "node --test tools/training-*.test.mjs(本次8个聚焦文件)",
      "status": "PASS",
      "details": "94/94通过,涵盖封面、场景、真实模型、目标、元数据、模式与步骤兼容。"
    },
    {
      "command": "pnpm run test:training-legacy",
      "status": "PASS",
      "details": "两项既有训练/地形合同检查通过。"
    },
    {
      "command": "content-training-studio.spec.ts / Edge",
      "status": "PASS",
      "details": "7/7通过(mock API的工作台回归),无跳过、无不稳定用例,独立报告studio-regression/html/index.html。"
    },
    {
      "command": "UTE2E_TRAINING_MODES=1 UTE2E_BASE_URL=http://192.168.31.168:6180 node tools/training-scene-visibility.e2e.mjs",
      "status": "PASS",
      "details": "独立Edge会话2/2通过,真实LAN服务、禁止业务写请求;模型可见像素差、固定场景依赖、JPEG尺寸与SHA-256全部通过。"
    },
    {
      "command": "node tools/training-modes-api-live.mjs(163:215;164:216)",
      "status": "PASS",
      "details": "18/18检查组通过,90次正式教学API请求;含训练/场景/模型固定快照只读复核,详见api-evidence.json。"
    }
  ],
  "confirmationItems": [
    "将实装工作台从固定离线识别回放扩展为读取正式任务、已发布场景和步骤,并接入人工/设备事件、提交与结果。",
    "将对抗默认入口从本地预设任务接入正式任务清单、创建和执行链路;确认通用巡检步骤与五阶段排故工作台的适用范围。"
  ],
  "finishedAt": "2026-09-05T15:58:39.239Z"
}

实装人工事件与红蓝对抗正式 API 闭环 · 通过

证据类型:API。

  • 开始命令重放不会重复激活或递增次数:通过
  • 实装任务固定发布版本、分配学员并开始:通过
  • 实装拒绝普通进度接口、抢跑、提前提交及错误事件身份:通过
  • 实装不匹配事件失败,必须重试后才可继续:通过
  • 实装逐步人工确认、事件幂等与载荷冲突:通过
  • PHYSICAL 提交、教员评定与结果回读:通过
  • 对抗红蓝两队各两人,按队伍共享且运行互相独立:通过
  • 对抗学员不能读取另一队运行明细:通过
  • 开始命令重放不会重复激活或递增次数:通过
  • RED 队岗位越权、跳步和提前提交均拒绝:通过
  • RED 队授权动作逐步推进、重放幂等、提交负责人限制:通过
  • CONFRONTATION 提交、教员评定与结果回读:通过
  • 红队完成不会推进蓝队进度:通过
  • 开始命令重放不会重复激活或递增次数:通过
  • BLUE 队岗位越权、跳步和提前提交均拒绝:通过
  • BLUE 队授权动作逐步推进、重放幂等、提交负责人限制:通过
  • CONFRONTATION 提交、教员评定与结果回读:通过
  • 正式任务快照固定训练、场景与模型发布版本:通过
api-evidence.json · 脱敏完整记录
{
  "title": "实装人工事件与红蓝对抗正式 API 闭环",
  "status": "PASS",
  "startedAt": "2026-09-05T15:48:46.342Z",
  "baseURL": "http://127.0.0.1:6180",
  "method": "真实 Auth / Gateway / Tran API;新建隔离学生与开发测试任务;不连接设备、视觉推理或外部通知。",
  "references": {
    "PHYSICAL": {
      "projectId": "163",
      "versionId": "215"
    },
    "CONFRONTATION": {
      "projectId": "164",
      "versionId": "216"
    }
  },
  "checks": [
    {
      "title": "开始命令重放不会重复激活或递增次数",
      "status": "PASS",
      "details": {
        "runId": "44",
        "version": 2,
        "attemptCount": 1
      }
    },
    {
      "title": "实装任务固定发布版本、分配学员并开始",
      "status": "PASS",
      "details": {
        "assignmentId": "50",
        "run": {
          "id": "44",
          "assignmentId": "50",
          "status": "IN_PROGRESS",
          "version": 2,
          "progressPercent": 0,
          "currentStepCode": "PHYSICAL-S01",
          "score": null,
          "passed": null
        },
        "content": {
          "projectId": "163",
          "versionId": "215"
        },
        "rules": [
          {
            "stepCode": "PHYSICAL-S01",
            "source": "MANUAL",
            "eventType": "step.physical-s01.confirmed",
            "progressPercent": 33
          },
          {
            "stepCode": "PHYSICAL-S02",
            "source": "MANUAL",
            "eventType": "step.physical-s02.confirmed",
            "progressPercent": 67
          },
          {
            "stepCode": "PHYSICAL-S03",
            "source": "MANUAL",
            "eventType": "step.physical-s03.confirmed",
            "progressPercent": 100
          }
        ]
      }
    },
    {
      "title": "实装拒绝普通进度接口、抢跑、提前提交及错误事件身份",
      "status": "PASS",
      "details": {
        "runId": "44",
        "forbiddenProgress": {
          "status": 403,
          "message": "实装运行只能由设备、视觉或人工确认事件推进"
        },
        "prematureSubmit": {
          "status": 409,
          "message": "训练步骤尚未全部完成,不能提交"
        },
        "skipped": {
          "status": 409,
          "message": "物理事件必须匹配当前待完成步骤,不能跳步或回退"
        },
        "untrustedDevice": {
          "status": 403,
          "message": "无权执行此操作"
        },
        "managerManual": {
          "status": 403,
          "message": "当前学员不是该运行成员"
        },
        "progressUnchanged": 0
      }
    },
    {
      "title": "实装不匹配事件失败,必须重试后才可继续",
      "status": "PASS",
      "details": {
        "runId": "44",
        "outcome": "UNMATCHED",
        "beforeRetry": {
          "status": 409,
          "message": "实装步骤失败后必须先重试,完成后不能再次推进"
        },
        "facts": [
          {
            "stepCode": "PHYSICAL-S01",
            "status": "IN_PROGRESS",
            "targetProgressPercent": 33,
            "attemptCount": 2,
            "failureCount": 1,
            "evidenceSourceCode": "COMMAND"
          },
          {
            "stepCode": "PHYSICAL-S02",
            "status": "PENDING",
            "targetProgressPercent": 67,
            "attemptCount": 0,
            "failureCount": 0,
            "evidenceSourceCode": "NONE"
          },
          {
            "stepCode": "PHYSICAL-S03",
            "status": "PENDING",
            "targetProgressPercent": 100,
            "attemptCount": 0,
            "failureCount": 0,
            "evidenceSourceCode": "NONE"
          }
        ]
      }
    },
    {
      "title": "实装逐步人工确认、事件幂等与载荷冲突",
      "status": "PASS",
      "details": {
        "runId": "44",
        "progression": [
          {
            "stepCode": "PHYSICAL-S01",
            "eventId": "MANUAL-FF103EF3-B498-4EAC-8F06-42DE46CC9CE4",
            "progress": 33,
            "replayVersion": 5,
            "alteredEventStatus": 409
          },
          {
            "stepCode": "PHYSICAL-S02",
            "eventId": "MANUAL-9D8A36B7-C161-4736-A71E-D1700E55216B",
            "progress": 67,
            "replayVersion": 6,
            "alteredEventStatus": 409
          },
          {
            "stepCode": "PHYSICAL-S03",
            "eventId": "MANUAL-1D1ADE8F-D32B-434B-A7D8-A2571E502137",
            "progress": 100,
            "replayVersion": 7,
            "alteredEventStatus": 409
          }
        ],
        "facts": [
          {
            "stepCode": "PHYSICAL-S01",
            "status": "COMPLETED",
            "targetProgressPercent": 33,
            "attemptCount": 2,
            "failureCount": 1,
            "evidenceSourceCode": "MANUAL"
          },
          {
            "stepCode": "PHYSICAL-S02",
            "status": "COMPLETED",
            "targetProgressPercent": 67,
            "attemptCount": 1,
            "failureCount": 0,
            "evidenceSourceCode": "MANUAL"
          },
          {
            "stepCode": "PHYSICAL-S03",
            "status": "COMPLETED",
            "targetProgressPercent": 100,
            "attemptCount": 1,
            "failureCount": 0,
            "evidenceSourceCode": "MANUAL"
          }
        ]
      }
    },
    {
      "title": "PHYSICAL 提交、教员评定与结果回读",
      "status": "PASS",
      "details": {
        "run": {
          "id": "44",
          "assignmentId": "50",
          "status": "REVIEWED",
          "version": 9,
          "progressPercent": 100,
          "currentStepCode": "PHYSICAL-S03",
          "score": 100,
          "passed": true
        },
        "submitReplay": true,
        "reviewReplay": true,
        "facts": [
          {
            "stepCode": "PHYSICAL-S01",
            "status": "COMPLETED",
            "targetProgressPercent": 33,
            "attemptCount": 2,
            "failureCount": 1,
            "evidenceSourceCode": "MANUAL"
          },
          {
            "stepCode": "PHYSICAL-S02",
            "status": "COMPLETED",
            "targetProgressPercent": 67,
            "attemptCount": 1,
            "failureCount": 0,
            "evidenceSourceCode": "MANUAL"
          },
          {
            "stepCode": "PHYSICAL-S03",
            "status": "COMPLETED",
            "targetProgressPercent": 100,
            "attemptCount": 1,
            "failureCount": 0,
            "evidenceSourceCode": "MANUAL"
          }
        ]
      }
    },
    {
      "title": "对抗红蓝两队各两人,按队伍共享且运行互相独立",
      "status": "PASS",
      "details": {
        "assignmentId": "51",
        "content": {
          "projectId": "164",
          "versionId": "216"
        },
        "configuration": {
          "teamSize": 2,
          "roundDurationMinutes": 7,
          "submitPositions": [
            "COMMANDER"
          ]
        },
        "runs": [
          {
            "id": "46",
            "assignmentId": "51",
            "status": "ACCEPTED",
            "version": 1,
            "progressPercent": 0,
            "currentStepCode": "",
            "score": null,
            "passed": null
          },
          {
            "id": "45",
            "assignmentId": "51",
            "status": "ACCEPTED",
            "version": 1,
            "progressPercent": 0,
            "currentStepCode": "",
            "score": null,
            "passed": null
          }
        ]
      }
    },
    {
      "title": "对抗学员不能读取另一队运行明细",
      "status": "PASS",
      "details": {
        "requestingTeam": "RED",
        "otherRunId": "46",
        "response": {
          "status": 403,
          "message": "无权查看该训练运行"
        }
      }
    },
    {
      "title": "开始命令重放不会重复激活或递增次数",
      "status": "PASS",
      "details": {
        "runId": "45",
        "version": 2,
        "attemptCount": 1
      }
    },
    {
      "title": "RED 队岗位越权、跳步和提前提交均拒绝",
      "status": "PASS",
      "details": {
        "runId": "45",
        "wrongPosition": {
          "status": 403,
          "message": "当前岗位不能推进共享训练进度"
        },
        "skipped": {
          "status": 400,
          "message": "必须按发布顺序完成当前步骤,不能跳步或回退"
        },
        "earlySubmit": {
          "status": 409,
          "message": "训练步骤尚未全部完成,不能提交"
        },
        "deadlineAt": 1788623936,
        "serverNow": 1788623517
      }
    },
    {
      "title": "RED 队授权动作逐步推进、重放幂等、提交负责人限制",
      "status": "PASS",
      "details": {
        "runId": "45",
        "progression": [
          {
            "stepCode": "CONFRONTATION-S01",
            "progress": 50,
            "replayVersion": 3,
            "changedCommandStatus": 409
          },
          {
            "stepCode": "CONFRONTATION-S02",
            "progress": 100,
            "replayVersion": 4,
            "changedCommandStatus": 409
          }
        ],
        "wrongSubmitPosition": {
          "status": 403,
          "message": "共享训练仅允许任务声明的负责人岗位提交结果"
        }
      }
    },
    {
      "title": "CONFRONTATION 提交、教员评定与结果回读",
      "status": "PASS",
      "details": {
        "run": {
          "id": "45",
          "assignmentId": "51",
          "status": "REVIEWED",
          "version": 6,
          "progressPercent": 100,
          "currentStepCode": "CONFRONTATION-S02",
          "score": 100,
          "passed": true
        },
        "submitReplay": true,
        "reviewReplay": true,
        "facts": [
          {
            "stepCode": "CONFRONTATION-S01",
            "status": "COMPLETED",
            "targetProgressPercent": 50,
            "attemptCount": 1,
            "failureCount": 0,
            "evidenceSourceCode": "COMMAND"
          },
          {
            "stepCode": "CONFRONTATION-S02",
            "status": "COMPLETED",
            "targetProgressPercent": 100,
            "attemptCount": 1,
            "failureCount": 0,
            "evidenceSourceCode": "COMMAND"
          }
        ]
      }
    },
    {
      "title": "红队完成不会推进蓝队进度",
      "status": "PASS",
      "details": {
        "redRunId": "45",
        "blueRun": {
          "id": "46",
          "assignmentId": "51",
          "status": "ACCEPTED",
          "version": 1,
          "progressPercent": 0,
          "currentStepCode": "",
          "score": null,
          "passed": null
        }
      }
    },
    {
      "title": "开始命令重放不会重复激活或递增次数",
      "status": "PASS",
      "details": {
        "runId": "46",
        "version": 2,
        "attemptCount": 1
      }
    },
    {
      "title": "BLUE 队岗位越权、跳步和提前提交均拒绝",
      "status": "PASS",
      "details": {
        "runId": "46",
        "wrongPosition": {
          "status": 403,
          "message": "当前岗位不能推进共享训练进度"
        },
        "skipped": {
          "status": 400,
          "message": "必须按发布顺序完成当前步骤,不能跳步或回退"
        },
        "earlySubmit": {
          "status": 409,
          "message": "训练步骤尚未全部完成,不能提交"
        },
        "deadlineAt": 1788623942,
        "serverNow": 1788623523
      }
    },
    {
      "title": "BLUE 队授权动作逐步推进、重放幂等、提交负责人限制",
      "status": "PASS",
      "details": {
        "runId": "46",
        "progression": [
          {
            "stepCode": "CONFRONTATION-S01",
            "progress": 50,
            "replayVersion": 3,
            "changedCommandStatus": 409
          },
          {
            "stepCode": "CONFRONTATION-S02",
            "progress": 100,
            "replayVersion": 4,
            "changedCommandStatus": 409
          }
        ],
        "wrongSubmitPosition": {
          "status": 403,
          "message": "共享训练仅允许任务声明的负责人岗位提交结果"
        }
      }
    },
    {
      "title": "CONFRONTATION 提交、教员评定与结果回读",
      "status": "PASS",
      "details": {
        "run": {
          "id": "46",
          "assignmentId": "51",
          "status": "REVIEWED",
          "version": 6,
          "progressPercent": 100,
          "currentStepCode": "CONFRONTATION-S02",
          "score": 100,
          "passed": true
        },
        "submitReplay": true,
        "reviewReplay": true,
        "facts": [
          {
            "stepCode": "CONFRONTATION-S01",
            "status": "COMPLETED",
            "targetProgressPercent": 50,
            "attemptCount": 1,
            "failureCount": 0,
            "evidenceSourceCode": "COMMAND"
          },
          {
            "stepCode": "CONFRONTATION-S02",
            "status": "COMPLETED",
            "targetProgressPercent": 100,
            "attemptCount": 1,
            "failureCount": 0,
            "evidenceSourceCode": "COMMAND"
          }
        ]
      }
    },
    {
      "title": "正式任务快照固定训练、场景与模型发布版本",
      "status": "PASS",
      "details": {
        "snapshots": [
          {
            "assignmentId": "50",
            "channel": "PHYSICAL",
            "content": {
              "projectId": "163",
              "versionId": "215"
            },
            "frozenScene": {
              "projectId": "159",
              "versionId": "209",
              "models": [
                {
                  "projectId": "156",
                  "versionId": "204",
                  "assetCode": "MODEL_MTO6WI6K_08-CESIUMMILKTRUCK"
                }
              ]
            }
          },
          {
            "assignmentId": "51",
            "channel": "CONFRONTATION",
            "content": {
              "projectId": "164",
              "versionId": "216"
            },
            "frozenScene": {
              "projectId": "160",
              "versionId": "210",
              "models": [
                {
                  "projectId": "157",
                  "versionId": "201",
                  "assetCode": "MODEL_MTO6V5W6_09-FOX"
                }
              ]
            }
          }
        ]
      }
    }
  ],
  "assignments": [
    {
      "id": "50",
      "code": "UTE2E-MODES-MTOK5BBA-PHYSICAL",
      "channel": "PHYSICAL",
      "contentProjectId": "163",
      "contentVersionId": "215",
      "status": "PUBLISHED",
      "version": 1,
      "frozenScene": {
        "projectId": "159",
        "versionId": "209",
        "models": [
          {
            "projectId": "156",
            "versionId": "204",
            "assetCode": "MODEL_MTO6WI6K_08-CESIUMMILKTRUCK"
          }
        ]
      }
    },
    {
      "id": "51",
      "code": "UTE2E-MODES-MTOK5BBA-CONFRONTATION",
      "channel": "CONFRONTATION",
      "contentProjectId": "164",
      "contentVersionId": "216",
      "status": "PUBLISHED",
      "version": 1,
      "frozenScene": {
        "projectId": "160",
        "versionId": "210",
        "models": [
          {
            "projectId": "157",
            "versionId": "201",
            "assetCode": "MODEL_MTO6V5W6_09-FOX"
          }
        ]
      }
    }
  ],
  "runs": [
    {
      "id": "44",
      "assignmentId": "50",
      "status": "REVIEWED",
      "version": 9,
      "progressPercent": 100,
      "currentStepCode": "PHYSICAL-S03",
      "score": 100,
      "passed": true,
      "channel": "PHYSICAL",
      "team": "NEUTRAL",
      "stepFacts": [
        {
          "stepCode": "PHYSICAL-S01",
          "status": "COMPLETED",
          "targetProgressPercent": 33,
          "attemptCount": 2,
          "failureCount": 1,
          "evidenceSourceCode": "MANUAL"
        },
        {
          "stepCode": "PHYSICAL-S02",
          "status": "COMPLETED",
          "targetProgressPercent": 67,
          "attemptCount": 1,
          "failureCount": 0,
          "evidenceSourceCode": "MANUAL"
        },
        {
          "stepCode": "PHYSICAL-S03",
          "status": "COMPLETED",
          "targetProgressPercent": 100,
          "attemptCount": 1,
          "failureCount": 0,
          "evidenceSourceCode": "MANUAL"
        }
      ]
    },
    {
      "id": "45",
      "assignmentId": "51",
      "status": "REVIEWED",
      "version": 6,
      "progressPercent": 100,
      "currentStepCode": "CONFRONTATION-S02",
      "score": 100,
      "passed": true,
      "channel": "CONFRONTATION",
      "team": "RED",
      "stepFacts": [
        {
          "stepCode": "CONFRONTATION-S01",
          "status": "COMPLETED",
          "targetProgressPercent": 50,
          "attemptCount": 1,
          "failureCount": 0,
          "evidenceSourceCode": "COMMAND"
        },
        {
          "stepCode": "CONFRONTATION-S02",
          "status": "COMPLETED",
          "targetProgressPercent": 100,
          "attemptCount": 1,
          "failureCount": 0,
          "evidenceSourceCode": "COMMAND"
        }
      ]
    },
    {
      "id": "46",
      "assignmentId": "51",
      "status": "REVIEWED",
      "version": 6,
      "progressPercent": 100,
      "currentStepCode": "CONFRONTATION-S02",
      "score": 100,
      "passed": true,
      "channel": "CONFRONTATION",
      "team": "BLUE",
      "stepFacts": [
        {
          "stepCode": "CONFRONTATION-S01",
          "status": "COMPLETED",
          "targetProgressPercent": 50,
          "attemptCount": 1,
          "failureCount": 0,
          "evidenceSourceCode": "COMMAND"
        },
        {
          "stepCode": "CONFRONTATION-S02",
          "status": "COMPLETED",
          "targetProgressPercent": 100,
          "attemptCount": 1,
          "failureCount": 0,
          "evidenceSourceCode": "COMMAND"
        }
      ]
    }
  ],
  "actors": [
    {
      "userId": "95",
      "team": "RED",
      "position": "COMMANDER",
      "departmentId": "5"
    },
    {
      "userId": "96",
      "team": "RED",
      "position": "OPERATOR",
      "departmentId": "5"
    },
    {
      "userId": "97",
      "team": "BLUE",
      "position": "COMMANDER",
      "departmentId": "5"
    },
    {
      "userId": "98",
      "team": "BLUE",
      "position": "OPERATOR",
      "departmentId": "5"
    }
  ],
  "requests": [
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/assignments",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/assignments/50/publish",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/assignments/50/accept",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/start",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/start",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44/steps",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/44/progress",
      "status": 403
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/submit",
      "status": 409
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 409
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 403
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 403
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44/steps",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 409
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/start",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44/steps",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 409
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 409
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/physical-events",
      "status": 409
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44/steps",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/submit",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/submit",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/review",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/44/review",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/44/steps",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/assignments",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/assignments/51/publish",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/assignments/51/accept",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/assignments/51/accept",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/assignments/51/accept",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/assignments/51/accept",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs?assignmentId=51&size=100",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/46",
      "status": 403
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/45/start",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/45/start",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/45/steps",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/45/execution",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/45/progress",
      "status": 403
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/45/progress",
      "status": 400
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/45/submit",
      "status": 409
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/45",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/45/progress",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/45/progress",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/45/progress",
      "status": 409
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/45/progress",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/45/progress",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/45/progress",
      "status": 409
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/45/submit",
      "status": 403
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/45/submit",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/45/submit",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/45/review",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/45/review",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/45/steps",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/46",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/46/start",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/46/start",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/46/steps",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/46/execution",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/46/progress",
      "status": 403
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/46/progress",
      "status": 400
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/46/submit",
      "status": 409
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/46",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/46/progress",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/46/progress",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/46/progress",
      "status": 409
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/46/progress",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/46/progress",
      "status": 200
    },
    {
      "method": "PUT",
      "path": "/api/tran/v1/teaching/runs/46/progress",
      "status": 409
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/46/submit",
      "status": 403
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/46/submit",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/46/submit",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/46/review",
      "status": 200
    },
    {
      "method": "POST",
      "path": "/api/tran/v1/teaching/runs/46/review",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/runs/46/steps",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/assignments/50",
      "status": 200
    },
    {
      "method": "GET",
      "path": "/api/tran/v1/teaching/assignments/51",
      "status": 200
    }
  ],
  "limitations": [
    "实装事件均为本次自动化提交的 MANUAL 人工确认或明确的权限拒绝测试,不代表真实设备、PLC、摄像机或视觉模型联调。",
    "教学运行结果来自正式 API;默认实装离线回放和默认对抗本地工作台的显示另行记录,不混同为服务端运行闭环。",
    "对抗按四名隔离学员的独立会话依次操作,检查服务端截止时间已生效;未等待整局自然超时,也不代表多人同时操作的压力或网络同步测试。",
    "测试账号使用本次随机密码,密码与会话仅在运行内存中;报告只保留用户 ID、队伍和岗位以便追溯。"
  ],
  "finishedAt": "2026-09-05T15:58:56.293Z",
  "retainedData": {
    "assignments": [
      "50",
      "51"
    ],
    "runs": [
      "44",
      "45",
      "46"
    ],
    "users": [
      "95",
      "96",
      "97",
      "98"
    ],
    "note": "仅新建本次开发测试数据,未修改或删除既有账号、任务与运行。"
  }
}

测试命令与构建

  • pnpm run build:通过。vue-tsc、Vite构建和legacy iframe分发校验均通过;仅已有包体积提示。
  • node --test tools/training-*.test.mjs(本次8个聚焦文件):通过。94/94通过,涵盖封面、场景、真实模型、目标、元数据、模式与步骤兼容。
  • pnpm run test:training-legacy:通过。两项既有训练/地形合同检查通过。
  • content-training-studio.spec.ts / Edge:通过。7/7通过(mock API的工作台回归),无跳过、无不稳定用例,独立报告studio-regression/html/index.html。
  • UTE2E_TRAINING_MODES=1 UTE2E_BASE_URL=http://192.168.31.168:6180 node tools/training-scene-visibility.e2e.mjs:通过。独立Edge会话2/2通过,真实LAN服务、禁止业务写请求;模型可见像素差、固定场景依赖、JPEG尺寸与SHA-256全部通过。
  • node tools/training-modes-api-live.mjs(163:215;164:216):通过。18/18检查组通过,90次正式教学API请求;含训练/场景/模型固定快照只读复核,详见api-evidence.json。

真实浏览器截图

实装:创建、绑定牛奶卡车、3步配置、保存重开与发布

实装:创建、绑定牛奶卡车、3步配置、保存重开与发布

实装:创建、绑定牛奶卡车、3步配置、保存重开与发布

实装:创建、绑定牛奶卡车、3步配置、保存重开与发布

对抗:每队人数为小数时阻止发布

对抗:显式清空岗位后保存重开,不恢复默认授权

对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布

对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布

对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布

对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布

对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布

实装:说明兼容修复回写,重新截图封面及发布

实装默认页面:离线播放与候选复核可操作,无法执行新卡车正式步骤

实装默认页面:离线播放与候选复核可操作,无法执行新卡车正式步骤

实装默认页面:离线播放与候选复核可操作,无法执行新卡车正式步骤

对抗默认页面:新建下拉仅有本地液压预设,无法选择已发布狐狸训练

对抗默认页面:新建下拉仅有本地液压预设,无法选择已发布狐狸训练

训练列表两种模式展示各自真实截图封面

训练列表两种模式展示各自真实截图封面

牛奶卡车实装检查:发布重开、模型实际可见、截图封面

牛奶卡车实装检查:发布重开、模型实际可见、截图封面

实测训练-牛奶卡车实装检查-20260905

狐狸红蓝协同巡检:发布重开、模型实际可见、截图封面

狐狸红蓝协同巡检:发布重开、模型实际可见、截图封面

实测训练-狐狸红蓝协同巡检-20260905

范围、限制与待确认项

  • 本报告区分编排 UI、默认教学 UI 和正式教学 API 三类证据;API 成功不能证明默认 UI 已连接该服务端运行。
  • 实装的设备或视觉事件链路未接真实设备验证;人工确认测试不能替代实机联调、安全联锁或实际操作技能考核。
  • 对抗的默认本地工作台与正式服务端任务分别留证;无证据时不声称多人跨浏览器实时协作或正式比赛胜负已联调。
  • 正式 API 对抗由四个隔离学员会话依次操作;检查服务端截止时间已生效,未等待整局自然超时,不作为多人并发或网络同步压力测试。
  • 100 分评定仅验证接口和结果留痕,不代表学员实际维修能力。测试不会向他人发送消息或下发真实设备指令。

实测边界与限制

  • 实装为MANUAL人工事件合同测试,没有接真实PLC、摄像头、视觉识别服务或安全联锁设备。
  • 两步狐狸巡检用于通用步骤与红蓝岗位合同,不是需要CONF-INSPECT等五个固定编码的完整排故作业;未宣称排故工作台阶段回写已打通。
  • 对抗验证服务端设置了7分钟截止时刻,未等待自然超时触发;未把API账号请求视为多人跨浏览器实时协同UI测试。
  • 牛奶卡车沿用原发布场景的视角,车间立柱遮挡部分车身;真实模型已渲染且封面来自该视口。
  • 实装当前发布217仅回填兼容说明并更新封面;已完成教学任务50仍固定不可变版本215。
  • 历史仅positionActions设置动作岗位的定义在教学读取侧与当前发布预检存在口径差异;本次按现有发布规则配置非空allowedPositions,未改后端岗位合同。
  • 开发环境列表切换曾出现一次15秒超时,刷新后恢复;独立复测两种模式共6次查询均HTTP 200、耗时247–403ms,未复现稳定慢查询。详见list-api-latency.json。
  • 实装事件均为本次自动化提交的 MANUAL 人工确认或明确的权限拒绝测试,不代表真实设备、PLC、摄像机或视觉模型联调。
  • 教学运行结果来自正式 API;默认实装离线回放和默认对抗本地工作台的显示另行记录,不混同为服务端运行闭环。
  • 对抗按四名隔离学员的独立会话依次操作,检查服务端截止时间已生效;未等待整局自然超时,也不代表多人同时操作的压力或网络同步测试。
  • 测试账号使用本次随机密码,密码与会话仅在运行内存中;报告只保留用户 ID、队伍和岗位以便追溯。

需要另行确认的功能范围

  • 将实装工作台从固定离线识别回放扩展为读取正式任务、已发布场景和步骤,并接入人工/设备事件、提交与结果。
  • 将对抗默认入口从本地预设任务接入正式任务清单、创建和执行链路;确认通用巡检步骤与五阶段排故工作台的适用范围。

报告生成:2026/9/6 00:01:37(Asia/Shanghai)。