Skip to content

fix: start notification expire timer only after bubble is displayed - #1691

Open
Ivy233 wants to merge 1 commit into
linuxdeepin:masterfrom
Ivy233:fix/notification-expire-after-display
Open

Ivy233 wants to merge 1 commit into
linuxdeepin:masterfrom
Ivy233:fix/notification-expire-after-display

Conversation

@Ivy233

@Ivy233 Ivy233 commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor
  1. Move the pending-timeout machinery from NotificationManager to a frontend ExpireTimer singleton, keeping one shared QTimer and absolute deadlines in a single QMultiHash while removing all server-side timer state
  2. Start a countdown only when a notification is actually inserted into the bubble model or the visible staging model, so queued, hidden and overflow notifications no longer expire before being displayed
  3. Make ExpireTimer::push idempotent for the same entity (same id + cTime), so the bubble and the staging area share the first deadline instead of restarting it, and cancel the old bubble-slot countdown when a replacement arrives
  4. Move hover blocking into ExpireTimer::setBlockId with a short grace period, without spinning the shared timer at a zero interval
  5. Keep the server authoritative for the notification lifecycle: expiration is reported to NotificationManager::notificationClosed via a queued invocation, and countdowns are stopped from the server's NotificationStateChanged instead of frontend model removal; notificationClosed now reports a close only once per notification
  6. Derive effective timeouts from NotifyEntity (timeout 0 or Critical urgency never expires, -1 falls back to the 5000 ms default) and carry bubbleId in the expired signal
  7. Match staging-area replacements by bubble id (the key preserved across a replace), like BubbleModel::replaceBubbleIndex
  8. Remove the obsolete pending-timeout code from NotificationManager/NotifyServerApplet and its outdated tests
  9. Also fix taskbar icons not updating when the Icon field of the desktop file changes (forward dataChanged in DockGlobalElementModel, fix refresh of multi-mapped rows in RoleCombineModel)

Log: Start the notification expire countdown only after the notification is displayed

Influence:

  1. A displayed notification disappears after its expire timeout (default 5 seconds); queued notifications no longer expire while hidden
  2. Hovering a bubble keeps it on screen, and it lingers about 1 second after the hover ends
  3. The same notification shown in the bubble and the staging area shares one countdown and is not timed twice
  4. Run the notification server and taskmanager unit tests

fix: 通知显示后才启动过期计时

  1. 将 NotificationManager 中的 pending-timeout 机制整体迁移到前端 ExpireTimer 单例:保留单个共享 QTimer 与按绝对截止时间组织的 QMultiHash,移除服务端全部定时器状态
  2. 仅在通知真正插入气泡模型或可见的暂存区模型时才启动倒计时,排队、隐藏和溢出的通知不再提前过期
  3. ExpireTimer::push 对同一通知(相同 id + cTime)幂等:气泡与暂存区共享首次截止时间、不重复计时;替换通知按 bubbleId 取消旧槽位倒计时后再启动新计时
  4. 悬停阻断逻辑下沉到 ExpireTimer::setBlockId,移开悬停后有短暂宽限期,共享 QTimer 不会以 0 间隔空转
  5. 生命周期仍由服务端统一管理:到期经 queued 调用转入 NotificationManager::notificationClosed,前端根据服务端 NotificationStateChanged 停止计时而非自行移除;notificationClosed 增加去重,避免同一通知重复上报关闭
  6. 有效超时时间由 NotifyEntity 推导(timeout 为 0 或 Critical 紧急级别永不过期,-1 回退默认 5000ms),expired 信号携带 bubbleId
  7. 暂存区替换匹配改用 bubbleId(替换时唯一不变的键),与 BubbleModel::replaceBubbleIndex 保持一致
  8. 删除 NotificationManager/NotifyServerApplet 中过时的 pending-timeout 代码及其旧测试
  9. 同时修复 desktop 文件 Icon 字段变化后任务栏图标不更新的问题(DockGlobalElementModel 转发 dataChanged,RoleCombineModel 修复多对一映射行的刷新)

Log: 通知显示后才启动过期计时

Influence:

  1. 气泡显示后默认 5 秒消失;大量通知排队时不再在显示前提前过期
  2. 鼠标悬停气泡时不消失,移开后停留约 1 秒
  3. 同一通知在横幅与暂存区共用同一倒计时,不会重复计时
  4. 运行通知服务端与任务管理器单元测试

PMS: BUG-372279

Summary by Sourcery

Start notification expiration when notifications become visible and centralize countdown management across presentation models.

Bug Fixes:

  • Start notification expiration only after a notification is displayed, preventing queued, hidden, and overflow notifications from expiring prematurely.
  • Keep displayed notifications alive while hovered and provide a brief grace period after hover ends.
  • Ensure taskbar icons refresh when desktop-file Icon values change.

Enhancements:

  • Centralize frontend expiration tracking with shared, idempotent countdowns across bubble and staging views while preserving server-authoritative notification lifecycle handling.
  • Derive expiration behavior from notification timeout and urgency, support replacement matching by bubble ID, and prevent duplicate close reports.

Tests:

  • Add ExpireTimer unit coverage for expiration, duplicate pushes, hover blocking, replacements, and non-expiring critical notifications.
  • Update notification server tests after removing obsolete timeout and hover APIs.

Chores:

  • Remove obsolete server-side pending-timeout state and handling.

@deepin-ci-robot

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Ivy233

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR defers starting the notification expiration timer until a bubble is actually inserted into the UI model, wiring a new bubbleDisplayed signal through BubblePanel to NotificationManager, which now schedules timeouts based on the stored client expire timeout only when notifications are displayed, with thread-safe forwarding from the applet and added unit tests.

Sequence diagram for deferred notification timeout start when bubble is displayed

sequenceDiagram
    participant BubbleModel
    participant BubblePanel
    participant NotifyServerApplet
    participant NotificationManager

    BubbleModel->>BubbleModel: insertBubble / replaceBubble
    BubbleModel-->>BubblePanel: bubbleDisplayed(id)
    BubblePanel->>NotifyServerApplet: notificationDisplayed(id)
    NotifyServerApplet->>NotificationManager: notificationDisplayed(id)
    NotificationManager->>NotificationManager: fetchEntity(id)
    NotificationManager->>NotificationManager: pushPendingEntity(entity, entity.timeout())
Loading

File-Level Changes

Change Details Files
Start notification expiration only when the bubble is displayed instead of on receipt.
  • Removed immediate scheduling of pending notification timeouts in Notify() based on hints expireTimeout.
  • Added a notificationDisplayed(qint64) slot in NotificationManager that fetches the entity, checks validity/urgency, reads its timeout(), and calls pushPendingEntity only for non-critical, expiring notifications.
  • Introduced a timeout() accessor on NotifyEntity to expose the stored expire timeout instead of passing it around separately.
panels/notification/server/notificationmanager.cpp
panels/notification/server/notificationmanager.h
panels/notification/common/notifyentity.cpp
panels/notification/common/notifyentity.h
Signal when a bubble is actually shown and propagate that to the notification server with correct threading semantics.
  • Added a bubbleDisplayed(qint64) signal to BubbleModel and emit it when inserting or replacing bubbles in the model.
  • Connected BubbleModel::bubbleDisplayed in BubblePanel to forward the ID to the notification server via notificationDisplayed using a direct connection.
  • Implemented NotifyServerApplet::notificationDisplayed to forward the call into NotificationManager::notificationDisplayed using Qt::QueuedConnection so the timeout QTimer starts on the worker thread.
panels/notification/bubble/bubblemodel.h
panels/notification/bubble/bubblemodel.cpp
panels/notification/bubble/bubblepanel.cpp
panels/notification/server/notifyserverapplet.h
panels/notification/server/notifyserverapplet.cpp
Add unit coverage for the new notificationDisplayed path to ensure robustness for various IDs.
  • Added a basic NotifyServerApplet test that calls notificationDisplayed with a valid ID to ensure no crashes.
  • Added edge-case tests that call notificationDisplayed with 0, -1, and max qint64, verifying the applet handles these IDs without throwing.
tests/panels/notification/server/notifyserverapplet_test.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Ivy233
Ivy233 marked this pull request as ready for review August 6, 2026 06:59

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @Ivy233, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch from 430dd63 to fcf70d8 Compare August 6, 2026 11:52
Comment thread panels/notification/bubble/bubblemodel.cpp Outdated
Comment thread panels/notification/bubble/bubblemodel.cpp Outdated
Comment thread panels/notification/bubble/bubblemodel.cpp Outdated
@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch 2 times, most recently from 0b0eb8c to 2ae625f Compare August 7, 2026 06:21
@Ivy233
Ivy233 requested a review from 18202781743 August 7, 2026 06:25
@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch from 2ae625f to 5ef467e Compare August 7, 2026 08:07
Comment thread panels/notification/server/notificationmanager.cpp
Comment thread panels/notification/common/expiretimer.h Outdated
Comment thread panels/notification/common/expiretimer.h
@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch from 5ef467e to aa5a30c Compare August 10, 2026 03:27
@Ivy233
Ivy233 requested a review from 18202781743 August 10, 2026 05:11
@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch from aa5a30c to fadfda6 Compare August 11, 2026 09:25
Comment thread panels/notification/common/expiretimer.cpp Outdated
Comment thread panels/notification/bubble/bubblemodel.cpp Outdated
Comment thread panels/notification/bubble/bubblemodel.cpp Outdated
Comment thread panels/notification/common/expiretimer.cpp Outdated
Comment thread panels/notification/common/expiretimer.cpp Outdated
Comment thread panels/notification/server/notificationmanager.cpp Outdated
@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch from fadfda6 to e12655f Compare August 11, 2026 14:25
@Ivy233
Ivy233 requested a review from 18202781743 August 11, 2026 14:25
Comment thread panels/notification/bubble/bubblemodel.cpp Outdated
Comment thread panels/notification/bubble/bubblepanel.cpp Outdated
Comment thread panels/notification/bubble/bubblemodel.cpp Outdated
Comment thread panels/notification/center/notifystagingmodel.cpp Outdated
@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch 3 times, most recently from a5757b6 to c7b598c Compare August 14, 2026 07:28
@Ivy233
Ivy233 requested a review from 18202781743 August 14, 2026 07:30
Comment thread panels/notification/center/notifystagingmodel.cpp Outdated
@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch 2 times, most recently from 0ea9c62 to 24d7693 Compare August 17, 2026 05:33
@Ivy233
Ivy233 requested a review from 18202781743 August 17, 2026 05:39
Comment thread panels/notification/center/notifystagingmodel.cpp Outdated
@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch from 24d7693 to 8c366c9 Compare August 17, 2026 07:43
@Ivy233

Ivy233 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

回复 auto review 提出的多线程竞态问题:经逐一核实,ExpireTimer 的所有调用点均在主线程,不存在跨线程访问 m_pendingEntities 的路径,该问题为误报,无需加锁。

  1. BubbleModel::insertBubble/replaceBubble、BubblePanel::setHoveredId、NotifyStagingModel::push/replace 均为 UI 侧调用,运行在主线程;
  2. NotifyServerApplet::init() 中连接 NotificationStateChanged 的 lambda(内部调用 ExpireTimer::remove)使用 Qt::QueuedConnection,接收者为 applet 本身(主线程创建),因此该 lambda 也在主线程执行(panels/notification/server/notifyserverapplet.cpp:65);
  3. 唯一的跨线程调用是 expired → m_manager->notificationClosed,通过 Qt::QueuedConnection 投递到 worker 线程(notifyserverapplet.cpp:74-77),该路径不触碰 ExpireTimer 内部状态;
  4. 单例首次构造发生在 init()(主线程),QTimer 与哈希表同属主线程,符合 QTimer 的线程亲和性要求。

另外,示例中在持锁状态下调用 m_timer->start() 并不能解决跨线程问题——QTimer 只能在其所属线程启动,真正跨线程时需要 QMetaObject::invokeMethod 投递;本实现已确保全部访问在主线程,故无此问题。

@Ivy233

Ivy233 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

/test github-pr-review-ci

@Ivy233
Ivy233 requested a review from 18202781743 August 17, 2026 09:43
@deepin-bot

deepin-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

TAG Bot

New tag: 2.0.53
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #1702

Comment thread panels/notification/server/notificationmanager.cpp Outdated
@deepin-bot

deepin-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown

TAG Bot

New tag: 2.0.54
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #1733

@deepin-bot

deepin-bot Bot commented Sep 22, 2026

Copy link
Copy Markdown

TAG Bot

New tag: 2.0.55
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #1752

@deepin-bot

deepin-bot Bot commented Sep 23, 2026

Copy link
Copy Markdown

TAG Bot

New tag: 2.0.56
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #1753

@Ivy233
Ivy233 requested a review from 18202781743 September 24, 2026 09:19
@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch 3 times, most recently from f5f29a6 to db17683 Compare September 28, 2026 05:29
// here instead of letting each frontend view call ExpireTimer::remove on
// its own. remove() by id is a no-op when the entry already expired on its
// own (its deadline was dropped when expired() was emitted).
connect(m_manager, &NotificationManager::NotificationStateChanged, this, [](qint64 id, int processedType) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NotificationStateChanged 这个不需要吧,在其它close里的路径里处理,

@Ivy233 Ivy233 Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已按建议调整:删除了新增的 NotificationStateChanged → ExpireTimer::remove 连接,改为在 NotifyServerApplet::notificationClosed() 主线程槽内直接调用 ExpireTimer::remove(id)。气泡按钮与通知中心的关闭路径都汇入该槽,在这里停表时机最早——先于 queued 投递给 manager 的关闭请求,也缩小了此前注释中提到的竞态窗口;自然到期路径无需 remove(条目在 expired 发出时已删除,remove 为幂等空操作)。

这里删除的是新增的计时器清理连接;init() 中原有的
NotificationStateChanged → NotifyServerApplet::notificationStateChanged
连接仍需保留。它负责把服务端的处理状态转发给前端,BubblePanel 依赖该信号移除已处理或已关闭的通知,并不属于 ExpireTimer 的清理逻辑。

不经过 notificationClosed() 槽的关闭路径(如第三方通过 D-Bus 调用 CloseNotification)无法在自身路径上停表:该调用经 adaptor 直接落在工作线程的 manager 上,而 ExpireTimer 及其 QTimer 具有主线程亲和性,就地调用属于跨线程访问。这类路径由 notificationClosed() 中 processedType != NotProcessed 的守卫兜底:滞留的倒计时条目到期后只会触发一次被守卫拒绝的空跑,不会对同一通知重复发出 NotificationClosed。

Start the expire countdown when a notification is actually displayed
instead of when its Notify call reaches the server, so a notification
can no longer time out before the user has seen it, and let the views
that present the notification drive that countdown:

1. Add ExpireTimer, a process-wide singleton in
panels/notification/common that keeps the deadline of every displayed
notification in a QMultiHash keyed by absolute time point and drives a
shared single-shot QTimer. It is the server's former pending-timeout
machinery moved to the display side. push() is idempotent: when the
bubble and the notification center show the same entity (same id and
cTime), the first deadline is kept instead of restarting it, a
replacement notification cancels the countdown of the bubble slot it
takes over, and a non-positive timeout (Critical urgency or
expireTimeout 0) means the notification never expires. setBlockId()
replaces setBlockClosedId() and lets the hovered bubble survive its
deadline, with a short grace period after the hover moves away.

2. BubbleModel::insertBubble/replaceBubble and
NotifyStagingModel::push/replace/open start the countdown of the
entities they display, and the staging area matches replacements by
bubble id through the new rowByBubbleId(), since a replacement may
reuse the original entity id or arrive with a new one.

3. NotificationManager drops the pending timeout state and its whole
timer path (m_pendingTimeout, m_pendingTimeoutEntities,
m_blockClosedId, setBlockClosedId, pushPendingEntity,
removePendingEntity, onHandingPendingEntities), and
notificationClosed() now ignores close requests for an invalid or
already processed entity, so a close queued by the expire timer can no
longer emit NotificationClosed twice for the same notification.

4. NotifyServerApplet owns the lifecycle boundary: it closes the
notification in the manager when ExpireTimer emits expired(), stops
the countdown in its notificationClosed() slot, where the close paths
of both frontends (the bubble button and the notification center)
converge, and delivers its D-Bus slots to the manager, which lives on
the worker thread, via QueuedConnection. A close path that bypasses
this slot (e.g. the D-Bus CloseNotification called by a client) leaves
its countdown entry to fire into notificationClosed(), where the
processed guard rejects it.

5. NotifyEntity exposes timeout() and urgency(), the unused
BubbleModel::clear() is removed, and expiretimer_tests covers expiry,
idempotent push, hover blocking, replacement and Critical
notifications.

Log: start the expire countdown only after a notification is displayed

Influence:
1. Verify notifications shown by the bubble and by the notification
center expire after the client-specified timeout (5 s by default)
2. Verify a hovered bubble does not expire, and a replaced
notification starts a fresh countdown for the new content
3. Verify Critical notifications and expireTimeout 0 notifications
never close by themselves

fix(notification): 通知显示后再开始计算过期时间

将过期倒计时的起点从服务端收到 Notify 调用改为通知真正显示之时,
避免通知在用户看到之前就已超时,并由展示通知的视图驱动该倒计时:

1. 新增 ExpireTimer(panels/notification/common 下的进程级单例),
以 QMultiHash 按绝对时间点保存所有已显示通知的截止时间,并用一个共享
的单次 QTimer 驱动,即原服务端的待超时机制迁移至展示侧。push() 幂等:
气泡通知与通知中心展示同一实体(id 与 cTime 相同)时保留首次的截止
时间而不重新计时;替换通知会取消其所占气泡槽位的旧倒计时;超时时间为
非正值(Critical 紧急度或 expireTimeout 为 0)表示通知不会自动过期。
setBlockId() 取代 setBlockClosedId(),阻止悬停中的气泡过期,并在悬停
移开后保留一段短暂的宽限时间。

2. BubbleModel::insertBubble/replaceBubble 与
NotifyStagingModel::push/replace/open 在展示实体时启动其倒计时;
通知中心通过新增的 rowByBubbleId() 按气泡 id 匹配替换通知,因为替换
通知可能沿用原实体 id,也可能带来新的实体 id。

3. NotificationManager 移除待超时状态及整条定时器路径
(m_pendingTimeout、m_pendingTimeoutEntities、m_blockClosedId、
setBlockClosedId、pushPendingEntity、removePendingEntity、
onHandingPendingEntities);notificationClosed() 现在忽略无效或已处理
实体的关闭请求,使过期定时器排队的关闭请求不会对同一通知重复发出
NotificationClosed。

4. NotifyServerApplet 负责生命周期边界:ExpireTimer 发出 expired() 时
在 manager 中关闭对应通知;在气泡按钮与通知中心两条前端关闭路径共同
汇入的 notificationClosed() 槽中停止倒计时;由于 manager 位于工作
线程,其 D-Bus 槽改为 QueuedConnection 投递。不经过该槽的关闭路径
(如客户端通过 D-Bus 调用 CloseNotification)由 notificationClosed()
中对已处理实体的守卫拒绝。

5. NotifyEntity 新增 timeout()/urgency() 接口,删除已无用的
BubbleModel::clear(),并新增 expiretimer_tests 覆盖过期、幂等 push、
悬停阻塞、替换通知和 Critical 通知等场景。

Log: 通知显示后才开始过期倒计时

Influence:
1. 验证气泡通知与通知中心展示的通知按客户端指定的超时时间(默认 5 秒)过期
2. 验证悬停中的气泡不过期,替换通知按新内容重新开始倒计时
3. 验证 Critical 通知和 expireTimeout 为 0 的通知不会自动关闭

PMS: BUG-372279
@Ivy233
Ivy233 force-pushed the fix/notification-expire-after-display branch from db17683 to 420992a Compare September 29, 2026 13:28
@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 97 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
评分详情 总体评分 97 分,大于 70 分通过阈值,代码质量优秀。本次 PR 将通知过期计时器逻辑从服务端移至前端展示层,修复了计时器在气泡显示前就启动的核心问题,同时改进了线程安全性和竞态条件处理。

🔍 详细分析

1. 语法逻辑 ✅

评价: 优秀 ✅ 通过

潜在问题:

  1. panels/notification/common/expiretimer.cpp:378 - static_cast转换可能对极大timeout值溢出,建议增加上限保护

建议: 建议在timer间隔转换处增加上限保护,如 qMin(interval, INT_MAX) 防止极端情况下的溢出


2. 代码质量 ✅

评价: 优秀 ✅ 通过

潜在问题:

  1. panels/notification/bubble/bubblemodel.cpp:103 - BubbleModel::clear()方法被移除,需确认无残留调用方

建议: 代码注释质量优秀,设计决策文档化程度高。建议确认clear()移除后无编译期遗漏的调用方


3. 代码性能 ✅

评价: 优秀 ✅ 通过

潜在问题:

  1. panels/notification/common/expiretimer.cpp:463 - onTimeout()中std::sort可优化为std::min_element以降低复杂度

建议: 对于通知系统典型的小规模数据(通常<20个),当前O(n)性能完全可接受。单QTimer设计配合最近截止时间驱动是高效的方案


4. 代码安全 🔒

评价: 优秀 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: 线程安全性通过将Qt::DirectConnection改为Qt::QueuedConnection得到改善。notificationClosed中新增的processedType()守卫有效防止了竞态条件下的重复关闭问题。无用户输入注入、无硬编码凭证、无缓冲区溢出风险。


💡 改进建议代码示例

// expiretimer.cpp - 建议在timer间隔转换处增加上限保护
void ExpireTimer::onTimeout()
{
    // ... 处理过期实体 ...

    if (m_pendingEntities.isEmpty()) {
        m_timer->stop();
        m_lastPoint = std::numeric_limits<qint64>::max();
        return;
    }

    auto points = m_pendingEntities.keys();
    // 优化:使用 std::min_element 替代 std::sort,O(n) vs O(n log n)
    m_lastPoint = *std::min_element(points.begin(), points.end());
    
    // 增加上限保护,防止极端timeout值导致int溢出
    auto rawInterval = qMax<qint64>(0, m_lastPoint - QDateTime::currentMSecsSinceEpoch());
    int safeInterval = static_cast<int>(qMin<qint64>(rawInterval, std::numeric_limits<int>::max()));
    m_timer->start(safeInterval);
}

本报告由 AI 代码审查工具自动生成

@Ivy233
Ivy233 requested a review from 18202781743 September 30, 2026 03:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants