# 实装与对抗训练实测报告 · 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步配置、保存重开与发布](screenshots/physical-settings-reopen.png)](screenshots/physical-settings-reopen.png) [![实装:创建、绑定牛奶卡车、3步配置、保存重开与发布](screenshots/physical-authoring.png)](screenshots/physical-authoring.png) [![实装:创建、绑定牛奶卡车、3步配置、保存重开与发布](screenshots/physical-preflight.png)](screenshots/physical-preflight.png) [![实装:创建、绑定牛奶卡车、3步配置、保存重开与发布](screenshots/physical-published.png)](screenshots/physical-published.png) [![对抗:每队人数为小数时阻止发布](screenshots/confrontation-invalid-team-size.png)](screenshots/confrontation-invalid-team-size.png) [![对抗:显式清空岗位后保存重开,不恢复默认授权](screenshots/confrontation-empty-positions.png)](screenshots/confrontation-empty-positions.png) [![对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布](screenshots/confrontation-positions.png)](screenshots/confrontation-positions.png) [![对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布](screenshots/confrontation-settings-reopen.png)](screenshots/confrontation-settings-reopen.png) [![对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布](screenshots/confrontation-authoring.png)](screenshots/confrontation-authoring.png) [![对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布](screenshots/confrontation-preflight.png)](screenshots/confrontation-preflight.png) [![对抗:狐狸场景、两步巡检、岗位配置、说明往返与发布](screenshots/confrontation-published.png)](screenshots/confrontation-published.png) [![实装:说明兼容修复回写,重新截图封面及发布](screenshots/physical-final-published.png)](screenshots/physical-final-published.png) [![实装默认页面:离线播放与候选复核可操作,无法执行新卡车正式步骤](screenshots/physical-teaching-list.png)](screenshots/physical-teaching-list.png) [![实装默认页面:离线播放与候选复核可操作,无法执行新卡车正式步骤](screenshots/physical-offline-preview.png)](screenshots/physical-offline-preview.png) [![实装默认页面:离线播放与候选复核可操作,无法执行新卡车正式步骤](screenshots/physical-offline-confirmed.png)](screenshots/physical-offline-confirmed.png) [![对抗默认页面:新建下拉仅有本地液压预设,无法选择已发布狐狸训练](screenshots/confrontation-final-entry.png)](screenshots/confrontation-final-entry.png) [![对抗默认页面:新建下拉仅有本地液压预设,无法选择已发布狐狸训练](screenshots/confrontation-final-local-create.png)](screenshots/confrontation-final-local-create.png) [![训练列表两种模式展示各自真实截图封面](screenshots/physical-list-cover.png)](screenshots/physical-list-cover.png) [![训练列表两种模式展示各自真实截图封面](screenshots/confrontation-list-cover.png)](screenshots/confrontation-list-cover.png) [![牛奶卡车实装检查:发布重开、模型实际可见、截图封面](screenshots/physical-independent-reopen.png)](screenshots/physical-independent-reopen.png) [![牛奶卡车实装检查:发布重开、模型实际可见、截图封面](screenshots/physical-independent-settings.png)](screenshots/physical-independent-settings.png) [![实测训练-牛奶卡车实装检查-20260905](screenshots/physical-final-cover.jpg)](screenshots/physical-final-cover.jpg) [![狐狸红蓝协同巡检:发布重开、模型实际可见、截图封面](screenshots/confrontation-independent-reopen.png)](screenshots/confrontation-independent-reopen.png) [![狐狸红蓝协同巡检:发布重开、模型实际可见、截图封面](screenshots/confrontation-independent-settings.png)](screenshots/confrontation-independent-settings.png) [![实测训练-狐狸红蓝协同巡检-20260905](screenshots/confrontation-final-cover.jpg)](screenshots/confrontation-final-cover.jpg) ## 范围、限制与待确认项 - 本报告区分编排 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)。