主题
BIM引擎底座 · 详细设计
文档版本:0.40 | 最近核对日期:2026-11-10
1. 适用范围
本文为首期实现契约,依据需求分析和概要设计。2026-11-08 已按下述页面、接口与分层实现(后端模块 digital-construction-bim,前端 /bim/model 与 /bim/viewer)。外部接口第 5 节已按真实部署实测修正,兼容性结论见测试与验收。
实施差异(与本文早期拟定的偏差,均已在实现中落地):
| 项 | 实现结果 |
|---|---|
| 分页返回 | 沿用平台裸 PageResult{rows,total}(不套 R),与其他业务模块一致 |
| 任务执行 | 复用公共 scheduledExecutorService 补捞 + 上传事务提交后直触发;BimTaskGuard(Redisson 分布式互斥)+ 引擎可达性探测保护多实例并发 |
| 引擎版本 | versionHistory 取最近完成版本,作为根节点/树/属性的 version 入参 |
| 全量构件 | getDataSetFileData 传 dataSetId 可返回全量构件和扁平属性(无分组、顺序;仅转换成功的数据集可用,2026-11-10 厂商确认);为保留分组、顺序和系统属性,仍走树遍历 + 叶子 getTreeChildren,属性逐构件 getElementParam;批量接口用于构件清单和原始值核对 |
| 多模型加载 | 前端采用有序串行加载队列与 30s 熔断超时,首模型清空画布后续追加,单模型失败错误隔离不影响其他已加载模型 |
| 预览生命周期 | 卸载时移除 RESystemReady/RESystemEngineCreated/REDataSetLoadFinish/RESystemSelElement 监听,调用 releaseEngine(true) 并清空 window.BlackHole3D;预览路由设置 noCache,重新进入时用新宿主对象重建引擎;sdkReady 后补启动加载队列,避免引擎创建事件早到导致模型停在 idle |
| 构件定位 | 修正为 Module.Camera.setCamLocateToElem([{ dataSetId, elemIdList: [runtimeElementId] }], 1.0) |
| 列表轻量筛选 | 列表工具栏增加涵盖专业(majorCode)轻量下拉筛选,联动后端 BimModelQueryBo 与工程视图过滤 |
| 转换状态 | 只用 batchGetConvertStatus;单数版不可用 |
| 上传路线 | 改为厂商私有化部署后台上传路线:placeholder/createInFolder 申请占位 + 流式 SFTP 安全传输至引擎宿主机(路径防穿越校验) + placeholder/complete 回调通知纳管,彻底解决旧 HTTP 分片上传易被引擎暂停的缺陷 |
| 属性筛选 | valueKind=NULL 匹配空值;给 rawValue 按原值精确比较(含空串);两者都不给=仅按名称;CLOB 经 DBMS_LOB.SUBSTR 比较 |
1.1 已实现接口清单
除本文第 3 节列表外,实施中新增/细化的端点:
| 方法与路径 | 说明 |
|---|---|
POST /bim/models/{modelId}/versions | multipart 上传新版本(bim:model:upload) |
GET /bim/versions/{versionId}/source | 原文件受控下载(bim:model:download;未发布版本另需上传或发布权限) |
POST /bim/versions/{versionId}/process | 重新处理未发布版本(bim:model:submit) |
POST /bim/versions/{versionId}/publish | 检查确认后发布(bim:model:publish) |
POST /bim/viewer/context | 预览上下文(CURRENT_SCOPE / EXACT_VERSION 互斥) |
GET /bim/versions/{versionId}/tree、/tree/elements | 目录树与节点构件(节点聚合其全部叶子后代) |
POST /bim/versions/{versionId}/elements/query | 构件分页与属性 AND 过滤 |
GET /bim/elements/{elementId}/properties | 完整分组属性 |
POST /bim/versions/{versionId}/elements/resolve | 运行时 ID → 唯一平台构件 |
运行配置:bim.engine.*(地址、账号、项目、文件夹节点、分片)、bim.viewer.resource-base、bim.upload.*、bim.scheduler.*;账号与密码在 profile 中提供可被环境变量覆盖的默认值。
2. 页面与交互
2.1 工程导航与模型台账
开发库菜单“BIM引擎底座 → 模型管理”,路由 /bim/model,组件 views/bim/model/index.vue。采用左侧工程台账导航、右侧模型台账的强业务布局,不设置独立查询卡。导航参考已核实的 views/business/engineering/unit-project/index.vue,复用其侧栏交互和平台样式;原单位工程台账页面的查询卡不带入 BIM。
| 区域 | 内容与行为 |
|---|---|
| 左侧工程台账导航 | 在建/规划/运营(默认在建)、空间/合同视图(默认空间)、树搜索、展开/折叠全部、刷新、侧栏收起;当前状态全部工程快捷入口 |
| 导航层级 | 空间:线路→工点→标段→单位工程;合同:线路→标段→工点→单位工程。单位工程为叶节点,不在导航树塞入模型版本和构件 |
| 右侧顶部 | 当前节点路径与范围标题;模型名称搜索、汇总范围内可用专业轻量筛选放同一工具栏。选中单位工程时专业只读,不重复筛选归属 |
| 列表 | 模型名称、所属单位工程、工点/标段/专业、当前发布版本、最新版本处理状态、最近更新时间;单工程视图将重复归属放标题,状态与操作固定在右侧 |
| 新建 | 先在树中选中单位工程,表单只读显示工程路径及专业,填写模型名称;不再手选线路/工点/标段/专业 |
| 版本区 | 原文件名、格式/大小、业务版本号、当前处理阶段与进度、解析计数、发布人/时间、失败原因 |
| 操作 | 台账行提供「预览」(有当前发布版看发布版,否则检查已完整解析的最新版)与「版本管理」;勾选多个模型后工具栏「集中预览(已选 N)」仅加载所选模型,且要求同一工点、均有当前发布版;版本行提供「预览」「发布」「重新处理」「原文件」,按权限与状态显示 |
新建模型及首次上传在同一操作流程展示,但后端将资产建立与文件上传分开处理。文件上传失败时保留已建资产,可重新选择文件;不声称已有可用版本。首期不增加删除、撤回发布、恢复历史为当前等动作。
导航上层节点显示当前授权范围内的单位工程数,单位工程叶节点显示模型资产数(不是版本数)。零模型的有效单位工程仍展示;无单位工程的空分支可以显示空态,但不能作为上传归属。树搜索涵盖线路/工点/标段/单位工程名称及编码,保留命中路径;清空搜索恢复树。默认线路收起,选中记录的祖先自动展开,长名称可悬停查看。
点节点即时刷新模型列表,清除表格勾选并将页码归1;进入版本/预览后返回时保留当前视图、建设状态、选中节点、树展开和页码。切换空间/合同视图时优先保留相同 unitProjectId;上层节点按同一工程范围重定位,不能把“标段内工点”扩大为“全工点”。切换建设状态或权限变化导致原节点不可见时清空旧结果,回到新状态全部工程并提示。请求失败显示重试,空树/空模型有各自空态,迟到响应不得覆盖新节点。
上层汇总视图的新建按钮禁用并说明“请先选择单位工程”;选中空模型工程后正常启用。工点、标段内工点及单位工程允许“集中预览”,线路或跨工点标段只汇总台账,需下钻后预览。不得默认把整个线路所有模型送入一个场景。
沿用平台侧栏和工作区高度规则,导航宽度参考既有单位工程侧栏约360px,可折叠;桌面保留可读树名称与表格,窄屏先收起侧栏,使用同一棵树展开选择。列表默认每页10条,表格正文独立滚动,工具栏和分页可达。浅深色使用平台令牌,不增加大幅统计卡或另一套菜单。
上传成功后自动发起处理。页面离开不取消后台处理;返回时重新查询真实状态。原文件上传进度仅代表浏览器到平台,后续引擎上传、排队、转换和解析分别显示。失败信息描述阶段及原因,不展示远程堆栈或内部地址。
2.2 模型预览
拟定路由 /bim/viewer,接收 mode 与对应定位参数:CURRENT_SCOPE 为 siteId 及可选 bidId/unitProjectId,EXACT_VERSION 为 versionId;这些参数只是业务定位,后端仍鉴权。集中查看从工点或单位工程进入,待发布检查从具体版本进入;互斥规则见3.2。此内部入口须登记固定路由,不能仅创建组件而点击后404。
页面提供返回入口、工点/单位工程/版本标题、按单位工程组织的模型列表与显隐、构件树、画布和属性面板。视角操作限首期需求:旋转、缩放、平移、复位和构件定位。属性显示分组、名称、值与单位,空字符串和空值可区分,重复名称保留为独立项。
工点集中查看仅列当前发布版本;加载各模型时保留原坐标。待发布检查允许先观察已可读取的几何,但解析未完成时属性区显示“解析尚未完成”,不以部分入库内容填充完整属性列表。
发布入口显示本版本文件、构件和属性计数,并要求发布人明确确认已检查几何与解析结果。该确认与发布同一次提交,不建立审批单或独立审批状态。
2.3 构件数据查询
在版本信息中进入构件列表,采用分页。构件名称与属性条件放列表工具栏或紧凑条件编辑区,不增加独立查询卡。可按构件名称、属性分组、名称及原始值筛选;多个属性条件采用AND,不同条件使用独立存在性判断,避免属性表联接导致构件重复计数。同一条件的分组、名称和值须命中同一属性行。
选择构件可查看其全部分组属性并在预览中定位。无几何的组合或信息构件仍保留数据,定位时说明“该构件无可定位几何”;不得为满足画面计数删除此类构件。首期不新增统计报表及跨模块数据绑定界面。
3. 平台接口契约
所有接口位于 /bim;JSON 返回采用平台 R<T>,分页为 R<PageResult<T>>,即 {code,msg,data:{rows,total}},不返回另造的 records/list 分页结构。文件与资源接口直接返回二进制流及对应 HTTP 状态,不套 R。下表参数名为平台契约,不直接暴露厂商对象。平台 Long ID 使用 BigNumberSerializer,前端按 string | number 保留原值。
分页沿用 PageQuery,参数在查询串或查询 BO 顶层传递 pageNum,pageSize。已核实公共 PageQuery 缺省 pageSize 为 Integer.MAX_VALUE,因此 BIM 分页接口显式设置模型/版本列表缺省10、构件列表缺省20,最大均200,超出范围返回参数错误,不直接沿用“未传即查全部”。不修改其他模块的公共默认值。模型列表默认按创建时间、ID倒序,版本按版本号倒序,构件按平台ID升序;对外不接受任意数据库字段排序。
| 方法与路径 | 输入/输出要点 | 功能权限 |
|---|---|---|
GET /bim/navigation/tree | viewType=space或bid, bizStatus=0/1/2 → 授权工程树及单位工程叶节点,默认space/1 | bim:model:query |
GET /bim/models | bizStatus,lineId?,bidId?,siteId?,unitProjectId?,majorCode?,name?,pageNum,pageSize → 当前树范围资产分页 | bim:model:query |
POST /bim/models | unitProjectId, name → modelId;其他归属由后端解析 | bim:model:add |
PUT /bim/models/{modelId} | name;不接受归属变更 → 操作结果 | bim:model:edit |
GET /bim/models/{modelId} | 资产与当前发布版本摘要 | bim:model:query |
GET /bim/models/{modelId}/versions | 版本及处理/发布摘要分页 | bim:model:query |
POST /bim/models/{modelId}/versions | multipart file → versionId, taskId;现有接收分支包含RVT/PAK,本轮交付验收仅RVT,PAK预留未验证 | bim:model:upload |
GET /bim/versions/{versionId} | 版本、当前任务及完整性计数 | bim:model:query |
POST /bim/versions/{versionId}/process | 无引擎地址入参;重新处理未发布版本 → taskId | bim:model:submit |
POST /bim/versions/{versionId}/publish | inspectionConfirmed: true → 当前发布版本摘要 | bim:model:publish |
GET /bim/versions/{versionId}/source | 鉴权后流式下载原文件 | bim:model:download |
POST /bim/viewer/context | mode 及对应定位参数,见下节 → 经授权的加载配置与模型映射 | bim:model:preview |
GET /bim/versions/{versionId}/tree | 可选 fileId, nodeRef → 根节点或子节点 | bim:model:preview |
GET /bim/versions/{versionId}/tree/elements | fileId, nodeRef, pageQuery → 树下构件引用分页 | bim:model:preview |
POST /bim/versions/{versionId}/elements/query | 名称、属性过滤条件、分页 → 构件分页 | bim:model:query |
GET /bim/elements/{elementId}/properties | 平台构件 ID → 完整属性分组 | bim:model:query |
POST /bim/versions/{versionId}/elements/resolve | runtimeElementId、可选 fileId → 唯一平台构件 ID | bim:model:preview |
GET /bim/versions/{versionId}/resources/{path}(条件实现) | 实测需要同源转发时提供,受限相对资源路径 → 资源流;不用代理时不建空接口 | bim:model:preview |
资源请求的确切路由展开方式由 SDK 实际子资源请求验证后固化;不提供任意 URL 代理接口。前端预览页面同时具备 query 与 preview,管理端版本检查另按需求核对 upload 或 publish。
3.1 主要响应对象
| 对象 | 字段与说明 |
|---|---|
BimNavigationNodeVo | id,label,level,lineId?,bidId?,siteId?,unitProjectId?,code?,majorCode?,majorName?,colorHex?,bizStatus?,unitCount,modelCount?,children;level为line/site/bid/unit;归属沿路径携带,叶节点含完整工程组合 |
BimModelVo | modelId,unitProjectId,unitProjectName,lineId,lineName,bidId,bidName,siteId,siteName,majorCode,majorName,name,currentVersionId,currentVersion,latestVersion;currentVersion为发布版本摘要(versionId/versionNo/publishedBy/publishedAt),latestVersion为BimVersionVo或空;归属名称由单位工程关联,无发布版本时currentVersionId/currentVersion为空 |
BimVersionVo | versionId, modelId, versionNo, sourceName, format, sizeBytes, currentTask, acceptedTaskId, publishedBy, publishedAt, publicationLabel |
BimTaskVo | taskId, status, phase, engineStatus, progress, expectedFiles, completedFiles, expectedElements, completedElements, propertyCount, errorCode, errorMessage, startedAt, finishedAt |
BimElementVo | elementId, versionId, fileId, fileName, runtimeElementId, stableId, name;stableId 可空,不承诺跨版本稳定 |
BimPropertyVo | propertyId, groupOrder, groupName, propertyOrder, name, valueKind, rawValue, unit;valueKind 区分 NULL 与 TEXT |
BimTreeNodeVo | nodeRef,fileId,name,hasChildren;nodeRef为平台适配后的节点定位,fileId为平台快照文件ID,不能用节点名称充当唯一身份 |
BimViewerContextVo | mode,siteId,unitProjectId?,models;models逐版含平台modelId/versionId/unitProjectId/name/versionNo、运行时数据集映射及适配后的加载配置(资源根、指定引擎版本、场景内容、坐标/变换与最小必要客户端材料)。加载配置中厂商字段按P0实测固化,不含服务端凭据 |
任务百分比可空,不能用缺失进度计算假的整体百分比。预期构件数在枚举完成前也可空。发布人、时间不由前端传入。
publicationLabel固定为UNPUBLISHED/CURRENT/HISTORICAL,由发布时间与资产当前版本指针计算;不存新状态列。currentTask为BimTaskVo或空,已发布版计数仍沿自身acceptedTaskId读取。属性接口返回有序BimPropertyVo数组,由groupOrder分组、propertyOrder排序,同名组和同名属性不得覆盖。构件树接口返回BimTreeNodeVo数组;不传fileId/nodeRef读文件根,两者同时提供读子节点,不允许只给一个。tree/elements返回BimElementVo分页,沿acceptedTaskId映射,不将厂商整数ID作为平台ID返回。
新增/改名的name去除首尾空白后必填,长度按MODEL_NAME设计及目标库字符容量校验;资产创建必填unitProjectId。归属字段不作为可编辑BO成员,前端不得用隐藏字段重复提交线路/标段/工点/专业。格式、大小、版本状态由各自服务边界校验。当前快照未就绪时构件/属性/树接口明确返回未就绪错误,不能用200空数组掩盖“尚未解析”;真实空结果则正常返回空集合。
3.2 查询与预览输入规则
viewer/context 的两种模式互斥:
| mode | 必填 | 可选 | 行为 |
|---|---|---|---|
| CURRENT_SCOPE | siteId | bidId、unitProjectId、versionIds | 按工点及可选标段/单位工程取有权访问资产的当前发布版本;组合必须真实一致。不传versionIds取该范围全部当前版,显式空数组拒绝,浏览器取消全部时直接卸载;显式集合逐项鉴权并核对仍为当前版 |
| EXACT_VERSION | versionId | 无 | 检查未发布版本或查看历史发布版本;不能同时传siteId/bidId/unitProjectId/versionIds,不回退到当前版 |
当前授权范围尚无发布模型时 CURRENT_SCOPE 返回空模型数组,页面显示空态;选定版本已不再是当前版本时返回明确的上下文变化提示,由页面刷新列表,不暗中替换为新版。非法/越权对象按统一异常处理。请求指定unitProjectId/bidId时不能通过扩大为全工点掩盖对象无权限;单纯工点汇总对授权单位工程取交集。
导航树一次返回当前建设状态下的授权工程及叶节点,首期不增加懒加载协议。节点id是包含视图与完整路径的稳定字符串键,不能仅用bidId/siteId造成重复key,也不能由前端拆key反推业务ID。所有查询参数取节点显式字段,多个条件按AND缩小范围;bizStatus缺省1且按线路建设状态过滤。专业筛选项从当前范围单位工程的标准专业去重得到,不新增独立专业候选接口。列表先在数据库按有效单位工程、授权及树范围过滤,再分页;不能只用工点ID过滤带标段上下文的节点。
构件查询 BO 包含 name、propertyFilters 和分页字段。name 为构件名文本包含匹配,转义数据库通配符;propertyFilters 缺省空数组,每项 groupName?、name、valueKind?、rawValue?:组名、属性名和值采用原值精确匹配,省略组名表示任意组;不传值条件表示只按属性名查找。valueKind=NULL 不允许同时传 rawValue;TEXT+空字符串与 NULL 分开匹配。最多10个条件并取 AND,同一条件必须命中同一属性记录。CLOB 比较通过已验证的 Mapper 适配实现,不把整版属性拉入 Java 后再分页。
resolve 未传 fileId 时在本版本 acceptedTaskId 对应文件集合内查找运行时 ID;恰好一条才返回。传 fileId 时核对其属于该快照。无匹配返回明确未找到,超过一条返回身份歧义,不能取第一条。运行时整数若需传回 SDK,在引擎适配边界按已验证范围转换;此规则不适用于平台 Long ID。
3.3 数据授权
统一入口按“请求对象 → 版本 → 资产 → 单位工程 → 实际线路/标段/工点组合”解析授权,不能只信任传入siteId或unitProjectId。文件和构件ID同样向上回溯。树nodeRef必须是本版本文件合法节点引用;不得直接传任意dataSetId给厂商接口。
未发布版本的上下文、树、构件/属性、原文件和资源接口统一额外校验上传或发布权限;版本状态摘要不等于内容访问授权。源文件保存在受控存储中,下载通过鉴权入口,不能将 SYS_OSS 中可直接公开读取的地址作为绕过授权的下载链接。
后端 @SaCheckPermission、菜单 SYS_MENU.PERMS、前端 v-hasPermi 逐字一致。现存禁用菜单中的 bim:model:view 及旧 BIM 进度任务权限不属于本设计;实施时定向核对并处理,不直接启用旧菜单恢复旧功能。
导航由BIM入口调用工程契约取得,不能要求BIM操作人员额外拥有 business:unitProject:list 或工程配置管理权限。契约与DTO放在digital-construction-api,由business实现并复用EngineeringDataAccessHelper的 assertPairVisible / 专属组合查询规则。工程契约提供有效单位工程及归属、授权范围和双视图树,BIM补充模型数量;不能直接依赖business实现包。无授权返回空集合,授权异常继续抛出,超级管理员也核对主数据有效性。旧设计的 listVisibleSharedSiteIds 不能用来放行单位工程模型。
现有单位工程导航只有line/site/bid三种节点。实施时将实际复用的导航侧栏提取为支持节点数据与选择事件的共享组件,BIM供应含unit叶节点的数据,原单位工程台账保留原层级、查询卡和计数含义;不要为了BIM重写原台账流程或给既有接口强塞新层级。需回归原台账双视图及筛选。
3.4 最小操作状态矩阵
以下全部先满足单位工程专属范围;“维护权限”指upload或publish,不能替代操作本身的query/preview/submit/download/publish权限。仅授予submit可执行已授权版本的重新处理,但不因此开放未发布内容;按岗位需要同时授予维护权限。
| 状态 | 普通查询人员 | 具有维护权限的人员 | 发布操作 |
|---|---|---|---|
| 未发布、处理中 | 状态摘要 | 摘要;资源就绪可查几何,解析未完成不查构件/属性 | 禁止 |
| 未发布、失败 | 状态摘要 | 查看失败原因;具有 submit 权限且远端状态已核实时重新处理 | 禁止 |
| 未发布、当前任务成功 | 状态摘要 | 预览、构件属性、按下载权限取原文件 | 具有 publish 权限、检查确认且完整性条件满足时允许 |
| 当前/历史发布 | 按自身 query/preview/download 权限读取 | 同左;上传新版本需 upload | 不重复发布、不重新处理已发布版本 |
4. 服务端分层与事务
拟设 BimModelController/Service、BimVersionController/Service、BimProcessService、BimParseService、BimViewerService 和 BlackHoleClient。Controller 处理协议及功能权限,Service 统一处理归属、状态、数据范围和事务;厂商字段与状态差异集中在适配器,不散落到页面。
| 操作 | 本地事务与外部调用边界 |
|---|---|
| 建立资产 | 核对单位工程有效、归属完整和专属权限后插入资产;不保存独立工点/专业归属;无远程调用 |
| 上传版本 | 流式保存原文件并验证上传结果,再短事务建立版本与任务;异常按文件存储既有机制处理本次孤立上传,不清理共享附件 |
| 任务调度 | 提交本地记录后由现有任务设施消费;每个阶段保存已获得的外部 ID 与实际结果 |
| 转换 | 远程排队及执行不占数据库长事务;轮询只更新对应版本当前任务 |
| 解析 | 每个批次短事务写入任务所属文件/构件/属性;任何失败不得切换可见快照 |
| 完成处理 | 核对集合与计数,在短事务中置任务成功并写版本 acceptedTaskId;无中间结果暴露 |
| 发布 | 再核对 acceptedTaskId、处理结果及检查确认;同事务写发布人/时间、切换资产 currentVersionId;不再次触发远程转换 |
重新处理不得针对已发布版本。未发布版本重新处理时清空 acceptedTaskId;仅在需要重新转换或资源描述已失效时清空相应资源描述。解析失败而引擎转换成果仍有效时,保留指定引擎版本和资源,直接重新解析,不能每次都重新上传转换。发布要求 currentTaskId 等于 acceptedTaskId,且该任务成功;已经发布的历史版本不通过发布接口再次切换为当前版本。
已有任务正在运行时返回当前任务及明确提示;这是业务状态限制,不额外引入锁或重试框架。任务恢复先读取已有阶段记录和引擎状态,无法判断的远程执行结果保留为待核实失败原因,不直接创建新的转换。
4.1 执行入口与阶段恢复
首期采用应用内任务执行器,复用公共 scheduledExecutorService 做定时检查,不要求为了 BIM 另建消息队列或 SnailJob 服务。上传事务提交后触发待处理任务;应用启动时核对遗留任务,定时检查补捞已落库未触发的 PENDING 任务并查询远端转换进度。远端转换等待期间本轮执行返回,不能用循环 sleep 长时间占住线程。任务频率、读取超时和批大小由运行参数提供,使用前核实当前部署实例及公共执行器使用方式。
公共执行器是设施,BIM_PROCESS_TASK 是任务事实;不能把一条异步内存消息当作持久任务。GIS 的任务执行器含特定租约、修订及回收逻辑,只参考本地事务边界和文件流读取,不整体复制其业务状态与并发设施。
| 上次阶段/事实 | 下一步 | 必须保持 |
|---|---|---|
| REGISTER未开始 | 申请独立文件上传,持久化返回映射后进入UPLOAD | 不把申请接口误当成只有建数据集 |
| REGISTER响应丢失 | 按本版本确定性远端文件名、目标目录核实是否已创建 | 未核实前不重复创建,也不覆盖同名其他版本 |
| UPLOAD中断 | 用已保存上传记录和厂商查询结果核对分片接收情况 | 本地最后成功索引不足以证明超时分片未送达;续传语义未验证时不猜测重发 |
| CONVERT排队/运行 | 继续查询同一数据集 | 平台重启不代表远端转换停止,不再次提交 |
| CONVERT已失败 | 记录实际失败;修复后显式重新处理 | 只有文件仍有效且远端未运行时才重新提交转换 |
| PARSE失败/进程中断 | 原任务保留失败证据;重新处理建新任务和新解析快照 | 成功转换成果可复用,不重复转换;不在旧部分快照上追加造成重复 |
| CHECK进程中断 | 回读本任务已有数据重新核对完整性;无法证明时标失败并允许重新解析 | 不凭状态字段直接将任务置成功 |
| 已发布 | 不恢复写入链路 | 只读旧成果,文件更新走新业务版本 |
查询远端失败时保留最近已知阶段及进度、记录脱敏查询错误;恢复查询成功后清理该临时错误。持续无法核实时可将平台尝试记为 FAILED、错误码标明“远端结果未明”,但不得把它映射为引擎转换失败;再次处理仍先核对远端状态。此处是实际长任务恢复,不增加无限自动重试策略。
5. 黑洞服务适配
下表已在2026-11-08用内网真实RVT样本逐项实测(证据:test-artifacts/blackhole-p0-20261108/),实测差异以「实测结论」列固化在适配器BlackHoleClient中。PAK未实测,2026-11-09用户明确将其支持及验证延期,不再作为本期技术阻断;现有格式接收分支不等于可用性承诺。异常不能吞掉后返回空集合。
| 环节 | 官方接口/SDK | 使用要点 |
|---|---|---|
| 引擎认证 | POST /blackHole3D/basic/account/getToken | 实测请求体 {userNameOrPhone, password},响应 {token, tokenId, isSuccess, errMsg}——token 在顶层而非 data 下;后续请求头 Authorization: <token>。服务端保管认证材料;项目/文件夹归属由适配配置管理,用户不输入引擎地址 |
| 申请模型上传 | POST /blackHole3D/project/modelFile/v3/createInFolder | 返回 fileId、dataSetId、fileDataId、fileKey,须保存后再上传;显式禁止覆盖其他版本 |
| 逐片上传 | POST /blackHole3D/project/modelFile/v3/append | multipart字段 FileId、FileDataId、Key、Index、FileInfo;字段大小写不可随意改写。Index 自 0 起(实测 5 片 0..4 全部接收,上传记录 blobIndex 末片等于 blobCount-1);末片后平台异步收尾,filemanage/v3/list 的 uploadStatus 由 6 变 4、checkResult=Passed 才可提交转换,否则返回「未找到需要转换的文件」。实测:单分片且 blobSize 大于文件本身时转换停滞在 68.4%,4MB 多分片约 100 秒完成,故 blobSize 不超过文件大小且默认 4MB |
| 提交转换 | POST /blackHole3D/project/modelFile/v3/addToConvertQueue | dataSetId,useDefaultConfig;转换配置不可过滤首期要求的原始属性 |
| 查询转换 | POST /blackHole3D/project/dataSet/v3/batchGetConvertStatus(实测结论) | 0空闲、1排队、2转换、5停止中、10完成、20失败,另见未文档化值 60;单数版 getConvertStatus 在本部署对有效数据集返回「无效的数据集」,状态查询必须走批量版。提交转换前须确认上传收尾完成(见上传行) |
| 资源信息 | POST /blackHole3D/project/dataSet/v3/viewDataSetModel | dataSetIds;区分服务端 dataSetId、资源 resId、浏览器运行时 dataSetId |
| 文件根目录 | POST /blackHole3D/project/dataSet/v3/dataSetRootNodes | dataSetId,version;获取文件索引、名称、uniqueId、enableLazyLoading。实测 version 必须是已完成转换的引擎版本号(取 GET modelTree/v3/versionHistory/{dataSetId}),version=0 返回空 files;单文件 RVT 的 fileIntId=-1,与构件身份的 hostFileId=1 属两个身份空间,不得混用 |
| 目录树 | POST /blackHole3D/project/dataSet/v3/dataSetFileTreeNodes 及 /lazy | 根据 enableLazyLoading 选择读取方式;不规定所有模型强制懒加载 |
| 全量构件 | POST /blackHole3D/project/element/v3/getTreeChildren(实测结论) | getDataSetFileData 的正确参数是 dataSetId,不是 fileName;已转换数据集可返回全量构件及扁平属性,但无分组、顺序,且不含 构件Id、构件名称、上传文件名称 三个系统属性。为保留分组、顺序和系统属性,全量枚举仍走「根节点 → dataSetFileTreeNodes 全树 → 叶子节点批量 getTreeChildren(dataSets:[{dataSetId,childNodeId}],version)」,属性逐构件 getElementParam;批量接口用于构件清单和原始值核对。样本 87 叶子 → 435 构件,无重复 |
| 构件身份 | POST /blackHole3D/project/element/v3/getProjectElementNamePaging | 按已枚举构件补充名称、hostFileId、elementGuidId;实测 {resultCount, skipCount, dataSetId, hostFileName:"", elemIds:[...]} → {totalCount, items:[{elementName, elementIntId, elementGuidId, hostFileId}]},按 elemIds 批量回填 |
| 分组属性 | POST /blackHole3D/project/element/v3/getElementParam | dataSetId,elementIntId,version,isQueryExtend=false;保留分组、原值、单位和顺序。实测一份样本构件返回 6 组(构件信息/文件名称/标高/…)、抽样中空字符串值 10 条、paramValue 可为 JSON null;paramType 是单位文本,不是数值类型 |
| 稳定标识能力 | POST /blackHole3D/project/dataSet/v3/supportInvariantUniqueId | 记录能力,不以文件后缀或“有 GUID 字段”直接断言可继承;实测 RVT 样本返回 data:true |
| 几何加载前提 | addAuthorPath、addPathIndex(实测结论) | 必须先注册资源授权文件与路径索引:addAuthorPath("RealEngineInitAuthorPath", pathCtrlUrlTxt)、addPathIndex("RealEngineInitPathIndex", pathCtrlUrlRes, pathCtrlUrlIndex),同时设置 window.isMinIO/pathCtrlUrlTxt/pathCtrlUrlRes/pathCtrlUrlIndex/skyBoxUrl/minIOUrl/isMinIOEnableToken。缺少注册时引擎只会触发加载回调而没有可视几何(构件集合为空、包围盒为空哨兵),本部署实测即由此导致 |
| SDK 版本 | 部署内 js/bimEngine3D*/default/webSDK/ | SDK 必须与引擎部署版本一致:本部署为 3.2.0.3772;官方公开包 3.2.0.3968 与本部署不匹配。首期固定使用部署同版本资产(随 dist 发布),升级后重验加载/选择/属性 |
| 几何加载 | BlackHole3D.Model.loadDataSet | 引擎创建事件后调用;实测字段为 dataSetId(resId 原值)、resourcesAddress(资源根 + resId)、dataSetCRS、engineOrigin、dataSetCRSNorth、transInfo、useTransInfo;官方查看器同序先加载 res_skybox |
| 加载结果 | REDataSetLoadFinish、getDataSetReady | 逐模型记录结果;最终还需观察几何,回调成功不能独立构成验收 |
转换记录接口(POST /blackHole3D/project/statistics/v3/... 项目统计族)可读取每次转换的 status/开始结束时间/convertDuration,用于核对实际耗时;停止转换使用 POST /blackHole3D/project/convertQueue/v3/stopConvertingQueue({dataSetId}),实测用于解除单槽位队列阻塞。
官方资料入口:转换说明、预览与目录树、全量构件接口、分组属性接口、构件身份接口、稳定标识说明、SDK加载参数。
5.1 上传协议与版本隔离
官方上传流程表明,createInFolder 会返回数据集和文件上传映射,不应先猜造一个独立“创建数据集”调用。其 isOverWrite 默认覆盖,必须显式传 false,并以平台 versionId 派生远端技术文件名,原始文件名仍保留于平台版本记录。
请求包含 fileName、fileSize、blobCount、blobSize、projId 及经验证的 nodeId。先以真实两次同名原文件上传证明两平台版本得到不同数据集,再固定目录策略。fileId 是管理端源文件ID,不是解析文件的 hostFileId;fileDataId 是上传记录ID;fileKey 是上传处理标识,不能混写到构件文件索引。
任务保存远端文件ID、上传记录ID、上传处理标识、分片大小/总数及最后已确认分片索引。上传处理标识仅服务端使用,不进入任务 VO、普通日志或前端。Index 的起点、续传查询、最后一片处理结果须通过目标部署实测确认;官方样例出现 Index=5 不能证明它从0还是1开始。最后一片应核对远端文件完成后才提交转换,不能因 HTTP 200 就认为所有分片完成。服务端流式读取平台原文件,按片传输,不把完整文件装入字节数组。
申请/上传接口返回失败时不吞掉内层业务错误;远端返回未知结果时使用上述阶段恢复规则。无法恢复的上传记录保留证据,只有确认失效且没有运行中转换后才能为同一未发布版本重新登记;不能创建两份同时可用的版本资源。
5.2 全量解析算法
- 固定版本的独立数据集及成功引擎版本,确认没有再次转换该数据集;取得文件清单,记录每份文件索引、原始 uniqueId 和名称。
- 单文件数据集用
getDataSetFileData(dataSetId、hasParams=true)直接取得构件清单与扁平源属性;该接口仅对转换成功的数据集可用,全量属性不含分组、顺序。多文件数据集仍按目录树读取全量构件并确定文件归属,同一文件内按运行时 ID 去重,跨文件重复按失败处理,再与整文件清单核对集合。不设置属性名称白名单。 - 按有界批次查询身份信息并核对返回集合,记录引擎 GUID 原值。单靠名字无法可靠匹配的文件保持稳定来源身份未知。
- 按构件读取含版本的分组属性;逐条保存 groupOrder、propertyOrder、原始分组/名称/值/单位。无属性构件保存构件行并记读取成功。
- 不分组的批量属性接口只用于构件清单和完整性核对;分组、顺序和
构件Id/构件名称/上传文件名称系统属性仍以getElementParam为准,不能为减少请求而丢失分组或单位。未验证的下载文件接口也不能直接替代已确定语义的接口。 - 每批写入新 taskId 下的快照。对 NULL 与空字符串分别编码;不把字符串数值转成浮点再存,不假设厂商 paramType 就是数值类型。
- 校验文件枚举全部完成;每文件枚举集合等于入库集合;每构件属性请求成功;逐构件分组源属性条数(扣除上述三个系统属性)等于整文件扁平属性条数;累计属性条数等于入库条数;与引擎提供的总数(若有)交叉核对。仅靠“写入数等于读取数”不足以证明源文件无漏项,验收还需源模型抽核及完整覆盖证据。
- 校验成功后任务置成功、acceptedTaskId 指向该任务。失败不切换,不让页面看到半套快照。
原始元数据长度超出设计容量时明确失败并保留问题定位,禁止静默截断。实际样本发现容量不足后扩展相应设计及结构,再重新解析。读取到相同构件 GUID 的不同构件全部保留,记录匹配能力异常,不以 GUID 唯一约束拒绝合法模型。
getElementParam公开参数没有独立文件ID。若实际数据集中出现跨文件重复运行时ID,必须核实厂商如何消歧,不能在无法确定所属文件时将同一属性响应复制给多个构件。文件枚举若遇同名内部文件,也须核实按文件名读取接口的真实区分方式;这些属于P0/P3实测依赖,不能静默漏掉其中一份。
5.3 资源加载与坐标
资源描述由适配器按实际部署模式转换,不在前端硬编码 MinIO 拼接。2026-11-08 已按厂商部署查看器逐项实测确认加载契约:
- SDK 必须与引擎部署同版本(本部署 3.2.0.3772),使用版本化目录
/bh-sdk/<版本>/javascript/隔离缓存;公开包 3.2.0.3968 与本部署不匹配。 - 初始化前写入
window.isMinIO、pathCtrlUrlTxt、pathCtrlUrlRes、pathCtrlUrlIndex、skyBoxUrl、minIOUrl、isMinIOEnableToken,并调用addAuthorPath("RealEngineInitAuthorPath", pathCtrlUrlTxt)与addPathIndex("RealEngineInitPathIndex", pathCtrlUrlRes, pathCtrlUrlIndex)。缺少注册时引擎只会触发加载回调而没有可视几何(构件集合为空、包围盒为空哨兵),这才是此前“回调成功无几何”的根因,depths.txt为 1 字节不是阻断条件。 loadDataSet参数:dataSetId取resId(无横线)、resourcesAddress为“资源根 + resId”、dataSetCRS/engineOrigin/dataSetCRSNorth(coordinatesType=None时为 null)、transInfo=[scale, rotate, translation]、useTransInfo=true;引擎创建后先加载res_skybox。initEngineSys在 wasm 运行时真正就绪前调用会被静默忽略,且RESystemReady可能在CreateBlackHoleWebSDK内同步触发;以RESystemEngineCreated为成功信号做有限重试,事件回调仅作兜底。- 资源地址、授权文件地址由服务端按部署配置派生并随预览上下文下发(
sdk字段),不含凭据;前端不提交任意资源地址。 - 环境隔离:dev 与 prod 使用引擎的不同项目空间,项目 ID 分别固定在
application-dev.yml/application-prod.yml,凭据在 profile 中提供可被环境变量覆盖的默认值;禁止两个环境共用一个项目 ID。生产页面为 HTTPS,浏览器不得直接请求 HTTP 引擎资源;生产 nginx 以/blackHole3D/同源反代引擎并透传 Host,应用开启BIM_ENGINE_USE_REQUEST_ORIGIN后按请求Origin生成 SDK 与resourcesAddress,IP 与域名入口各自同源。开发服务器当前到引擎网络不通,远程 dev 的结构与接口可验证、实际转换/预览需网络放行。
不能普遍将 total.xml 改写为 .bin 当作正式修复。
transInfo 按 SDK 要求使用缩放、四元数、平移;只有声明有效坐标系时使用对应基点与北向参数。初始化时如无已加载模型不能把 clearLoaded 设为 false。切换版本时清理运行时映射,等待新版本加载后再接受选择回调。
选择事件若只给运行时构件 ID,须先在已加载版本的完整快照内解析所属文件;唯一命中才能定位平台构件。若出现跨文件歧义,使用已核实的厂商文件映射能力消歧,未取得有效文件身份时明确报错,不猜选第一条。解析完成前不开放依赖完整文件/构件映射的树与属性交互。
平台普通请求由Axios加Authorization和clientid,不代表SDK Worker、WASM资源请求也会自动携带。首期先验证实际SDK多级路径、Content-Type、必要Range/206及部署加载方式,采用已配置的厂商客户端机制或现有平台转发能力,不新增专用短期票据体系。服务端凭据不得返回前端,也不能通过关闭已有鉴权或将长期平台Token拼入URL解决加载问题。
确需同源转发时只接受本版登记的相对资源,不接受任意URL或越界路径,保留入口功能与工程鉴权。源站封闭、缓存与会话/撤权后的资源失效、端到端版本访问票据作为后续加固,不作为本期业务闭环阻断项。现有源站若仍可直接读取,应在验证结论记录实际限制,不宣称端到端隔离通过;本任务不授权修改源站安全配置。
5.4 工程引用保护与回归
模型只存unitProjectId。单位工程改名、验收状态变化不改变模型身份;现有updateUnitProject仅改名称、国标编码和验收状态,不提供归属转移。BIM不额外冻结这些合法编辑。
工程基础删除单位工程前通过API契约查询BIM引用,存在任意模型资产(包括尚无版本的资产)则拒绝并提示“单位工程已关联BIM模型”。保护覆盖UnitProjectServiceImpl.deleteByIds、BidSectionServiceImpl调整专业/范围移除单位工程、删除标段批量删除单位工程等实际入口,检查发生在破坏关系前且原事务整体失败。不只保护台账删除按钮,也不删除模型配合工程清理。契约放API模块、实现在BIM模块,双方不建立实现包循环依赖。实施时继续检索其他单位工程删除入口,并回归无BIM引用时原有分部工程等保护。
6. 关键失败路径
| 情况 | 对用户行为 | 数据处理 |
|---|---|---|
| 不支持的格式、超限文件 | 上传前提示,服务端重复核对输入合法性 | 不形成成功版本 |
| 引擎认证或网络失败 | 显示失败阶段及重新处理入口 | 保存本次已创建的资源标识,核实远程状态后恢复 |
| 转换长时间无进展 | 显示最后进度及更新时间;提示处理异常 | 不自动判为完成,不无依据重复提交转换 |
| 文件/属性读取失败 | 明确解析未完成,禁止发布 | 保留失败任务,完整快照指针不变 |
| 回调成功但无几何 | 保留错误/空画面提示,不自动勾选检查通过 | 发布检查不通过;进一步核查资源与模型 |
| 单个模型加载失败 | 标记该模型,其他模型可继续查看 | 不将整场景误报全部成功 |
| 历史资源不可读 | 说明该版本资源异常 | 不降级到最新版,不修改历史快照 |
| 平台业务接口越权或会话过期 | 统一权限/登录提示 | 不返回预览上下文、原文件或属性;外部源站资源失效属后续加固 |
7. 验证分解
需要验证工程导航与原台账回归、单位工程引用保护、平台状态与发布事务、解析全量性与失败恢复、专属工程权限、两种格式真实转换、同工点叠加与历史一致性。源站旁路专项后置。用例及当前证据统一记入测试与验收,不将mock、接口存在或SDK初始化作为业务闭环完成证据。