12 KiB
交接文档:数据集详情图表 + 全 App 网络层异步化
给下一个会话:读完本文件即可无缝接手。最后更新 2026-06-12。 分支
feat/dataset-detail-chart,领先 main 68 commits,工作区干净,测试 116/116 全绿。
0. 一句话现状
geopro(Qt6/C++ 离线桌面客户端,1:1 复刻赛盈地空 web)已完成两大块:
- 数据集详情图表(仅 ERT 反演
dd_inversion_data,QwtPlot 落地,用户已验收)。 - 全 App 网络层 100% 异步化(详情/导航/登录/项目列表全异步,同步
QEventLoop阻塞路径已彻底删除,无技术债)。
均未合并入 main,分支挂起等收尾。
1. 背景
- geopro = Qt6 / C++ 离线桌面客户端,目标像素级 1:1 复刻 web 系统
http://tenant.geomative.cn/#/projectSpace/datasetMange/datasetInfo(赛盈地空)。 - 硬约束:禁 QWebEngine/Chromium(离线)。图表用本地 QwtPlot(轴/交互/图例)+ VTK 算法层(等值线几何)+ 连续/离散色阶。验收 = 视觉等价 + 交互一致(非字节级 diff)。
- 标准测试 ds:
id=1458990939709440(ddCode=dd_inversion_data)。 - 站点 baseUrl:
http://tenant.geomative.cn/pop-api。Auth header:geomativeauthorization: Geomative <token>。
2. 技术栈与构建/测试
- Qt6 Widgets + Qwt 6.2(源码
external/qwt-src/,用cmake/qwt.cmake以 CMake 构建静态库,不要用 qmake——本机 VS2026 缺 vswhere)。 - VTK 9.6 仅算法层(
vtkBandedPolyDataContourFilter/vtkStripper/vtkSplineFilter/vtkDataSetSurfaceFilter),不做 VTK 渲染。 - CMake(VS 自带)+ Ninja + MSVC 14.51;GoogleTest/CTest;vcpkg(仅非 Qt 依赖)。
- 构建:
powershell.exe -ExecutionPolicy Bypass -File scripts/dev-build.ps1- ⚠️
LNK1104 无法打开 geopro_desktop.exe= exe 在运行:先Get-Process geopro_desktop -ErrorAction SilentlyContinue | Stop-Process -Force再构建。 - ⚠️ 改头文件后偶发 ninja 增量陈旧导致链接报「符号在 main.cpp.obj 重复定义」:删
build/release/src/app/CMakeFiles/geopro_desktop.dir/main.cpp.obj后重建。
- ⚠️
- 测试:
powershell.exe -ExecutionPolicy Bypass -File scripts/dev-test.ps1(只跑 ctest 不构建——必须先 dev-build)。当前 116/116 绿。- 单测过滤:
build/release/tests/geopro_tests.exe --gtest_filter=ApiBatch.* - ⚠️ 直接跑 exe 时 4 个
CrsTransform/VoxelRegister/Terrain失败是缺PROJ_DATA环境项;ctest(dev-test)会注入环境、全绿——以 dev-test 的 "X/X passed" 为准。
- 单测过滤:
- 网络/署名:AuthLiveTest 联网真打站点;提交信息全局禁用署名(勿加 Co-Authored-By)。
3. 第一块:数据集详情图表(已完成,仅 dd_inversion_data)
详情含两页签,均经用户逐项验收:
- 原数据:Plotly scattergl 风格散点(方形点/白描边/连续色阶/x 轴顶部)。
- 网格数据:填充等值面(栅格 QImage)+ 黑色等值线(vtkStripper+vtkSplineFilter 样条平滑,每条线只标一个数值)+ NaN 白边裁剪 + 底部异常表/描述。
数据 API(均经 Playwright 实测):
| 页 | 接口 |
|---|---|
| 原数据 | lvl/colorGradation/getDetail(type1) + dd/ert/inversion/getErtRawDataScatterGraph/{id} |
| 网格 | dd/ert/inversion/rows/{id}(服务端网格化,波动 1–4s) + colorGradation/getDetail(type2) + exception/queryException/{id} |
⚠️ 架构偏离:原 spec 定 QGraphicsView,实际落地用 QwtPlot(见
plans/2026-06-11-dataset-detail-chart-v2-qwt.md,权威)。
策略分派已打通(本次会话):控制器从硬编码 dd_inversion_data 改为走 ChartStrategyRegistry(在 controller 层 src/controller/IDatasetChartStrategy.hpp,含 hasGridPhase()),未注册类型优雅降级「暂不支持」。ErtInversionStrategy(app 层)已注册。这是接其余 dd 类型的地基。
4. 第二块:全 App 网络层异步化(本次会话完成,无技术债)
动机:原 ApiClient 用 QEventLoop 死等每个请求 → 全 App 冻 UI(网格 rows 1–4s 最痛)。
核心安全不变量(spec §5.0,务必遵守):「abort 后绝不回灌」靠三件套——
- 每层
aborted_入口守卫(disconnect只是尽力而为,挡不住已入队的迟到信号); - 控制器句柄身份比对(
if (load != current_) return;丢弃迟到信号); - 一律
deleteLater,禁止同步 delete。 错误判定口径:业务code != 200 || !rawError.isEmpty()(HTTP 200 也可能 code=500)。
net 层原语(src/net/,AUTOMOC 已 ON):
IApiCall/ApiCall:单请求句柄(包 QNetworkReply),finished(ApiResponse)+abort(),自管理 deleteLater。ApiBatch:并发汇聚 N 个 IApiCall,全成功succeeded(QList)/ 任一失败 fail-fastfailed(i,resp)+abort 其余。ApiChain:串行依赖链(上步结果喂下步工厂,工厂可抛转 failed)。契约:首个 step 工厂不得同步抛/同步 fire(否则信号在调用方连接前丢失;生产路径都发异步请求,满足)。ApiResponseParse::buildResponse:sync/async 共用解析(已无 sync 调用方,仅 ApiCall 用)。ApiClient:仅getAsync/postJsonAsync(同步get/postJson/await/QEventLoop 已删除)。
data 层:
- 详情:
ChartLoad/GridLoad(抽象基 +ApiChartLoad/ApiGridLoad实现,包 ApiBatch + 注入 parse)、IAsyncDatasetRepository、ApiDatasetRepository.loadChartAsync/loadGridAsync、DatasetLoads.hpp(ChartParts/GridParts)。 - 导航:
NavRequest(单非模板句柄,done(QVariant)/failed(QString);ApiNavRequest包 IApiCall + 解析器)、NavLoads.hpp(各类型Q_DECLARE_METATYPE)、IAsyncProjectRepository(9 方法...Async后缀,返回NavRequest*,薄封装;汇聚/链编排放控制器)、ApiProjectRepository仅实现异步接口。
controller 层:
DatasetDetailController:abort-and-replace + 句柄身份比对 +loadStarted(dsId,Phase)+ 析构 abort;走ChartStrategyRegistry分派。WorkbenchNavController:全异步(NavRequest 续延依赖链 + 控制器内并发计数 + abort-and-replace + 身份比对);删除busy_/BusyGuard/drainPendingCheckedTms;busyChanged(bool)语义改为「有在飞句柄」(去抖);setCheckedTms入口去重 + 「最后一次为准」由 abort-and-replace 承接 +tmExceptionCache_缓存命中不发请求。对外信号面零改动(main.cpp 接线没动)。
app 层:
AuthService(net 层):fetchCaptchaAsync()→CaptchaLoad、loginAsync()→LoginLoad(内用 ApiChain:verifyCodeCheck→RSA(step2 工厂内)→login2)。LoginWindow:不冻 + 可取消(析构 abort),删repaint()hack。ProjectListDialog:改用IAsyncProjectRepository(NavRequest + abort-and-replace + 身份比对 + 析构 abort),过滤/分页/选项目行为等价。LoadingOverlay:网格懒加载「加载中」遮罩,接loadStarted、就绪/失败隐藏。main.cpp:qRegisterMetaType<ApiResponse>();装配注入 registry/异步 repo(引用绑定,接线信号面不变)。
执行方式:全程 subagent-driven(每块 implementer + spec 符合度评审 + 代码质量评审 opus + follow-up 加固)。测试 75 → 116(+41,含 abort 回灌防护回归用例)。
5. 关键文件地图
- net:
src/net/{IApiCall.hpp,ApiCall.*,ApiBatch.*,ApiChain.*,ApiResponseParse.*,ApiClient.*,AuthService.*,AuthLoads.*} - data:
src/data/api/{DatasetLoads.hpp,DatasetLoadHandles.*,ApiDatasetRepository.*,NavRequest.*,NavLoads.hpp,ApiProjectRepository.*,DatasetChartDto.*}、src/data/repo/{IAsyncDatasetRepository.hpp,IAsyncProjectRepository.hpp,IDatasetRepository.hpp(本地样例同步,保留),RepoTypes.hpp} - controller:
src/controller/{DatasetDetailController.*,WorkbenchNavController.*,IDatasetChartStrategy.hpp} - app:
src/app/main.cpp、src/app/panels/{DatasetDetailPanel.*,DatasetDetailPage.*,LoadingOverlay.*,chart/*}、src/app/login/LoginWindow.*、src/app/ProjectListDialog.*、src/app/panels/chart/ErtInversionStrategy.hpp - 测试:
tests/net/{FakeApiCall.hpp,test_api_batch.cpp,test_api_chain.cpp,test_auth.cpp(live),test_auth_loads.cpp}、tests/data/{test_nav_request.cpp,test_dataset_load_handles.cpp}、tests/controller/{test_dataset_detail_controller.cpp,test_workbench_nav_controller.cpp}、tests/app/test_chart_strategy_registry.cpp
6. 尚未完成 / 下一步(按优先级)
A. 收尾本分支(最先)
分支领先 main 68 commits、116/116 绿、工作区干净。用户多次选「保持现状」未合并。下一步可问用户:①合并回 main(本地) ②推送建 PR(origin=gitea https://gitea.geomative.cn/gaozheng/geopro.git) ③保持现状。用 superpowers:finishing-a-development-branch。
B. 其余 dd 类型详情图(计划已写,多数 BLOCKED)
计划:plans/2026-06-11-dataset-detail-other-dd-types.md。
- Phase 1(策略分派打通):✅ 已完成(本次会话)。
- Phase 0(样本探查):需做——用 Playwright 登录原版 web,对每类 dd 找有数据对象、抓真实响应存 fixtures + 渲染规格。这是后续所有类型的前置。
- Phase 2(ERT 测量散点)/ Phase 3(TEM 折线,新 LineChartView):仅当 Phase 0 确认有样本才解锁。
- Phase 4:BLOCKED——dd_grid/轨迹/测井/GPR,当前租户无活数据样本(GPR 对象无数据、无测井数据),按 1:1 复刻铁律不能凭空实现。
- 矩阵详见计划文件。
C. 工具条编辑类功能(网格页占位按钮,未做,单独立项)
白化 / 滤波处理 / 色阶配置 / 异常框注 / 自动标注 / 网格化 / 另存为 / 导出 / 描述富文本 / 大视图(Esc)全屏。当前是占位按钮。交互较重,建议先 brainstorm 拆解。spec §2.3 列为范围外。
D. 纯整洁 follow-up(非债、非阻断)
- 删
DatasetDetailController::ChartData.grid/gridScale死字段(从未填充,预存)。 - 若未来引入 cross-thread/QueuedConnection,再补
qRegisterMetaType<QList<ApiResponse>>()。
7. 相关文档
- spec:
docs/superpowers/specs/2026-06-11-apiclient-async-design.md(异步设计 + §5.0 安全不变量 + §7 错误判定/退出契约 + 顶部「状态更新」=已完成)、docs/superpowers/specs/2026-06-11-dataset-detail-view-design.md(详情视图,顶部状态=仅 ERT 反演完成 + QwtPlot 偏离)。 - plan:
docs/superpowers/plans/2026-06-11-apiclient-async-datasetdetail.md(详情试点)、2026-06-11-apiclient-async-rollout.md(导航 Part A + 登录 Part B,均落地)、2026-06-11-dataset-detail-other-dd-types.md(其余 dd 类型,Phase 1 done)、2026-06-11-dataset-detail-chart-v2-qwt.md(QwtPlot 返工,权威)。 - API 文档:
docs/apis(business_OpenAPI.json)。 - 外部方案:
docs/Geopro3.0_二维图表返工技术方案.md。
8. 记忆(务必遵守,~/.claude/projects/D--Git-lanbingtech-geopro/memory/)
reply-in-chinese— 全程中文回复。study-original-via-playwright— 任何不确定必须用 Playwright 实地看原版,禁止臆测(尤其做新 dd 类型详情图前抓样本/规格)。no-embellishment-replicate-exactly— 严格 1:1,不加原版没有的特性(曾因等值线「周期重复标注」被纠正)。
9. 工作方式备注(本次会话沿用,效果好)
- 用户偏好 subagent-driven 执行 + 实现后 code review + spec 符合度 双评审(opus 评审);「非必要不停,一口气做完」。
- 真并行构建在本项目不安全(dev-build 硬编码单一 build 目录 + 多任务共改 main.cpp/CMakeLists)→ 用顺序执行;worktree 隔离不了构建(硬编码路径 + 冷配置开销)。
- 破坏性接口改形需原子落地(同批提交)保持构建绿(详情试点 Task5+6 合并、Part A A4+A5、本次清债 都是此教训)。
- 评审 follow-up 多为小加固,直接在新代码上补;「不为不可能的场景写错误处理」(CLAUDE.md §2)——评审若建议为构造保证不会发生的情况加防御,可不采纳。