fix(controller): setCheckedTms busy 时挂起重放(不丢弃) + 切项目清 currentParentId/挂起态

This commit is contained in:
gaozheng 2026-06-10 21:22:39 +08:00
parent d435fca32d
commit 3daaad3de3
2 changed files with 33 additions and 4 deletions

View File

@ -1,5 +1,7 @@
#include "WorkbenchNavController.hpp"
#include <QMetaObject>
#include "dto/NavDto.hpp"
namespace geopro::controller {
@ -10,8 +12,8 @@ using data::Workspace;
WorkbenchNavController::WorkbenchNavController(data::IProjectRepository& repo, QObject* parent)
: QObject(parent), repo_(repo) {}
namespace {
// RAII进入公共导航操作时置忙驱动等待光标任何返回路径都复位——保证 busyChanged 配平。
// 命名(非匿名)以匹配 controller 的 friend 声明,从而在析构时排空挂起的勾选请求。
struct BusyGuard {
WorkbenchNavController* self;
bool* busy;
@ -20,11 +22,16 @@ struct BusyGuard {
emit self->busyChanged(true);
}
~BusyGuard() {
WorkbenchNavController* ctrl = self; // 取本地副本lambda 不能捕获成员
*busy = false;
emit self->busyChanged(false);
emit ctrl->busyChanged(false);
// 触发源是延迟合并发射,可能落在嵌套事件循环里:用队列调用在调用栈/嵌套循环展开后再排空,
// 那时 busy_ 已可靠为 false重放才会真正执行lambda 捕获的 ctrl 生命周期与应用一致,安全)。
if (ctrl->checkedTmsPending_)
QMetaObject::invokeMethod(
ctrl, [ctrl] { ctrl->drainPendingCheckedTms(); }, Qt::QueuedConnection);
}
};
} // namespace
void WorkbenchNavController::start() {
if (busy_) return;
@ -51,6 +58,10 @@ void WorkbenchNavController::loadProjectsAndStructure() {
}
lastProjects_ = ps.value.rows;
tmExceptionCache_.clear();
currentParentId_.clear(); // 切项目/工作空间重置选中态spec §6
currentParentConfType_ = 0;
checkedTmsPending_ = false; // 丢弃跨项目的陈旧挂起重放
pendingCheckedTms_.clear();
QString curP;
if (!ps.value.rows.empty()) {
const auto& first = ps.value.rows.front();
@ -107,6 +118,10 @@ void WorkbenchNavController::switchProject(const QString& projectId) {
}
lastStructNodes_ = st.value;
tmExceptionCache_.clear();
currentParentId_.clear(); // 切项目/工作空间重置选中态spec §6
currentParentConfType_ = 0;
checkedTmsPending_ = false; // 丢弃跨项目的陈旧挂起重放
pendingCheckedTms_.clear();
emit structureLoaded(QString::fromStdString(currentProjectName_), st.value);
}
@ -166,7 +181,11 @@ void WorkbenchNavController::loadMoreFiles() {
}
void WorkbenchNavController::setCheckedTms(const QStringList& tmObjectIds) {
if (busy_) return;
if (busy_) { // 触发源是延迟合并发射,可能落在别的同步操作的嵌套事件循环里:
pendingCheckedTms_ = tmObjectIds; // 不丢弃,记下最新一次请求,待空闲重放
checkedTmsPending_ = true;
return;
}
BusyGuard guard(this, &busy_);
auto nameOf = [this](const std::string& id) -> std::string {
for (const auto& n : lastStructNodes_)
@ -198,6 +217,12 @@ void WorkbenchNavController::setCheckedTms(const QStringList& tmObjectIds) {
emit exceptionTreeLoaded(groups, total);
}
void WorkbenchNavController::drainPendingCheckedTms() {
if (busy_ || !checkedTmsPending_) return;
checkedTmsPending_ = false; // 先清标志再重放,避免重入自旋
setCheckedTms(pendingCheckedTms_); // 此时 busy_=false会正常执行
}
void WorkbenchNavController::selectDataset(const QString& dsObjectId) {
if (dsObjectId.isEmpty() || busy_) return;
BusyGuard guard(this, &busy_);

View File

@ -45,10 +45,14 @@ signals:
void loadFailed(const QString& stage, const QString& message);
private:
friend struct BusyGuard; // 允许在 guard 析构时排空挂起的勾选请求
void loadProjectsAndStructure(); // start + switchWorkspace 共用
void drainPendingCheckedTms(); // 空闲后重放最近一次被挂起的勾选集
data::IProjectRepository& repo_;
bool busy_ = false;
bool checkedTmsPending_ = false;
QStringList pendingCheckedTms_;
std::vector<data::ProjectSummary> lastProjects_;
std::string currentWorkspaceId_, currentProjectId_, currentProjectName_, currentCrsCode_;
std::string currentParentId_;