213 lines
16 KiB
Markdown
213 lines
16 KiB
Markdown
# Batch 2 实施依据:对象/数据集对话框(新建/编辑/导入/导出/插件)
|
||
|
||
实地研究原系统 `http://tenant.geomative.cn`(项目「香港威立雅」`projectId=1439735554211840`,
|
||
projectTypeId `1445121423155200`)所得。API 经页面 token replay + 真实 DOM 操作 + fetch 录制捕获。
|
||
|
||
> 口径:成功码 `code==200`;列表载荷在 `data`(非对象时包成 `data.value`)。
|
||
> token 头 `geomativeauthorization`;base `http://tenant.geomative.cn/pop-api`。
|
||
|
||
---
|
||
|
||
## 更新(2026-06-15):权威来源已厘清,前述"⚠️未捕获"壁垒解除
|
||
|
||
三个来源各司其职,别再混用:
|
||
- **xlsx「客户端」页签** = 客户端菜单/交互**规格(权威)**。
|
||
- **原 web 压缩 bundle = 提交体的"地面真相"(首要权威)**:线上正在跑、能成功建对象的真实前端代码,它发什么服务端就收什么。
|
||
bundle 抓 `http://tenant.geomative.cn/static/js/` 各路由 chunk 后 grep 还原:
|
||
`GsForm-*.js`(GS表单) / `tmForm-D7d9h9Nb.js`(TM表单,数据管理页) / `ImportForm-*.js`(导入) / `projectStructure-*.js`。
|
||
- **`docs/apis` OpenAPI = 交叉校验(次级)**:自动生成、已知有缺漏/错标(如 GS 名称字段标成 `projectName`)。**与 bundle 冲突时以 bundle 为准**,并在 §B 并列标注、留一次真实请求验证。
|
||
- **原型 `prototype.geomative.cn`** = 粗略视觉 mockup,菜单画不全,**不作为菜单删减依据**。
|
||
|
||
### A. xlsx 规定的菜单(= 现有实现已对齐,勿按原型删减)
|
||
|
||
| 区域 | 菜单/动作(xlsx 原文) | 对话框来源(xlsx 标注) |
|
||
|---|---|---|
|
||
| 对象-添加+ | 新建GS(当前为TM时无效)、新建TM | GS→`项目配置\项目结构\添加`;TM→`数据管理\新增方法对象` |
|
||
| 对象-右键 | 显示/隐藏、定位、属性、异常详情、编辑、新建GS、新建TM、导入DS(1..n)、删除 | 编辑→`数据管理\编辑`;导入→`数据管理\导入` |
|
||
| 数据集-添加+ | 新建/导入一个数据集 | `数据管理\可新建数据类型\导入` |
|
||
| 数据集-右键 | 数据集详情、属性、插件1/2/3…、导出、删除 | 导出→`批量导出\文件导出` |
|
||
|
||
注:xlsx 第32行"显示项目(**也是一个GS**)"——**项目根本身就是 GS 节点**。最强证据是真实数据:`queryProjectStruct` 返回的结构里确有 TM(type=2) 直接挂在项目根(type=1) 下(如 NERT1、"123")——若根不允许建 TM,该数据无从产生。故**项目根(作为 GS)允许新建 TM**。
|
||
|
||
### B. 提交体(**bundle 为准,OpenAPI 并列校验**,替换下文旧"⚠️未捕获")
|
||
|
||
bundle 还原(= 客户端应照此发送):
|
||
```
|
||
POST /business/gsObject { gsTypeId, parentId, projectId, name, responsiblePersonName, properties }
|
||
PUT /business/gsObject { gsTypeId, id, projectId, name, responsiblePersonName, properties } // 编辑无 parentId
|
||
· gsTypeId(测试对象类型)、responsiblePersonName(负责人)、name 为顶层固定字段;动态字段在 properties
|
||
· parentId 仅 POST 用 = 右键所在节点 id(含项目根)
|
||
|
||
POST /business/tmObject { tmTypeId, name, properties, projectId, parentId, parentType:"1" }
|
||
PUT /business/tmObject { tmTypeId, id, name, properties, projectId, parentId, parentType:"1" }
|
||
· bundle 中 create/edit 用同一 spread,故 PUT 也带 parentId/parentType
|
||
· parentId = 右键所在节点 id(GS 或项目根);parentType 恒字符串 "1"
|
||
|
||
POST /business/dsObject/import (参数走 query string;file 为 multipart 上传项)
|
||
{ file*, dsTypeId*, projectId*, structParentConfType*(int), structParentId*, scriptCode?, scriptParamListJsonStr?, aliasName? }
|
||
· 选脚本先 getDynamicForm(typeId=scriptId, type=6) 取脚本参数;checkImport 校验坐标
|
||
```
|
||
getDynamicForm 的 `type`:**1=GS对象 / 2=TM对象 / 6=导入脚本参数**(已确认)。
|
||
|
||
**✅ 线上实测已确认(intercept-abort,未写库):动态字段嵌套在 `properties.<fieldCode>` 下。**
|
||
证据:编辑弹窗真实字段 DOM id —— GS `properties_topography/geotechnical/climate/hydrology`、TM `properties_supervisor/notes` 等;顶层固定字段(gsTypeId/responsiblePersonName/name、tmTypeId/name)与 properties 分开渲染。
|
||
|
||
**bundle ↔ OpenAPI 冲突(已由压缩源码分析确证,以 bundle 为准;OpenAPI 为文档生成误差):**
|
||
下表结论来自 GsForm/tmForm 压缩源码还原(= 线上前端真实请求构造)。线上 app 正是发这些 body 且能成功建/改对象,故服务端接受度亦由线上运行佐证。OpenAPI 的 `projectName`/整数等是自动生成的文档误差,不采信。
|
||
| 字段 | bundle(线上真实代码) | OpenAPI(生成文档) | 取舍 | 实测状态(含源码分析) |
|
||
|---|---|---|---|---|
|
||
| GS 名称 | 顶层 `name` | POST 标 `projectName`、PUT 无 | 用 `name`(projectName 系 DTO 误标) | ✅ 源码已证 |
|
||
| TM 名称 | 顶层 `name` | 无 `name` | 用 `name`(OpenAPI 漏列) | ✅ 源码已证 |
|
||
| `parentType` | 字符串 `"1"` | integer | 发字符串 `"1"` | ✅ 源码已证 |
|
||
| PUT tmObject 的 parentId/parentType | 带(create/edit 同一 spread) | 无 | 带(匹配线上) | ✅ 源码已证 |
|
||
| 动态字段位置 `properties.<code>` | 是 | —— | 随 bundle | ✅ 源码 + 运行时双证 |
|
||
> 注:拦截-阻断的运行时抓包在本数据集走不通(变更表单必填项多、arco 校验拦截、且不写生产库),但**源码分析已足以确证 body**,无需真实写入。
|
||
|
||
### C. 客户端待改项(对照现有实现)
|
||
|
||
1. **`ObjectFormDialog` 提交体字段错误**(最关键):
|
||
- 现:`{typeId, type, projectId, id?, properties}` + extraBody `{structParentId, structParentConfType}`
|
||
- 应(按 bundle):GS→`{gsTypeId, parentId(仅新建), name, responsiblePersonName, properties}`;
|
||
TM→`{tmTypeId, parentId, parentType:"1", name, properties}`(+ 编辑加 id)
|
||
- `structParentId/structParentConfType` 是**导入**的字段,被误用于新建对象(OpenAPI 证实这两字段只在 `dsObject/import` 出现)。
|
||
2. **项目根节点**:现为"非交互无菜单",应按 GS 处理(提供 新建GS/新建TM/属性,依 xlsx 第32行 + 真实数据"TM 挂根")。
|
||
3. **新建TM 方法类型来源**(决策):**用 `queryTmType?projectId=&gsId=`**——带 gsId 能按所选 GS 过滤方法类型,比 web 全局 tmMethodList 更准。
|
||
4. **父对象确定逻辑**(决策):客户端**不复刻** web 数据管理页"先选左树"的选择器;**父对象 = 右键所在节点**(GS 或项目根),`parentId` 直接取该节点 id。
|
||
5. **`DynamicFormEditor` 顶层固定字段**:GS 须含 gsTypeId 下拉 + responsiblePersonName + name;TM 新建须含 tmTypeId 下拉 + name。
|
||
6. `displayComponentType` 全集映射 —— **已由源码确证**,见 §E.1。
|
||
|
||
### D. 状态(原"待确认"已全部由源码分析落地)
|
||
- ✅ `displayComponentType` 完整映射 → §E.1
|
||
- ✅ 导出 body → §E.2
|
||
- ✅ 插件机制 → §E.3(结论:web 无"ds→插件"菜单,客户端此项为原创设计,需产品决策)
|
||
- §B 字段已由源码确证,**不再需要真实写入回证**。
|
||
- (`PUT /dsObject/updateDsObject` body OpenAPI 已定义 `{dsObjectId, description, attachedParameters, ...}`,见下文第六节。)
|
||
|
||
### E. 源码补全(FieldItem / 导出 / 插件,均来自压缩源码)
|
||
|
||
**E.1 `displayComponentType` → 控件(FieldItem `index-1LyDq-Qg.js`,权威全集)**
|
||
| 值 | 控件 | 备注 |
|
||
|---|---|---|
|
||
| 1 | a-input 单行文本 | |
|
||
| 2 | a-input **禁用** | 只读文本 |
|
||
| 3 | a-checkbox | label=item.name |
|
||
| 4 | a-select 下拉 | options=optionsObject |
|
||
| 5 | a-input 单行文本 | 同 1 |
|
||
| 6 | a-date-picker 日期 | |
|
||
| 7 | a-time-picker 时间 | |
|
||
| 8 | a-date-picker show-time | 日期时间 `YYYY-MM-DD HH:mm:ss` |
|
||
| 9 | a-textarea 多行文本 | |
|
||
| 10 | a-input-number 数字 | |
|
||
| 11 | a-tree-select 树选择 | data=optionsObject, fieldNames{key:value,title:label,children:childList} |
|
||
| 其他 | a-input-number mode=button | 步进数字 |
|
||
- 字段路径 `${fieldPrefix=properties}.${fieldCode}` → 确认动态值在 `properties.<fieldCode>`。
|
||
- **`requiredType` 语义纠正:1=必填可编辑;2=只读禁用(`disabled: isDisabled||requiredType===2`,required 仅 ===1);其他=可选可编辑。**
|
||
- comp 类型由后端按类型配置(如"测区"把地形地貌配成 6=日期选择器),客户端按本表渲染即可。
|
||
|
||
**E.2 导出 body(ExportModal `ExportModal-DWdo-HxP.js`,两套)**
|
||
```
|
||
模板导出 POST /business/templateExport/export { dsObjectIdList:[ds ids], templateId }
|
||
文件导出 POST /business/dataFileExport/export { idList:[ds ids], fileType [, startTime, endTime] }
|
||
// startTime/endTime 仅 fileType==5(时序数据)
|
||
文件下载 POST /business/dataFileExport/download (responseType=blob)
|
||
```
|
||
- `selectType`(checked/currentPage) 仅决定取哪些 id,不进 body。
|
||
- 批量导出页流程:选数据类型/时间范围 → 勾选文件(ds ids) → 「导出」开 ExportModal(选模板) → 提交。
|
||
- 客户端数据集右键「导出」(单 ds, 按模板) → `templateExport/export { dsObjectIdList:[该ds], templateId }`;模板来自 localStorage `template`.fileTemplateList 或 `dataFileExport/queryFileType`。
|
||
|
||
**E.3 插件机制(结论:web 无对应交互,客户端为原创设计)**
|
||
- web 中**没有**"数据集右键→插件"菜单。模型从模型管理页按类型用专用 Modal 调起(resipy=ERT反演、gprpy=GPR处理 等,见 `resipyModel-*.js`/`gprpyModel-*.js`/`modelManage-*.js`)。
|
||
- 关联方向是**模型→可用数据集**:`GET /business/model/{scriptType}/dsObjectList/{projectId}`(如 `model/resipy/dsObjectList/{projectId}`),即每个模型声明它能跑哪些 ds。
|
||
- 全局模型目录:`GET /business/model/list`;任务分页:`POST /business/model/task/page`。
|
||
- xlsx 的"插件1/2/3 = 列出与当前 ds 关联的插件"是**客户端原创**(ds→模型 的反向)。web 无单一直达接口,需:用 model/list + 各模型 dsObjectList 反查匹配,或后端新增"按 ds 列模型"接口。**此项需产品/后端决策,非源码可定。**
|
||
|
||
---
|
||
|
||
## 一、项目结构(已用于现有功能)
|
||
`GET /business/projectStruct/queryProjectStruct/{projectId}`
|
||
→ 扁平节点 `[{id,parentId,name,type(1=GS,2=TM),typeName,typeId,confCode,collectTime}]`,根 parentId="0"。
|
||
|
||
## 二、编辑 / 新建对象 —— 动态表单(核心,最大工作量)
|
||
|
||
### 表单 schema 来源(统一端点,编辑弹窗打开时调用)
|
||
`POST /business/project/getDynamicForm`
|
||
body `{"typeId": <类型id>, "id": <对象id>, "type": <1=GS|2=TM>, "projectId": <projectId>}`
|
||
→ 返回结构同 getDetail:
|
||
```
|
||
{
|
||
typeId, confCode, name(类型名), description,
|
||
formList: [ { groupName, values: [ FieldDef... ] } ], // 可多组(对应弹窗内分页签:基本信息/测线布设/数据质量检查...)
|
||
properties: { <fieldCode>: <当前值> } // 编辑预填;新建时为空
|
||
}
|
||
```
|
||
(getGsObjectDetail / tmObject/getDetail 返回同样的 formList/properties,可互为参考。
|
||
TM 的 getDetail 还含 dsList / dsClassifyTypeList / gridFieldList。)
|
||
|
||
### FieldDef 字段定义
|
||
```
|
||
confFieldId, fieldUseType(1=核心字段,2=普通),
|
||
fieldCode(键), fieldName(标签),
|
||
displayComponentType(控件类型), requiredType(1/2 必填标志——待核实方向),
|
||
displaySort, fieldDataType(4=字符串,5=日期,6=日期时间...),
|
||
fieldConfigJsonObject:{fieldChnFormat,fieldRemark,fieldEngFormat},
|
||
optionsObject(下拉项;普通字段为 null)
|
||
```
|
||
|
||
### displayComponentType 已观察样例(需补全映射)
|
||
TM「常规高密度电阻率法」编辑弹窗实测控件:
|
||
- 方法名称(只读文本)
|
||
- 基本信息组:名称/电极数/电极间距/测线长 = **只读文本**(核心字段 fieldUseType=1,编辑时禁用)
|
||
- 设备 = 下拉(必填) ; 布设日期 = 日期选择(必填) ; 天气 = 下拉(必填)
|
||
- 布设人/审核人 = 文本(必填) ; 备注 = 文本
|
||
GS「测区」formList 含:创建人/创建日期(comp6)/名称(comp1)/创建时间(comp7)/地形地貌(comp8)/岩土性质...
|
||
→ 推测 comp1=单行文本, 6=日期, 7=日期时间, 8=多行文本;**落地前需逐一在原版核实**。
|
||
|
||
### 新建 TM 的类型选择
|
||
`GET /business/tmObject/queryTmType?projectId=..&gsId=..`
|
||
→ `[{label:"瞬变电磁方法", value:<tmTypeId>, code:"TEM01"}]`
|
||
新建流程:选 GS → queryTmType 选方法类型 → getDynamicForm(typeId,type=2,无id) 取空表单 → 填 → POST。
|
||
|
||
### ~~⚠️ 未捕获(真实壁垒,不可猜)~~ → 已解除,见上「更新 §B」
|
||
~~下列 body 当时认为不可得;实为 OpenAPI requestBody + bundle 已给出,参见文首「更新 §B」。~~
|
||
- ~~`POST /business/gsObject`~~ → 见 §B
|
||
- ~~`POST /business/tmObject`~~ → 见 §B
|
||
- ~~`PUT /business/gsObject` / `PUT /business/tmObject`~~ → 见 §B
|
||
- `PUT /business/dsObject/updateDsObject`(更新DS body)仍待确认
|
||
|
||
## 三、删除(已实现 Batch1)
|
||
`DELETE /business/gsObject/{id}` `DELETE /business/tmObject/{id}` `DELETE /business/dsObject/{id}`
|
||
|
||
## 四、导入 DS
|
||
TM 的 getDetail.dsList = 该 TM 可承载的 ds 类型 `[{dsTypeId,nameChn,nameEng,canImport,canExport,...}]`(canImport=true 的可导入)。
|
||
`GET /business/dsObject/query/script?dsTypeId=..&tmTypeBaseConfId=..` → 该类型可用导入脚本。
|
||
`POST /business/dsObject/checkImport` → 校验脚本所需轨迹/坐标文件是否存在。
|
||
`POST /business/dsObject/import`(query 参数已知:aliasName, dsTypeId*, file*, projectId*, scriptCode, scriptParamListJsonStr, structParentConfType*, structParentId*)
|
||
⚠️ file 为上传项(multipart);scriptParamListJsonStr 结构未捕获。
|
||
|
||
## 五、导出
|
||
`POST /business/templateExport/queryExportObject` body `{projectId}` → 可选导出对象树 `[{id,parentId,name,check}]`。
|
||
`GET /business/templateExport/queryDataType/{tmTypeBaseConfId}` → 按方法查数据类型。
|
||
`POST /business/templateExport/export`(body 未捕获)。
|
||
模板列表见 localStorage `template`:templateTypeList[数据管理-数据报告/异常体报告], fileTypeList[WORD/EXCEL], fileTemplateList。
|
||
另:数据集详情页「导出」按钮点击未发请求/未弹窗(可能直接下载或需先配模板)——待核实。
|
||
|
||
## 六、插件(数据集右键)
|
||
`GET /business/model/list` → 全局模型目录 `[{id,scriptCode,scriptName,scriptOperationType,formItemList}]`
|
||
例:script_ert_inner_inversion「ert反演(默认)」、script_radar_resultant_data_processing「雷达数据处理(默认)」。
|
||
⚠️ "与当前 ds 关联"的过滤逻辑未捕获(model/list 不含 ds 类型关联;各模型用 dsObjectList/{projectId} 声明可接受的数据源)。
|
||
`POST /business/model/task/page` → 模型任务分页(用于"数据集任务"面板)。
|
||
|
||
## 实施建议顺序
|
||
> body 已由「更新 §B」给出,下列"先捕获"前置多数已不需要;仅 §D 三项仍待确认。
|
||
1. 动态表单引擎 `DynamicFormEditor`(Qt):按 formList 渲染 comp1/6/7/8 + 下拉(optionsObject) + 必填校验 + 只读核心字段;编辑用 properties 预填;GS/TM 顶层固定字段(§C-5:gsTypeId/responsiblePersonName/name、tmTypeId/name)。**displayComponentType 全集映射仍需核实。**
|
||
2. **修编辑/新建提交体字段**(§C-1):GS→gsTypeId+parentId(仅新建)+name+responsiblePersonName;TM→tmTypeId+parentId+parentType:"1"+name。
|
||
3. 项目根菜单按 GS 处理(§C-2);新建TM 类型用 queryTmType(§C-3);父对象=右键节点(§C-4)。
|
||
4. 导入 DS(dsList 选类型 → query/script → 文件 → checkImport → import,body 见 §B)。
|
||
5. 导出(queryExportObject 选对象 + 模板 → export)。**export body 待确认(§D)。**
|
||
6. 插件子菜单(model/list 列出;调用/关联逻辑待定,§D)。
|
||
|
||
## 捕获提交载荷的方法(已在原版页面注入录制器 window.__rec)
|
||
在 Playwright 浏览器对原系统执行一次「编辑保存/新建保存/导入/导出」(填完必填项),
|
||
即可从 `window.__rec` 读到真实 method+url+body。
|
||
注:编辑/新建/导入的 body 已由 bundle 还原(§B),此法现仅用于**验证 §B 冲突表 4 项**与**捕获导出 export body**(会改数据,谨慎)。
|