diff --git a/docs/superpowers/specs/2026-06-09-real-api-navigation-design.md b/docs/superpowers/specs/2026-06-09-real-api-navigation-design.md index aa063a2..0af9ee9 100644 --- a/docs/superpowers/specs/2026-06-09-real-api-navigation-design.md +++ b/docs/superpowers/specs/2026-06-09-real-api-navigation-design.md @@ -47,11 +47,12 @@ token 已由登录注入(`geomativeauthorization` 头),下列接口直接 | 切换工作空间 | POST | `/business/system/tenant/enterprise/switch/{tenantId}` | 信封 code/msg | | 项目列表 | GET | `/business/project/queryByUser?lastProjectId=` | `{hasNextPage, projectList:[{id, projectName, projectTypeName, referenceCRSCode, referenceCRSName, status, ...}]}`(游标分页,首页传空 lastProjectId) | | 项目结构 | POST | `/business/projectWorkbench/queryProjectStruct` | body `{projectId}`;data `{projectStructList:[{id, name, parentId, type, typeId, typeName, confCode}]}` | -| TM 下 DS | GET | `/business/projectWorkbench/queryDsByTmObjectId/{tmObjectId}` | `[{id, name, ddCode, typeName}]` | +| TM 下数据(页签) | POST | `/business/dsObject/data/page` | body `{projectId, structParentId:, structParentConfType:2, classifyTypeList:[3], pageNo, pageSize}`;`data.list[{id, dsName, name(类型名), ddCode}]` | +| TM 下文件(页签) | POST | `/business/dsObject/file/page` | body 同上但 `classifyTypeList:[1]`;项另含 `file{name, size, url}` | **层级确认(修正需求方假设)**:真实结构**不是** `项目→tm→ds`,而是 **`项目 → GS(工区) → TM(测线) → DS`**。 - `queryProjectStruct` 返回一个**扁平 parent-child 列表**(仅含 GS + TM 两类节点,**不含 DS**),客户端按 `parentId` 自建树。 -- DS 不在结构列表里,按 TM 单独拉取(`queryDsByTmObjectId`)。 +- DS 不在结构列表里:按 TM 拉数据/文件两类,分别用 `dsObject/data/page`(classify=3)、`dsObject/file/page`(classify=1),传 `structParentId=`、`structParentConfType=2`。(实测:`queryByUser` 返回空,项目列表改用 `my/profile/queryProject`;`queryDsByTmObjectId` 字段不全已弃用。) - **项目不能直接挂 DS**;DS 永远挂在 TM 下。但由于是 `parentId` 扁平结构,**TM 可直接挂在项目下(无中间 GS)**——这是"项目直接挂"印象的来源,但叶子仍是 TM→DS。 **节点判定**:结构列表只含 GS+TM,故 **TM = 该节点在结构列表中无子节点(叶子)**;非叶子 = GS。 @@ -116,7 +117,7 @@ struct StructNode { int type = 0; }; ``` -`DsNode{id,name,ddType}` 复用;映射时 `ddCode → ddType`。 +新增 `DsRow{id, dsName, typeName, ddCode, fileName, fileUrl, fileSize}`(数据/文件页签行通用;文件行含 file*)。`DsNode` 仅本地样本仓储继续用。 ### 5.3 数据访问层 `data` @@ -134,7 +135,9 @@ public: virtual RepoResult switchWorkspace(const std::string& tenantId) = 0; virtual RepoResult> listProjects(const std::string& lastProjectId) = 0; virtual RepoResult> loadStructure(const std::string& projectId) = 0; - virtual RepoResult> loadDatasetsOfTm(const std::string& tmObjectId) = 0; + virtual RepoResult> loadTmRows(const std::string& projectId, + const std::string& tmObjectId, + int classifyType) = 0; // 3=数据 1=文件 }; ``` @@ -147,7 +150,7 @@ public: - `parseWorkspaces(QJsonArray) -> vector`(`isCurTenant==1 → isCurrent`)。 - `parseProjects(QJsonObject) -> {vector, bool hasNextPage}`。 - `parseStructNodes(QJsonArray) -> vector`。 -- `parseDatasets(QJsonArray) -> vector`(`ddCode→ddType`)。 +- `parseDsRows(QJsonArray) -> vector`(data/file page 的 `data.list`;`name→typeName`,`file{name,size,url}`)。 - `buildStructTree(vector) -> vector`:扁平→**通用树**(不强塞 `Project/Gs/Tm` 刚性模型, 以适配任意层级 + TM 直挂项目)。`StructTreeNode{StructNode node; bool isTm; vector children}`。 - 以 `parentId` 归并;`parentId` 为空或不在集合内(孤儿)的节点为根层。 @@ -173,7 +176,8 @@ signals: void projectsLoaded(const std::vector&, QString currentId); // 发出项目名 + 扁平结构节点;建树(buildStructTree)在 ObjectTreePanel 内完成。 void structureLoaded(const QString& projectName, const std::vector&); - void datasetsLoaded(const QString& tmObjectId, const std::vector&); + void datasetsLoaded(const QString& tmObjectId, const std::vector&); // 数据页签 + void filesLoaded(const QString& tmObjectId, const std::vector&); // 文件页签 void loadFailed(const QString& stage, const QString& message); // 出错→UI 空/错状态 void busyChanged(bool busy); // 同步阻塞期间置 WaitCursor private: @@ -202,7 +206,7 @@ private: `showMessage(msg)` 显示空/错占位。信号 `tmClicked(QString tmObjectId)` / `tmCheckToggled(...)` (后者为前瞻钩子,本轮无消费者)。 -**`app/panels/DatasetListPanel`**(已有)—— `datasetsLoaded` → `populateDatasetList`;空时显示"暂无数据集"。 +**`app/panels/DatasetListPanel`** —— `datasetsLoaded`→`populateDatasetList`(数据:dsName+类型名);`filesLoaded`→`populateFileList`(文件:文件名+可读大小,url 存角色备下载);空时占位。列表去隔行变色,改细分割线。 **中央/详情**:移除"启动自动渲染本地 demo";DS 点击 → 详情面板与中央视图显示占位文案 "该数据集渲染将在下一阶段接入 dd 接口"。渲染代码保留。 @@ -225,7 +229,7 @@ private: → controller.switchProject: loadStructure(id) → emit structureLoaded;清空 DS 列表/详情占位 选 TM: ObjectTreePanel.tmClicked(tmObjectId) - → controller.selectTm: loadDatasetsOfTm → emit datasetsLoaded → DatasetListPanel 填充 + → controller.selectTm: loadTmRows(pid,tm,3)+loadTmRows(pid,tm,1) → emit datasetsLoaded + filesLoaded → 数据/文件页签 点 DS: DatasetListPanel → 中央/详情显示占位"待接入"(本轮不渲染真实数据) ``` @@ -284,7 +288,7 @@ void rebuildCentralScene(geopro::render::Scene& scene, vtkRenderer* renderer, ## 9. 测试策略 依既有无测试桩 + 依赖 live 服务器的现实,聚焦**纯逻辑单测**(GoogleTest + CTest): - `dto/NavDto` 映射:喂样本 JSON(取自 OpenAPI example / 手造)验证 - `parseWorkspaces / parseProjects / parseStructNodes / parseDatasets` 字段与 `ddCode→ddType`、`isCurTenant→isCurrent`。 + `parseWorkspaces / parseProjects / parseStructNodes / parseDsRows` 字段与 `name→typeName`、`isCurTenant→isCurrent`。 - `buildStructTree` 扁平→树:覆盖 项目根→GS→TM、TM 直挂项目(无 GS)、孤儿 parentId、空列表、防环 等场景。 - 不做 live 集成 / E2E(无桩、依赖真实后端)。控制器/UI 信号联动靠手动联调验证。 - 目标:纯逻辑文件(dto + tree builder)覆盖率优先达标;UI/网络 IO 不计入。