88 lines
4.3 KiB
C++
88 lines
4.3 KiB
C++
#include "DatasetDetailController.hpp"
|
||
#include <exception>
|
||
#include <QtGlobal>
|
||
#include "repo/IAsyncDatasetRepository.hpp"
|
||
#include "api/DatasetLoadHandles.hpp"
|
||
namespace geopro::controller {
|
||
|
||
DatasetDetailController::DatasetDetailController(data::IAsyncDatasetRepository& repo,
|
||
ChartStrategyRegistry& registry, QObject* parent)
|
||
: QObject(parent), repo_(repo), registry_(registry) {
|
||
// QSignalSpy / 队列连接对自定义类型需注册(页签集随 datasetOpened 传递)。
|
||
qRegisterMetaType<std::vector<controller::TabSpec>>("std::vector<controller::TabSpec>");
|
||
}
|
||
|
||
DatasetDetailController::~DatasetDetailController() {
|
||
// 退出契约:abort 全部在飞句柄,不依赖外部析构顺序兜底。
|
||
for (auto& load : inflight_)
|
||
if (load) load->abort();
|
||
}
|
||
|
||
void DatasetDetailController::openDataset(const QString& dsId, const QString& ddCode,
|
||
const QString& dsName, const QString& tmObjectId) {
|
||
qInfo("[detail] openDataset id=%s ddCode=%s name=%s tm=%s", qUtf8Printable(dsId),
|
||
qUtf8Printable(ddCode), qUtf8Printable(dsName), qUtf8Printable(tmObjectId));
|
||
auto* s = registry_.find(ddCode.toStdString());
|
||
if (!s) { // 未注册策略 → 优雅降级
|
||
qWarning("[detail] 未注册策略 ddCode=%s → 降级提示", qUtf8Printable(ddCode));
|
||
emit loadFailed(dsId, QStringLiteral("暂不支持该数据类型的预览"));
|
||
return;
|
||
}
|
||
const std::vector<controller::TabSpec> tabs = s->tabs();
|
||
emit datasetOpened(dsId, ddCode, dsName, tmObjectId, tabs);
|
||
for (int i = 0; i < static_cast<int>(tabs.size()); ++i)
|
||
if (!tabs[static_cast<size_t>(i)].lazy) loadTab(dsId, ddCode, i);
|
||
}
|
||
|
||
void DatasetDetailController::loadTab(const QString& dsId, const QString& ddCode, int tabIndex) {
|
||
loadTabImpl(dsId, ddCode, tabIndex, /*pageNo*/ 1, /*pageSize*/ 0);
|
||
}
|
||
|
||
void DatasetDetailController::loadTabPaged(const QString& dsId, const QString& ddCode, int tabIndex,
|
||
int pageNo, int pageSize) {
|
||
loadTabImpl(dsId, ddCode, tabIndex, pageNo, pageSize);
|
||
}
|
||
|
||
void DatasetDetailController::loadTabImpl(const QString& dsId, const QString& ddCode, int tabIndex,
|
||
int pageNo, int pageSize) {
|
||
auto* s = registry_.find(ddCode.toStdString());
|
||
if (!s) return; // 策略消失(不应发生):静默不加载
|
||
const std::vector<controller::TabSpec> tabs = s->tabs();
|
||
if (tabIndex < 0 || tabIndex >= static_cast<int>(tabs.size())) return;
|
||
const controller::TabSpec& spec = tabs[static_cast<size_t>(tabIndex)];
|
||
|
||
// loadAsync 对未知 loaderKey 抛 std::runtime_error;若逃逸槽函数会被 GuardedApplication
|
||
// 吞掉、遮罩永久悬挂(文档化的崩溃/挂起类)。就地兜底为 loadFailed,且不留半注册的在飞句柄。
|
||
data::DetailLoad* load = nullptr;
|
||
try {
|
||
load = repo_.loadAsync(spec.loaderKey.toStdString(), dsId.toStdString(), pageNo, pageSize);
|
||
} catch (const std::exception& e) {
|
||
qWarning("[detail] loadAsync 失败 id=%s tab=%d key=%s: %s", qUtf8Printable(dsId), tabIndex,
|
||
qUtf8Printable(spec.loaderKey), e.what());
|
||
emit loadFailed(dsId, QStringLiteral("暂不支持该数据类型的预览"));
|
||
return;
|
||
}
|
||
|
||
if (auto& prev = inflight_[tabIndex]) prev->abort(); // abort-and-replace 该槽位
|
||
inflight_[tabIndex] = load;
|
||
emit tabLoadStarted(dsId, tabIndex);
|
||
|
||
QObject::connect(load, &data::DetailLoad::done, this,
|
||
[this, load, dsId, tabIndex](const QVariant& payload) {
|
||
if (load != inflight_.value(tabIndex)) return; // §5.0 句柄身份比对:丢弃迟到信号
|
||
inflight_.remove(tabIndex);
|
||
emit tabReady(dsId, tabIndex, payload);
|
||
});
|
||
QObject::connect(load, &data::DetailLoad::failed, this,
|
||
[this, load, dsId, tabIndex](const QString& msg) {
|
||
if (load != inflight_.value(tabIndex)) return;
|
||
inflight_.remove(tabIndex);
|
||
qWarning("[detail] 页签加载失败 id=%s tab=%d: %s", qUtf8Printable(dsId), tabIndex,
|
||
qUtf8Printable(msg));
|
||
emit loadFailed(dsId, msg);
|
||
});
|
||
}
|
||
|
||
void DatasetDetailController::focusDataset(const QString& dsId) { emit focusRequested(dsId); }
|
||
} // namespace geopro::controller
|