From 8337db5ac9ffeea1e2abbb8c2e43cd3040eacd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Thu, 4 Jun 2026 16:53:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=AC=E5=91=8A=E9=87=8D=E5=A4=8D=E6=8F=90?= =?UTF-8?q?=E9=86=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_notice/models/yuthon_notice.py | 34 ++++++++++++++++++++------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/yuthon_notice/models/yuthon_notice.py b/yuthon_notice/models/yuthon_notice.py index 0c293ae0c..0757932ba 100644 --- a/yuthon_notice/models/yuthon_notice.py +++ b/yuthon_notice/models/yuthon_notice.py @@ -207,23 +207,39 @@ class YuthonNotice(models.Model): def notice_publish(self): self.ensure_one() - # 幂等性保护:如果已经发布,不再执行 - if self.notice_state == 'yes': - _logger.info(f"公告 {self.name} (ID: {self.id}) 已发布,跳过重复发布") + # ========== 数据库行级锁:防止并发/重复调用 ========== + # 在事务中对当前公告行加排他锁。如果 notice_publish 被 workflow + # 多次触发,第二个及后续调用会在此等待,直到第一个完成。 + # 锁内 flush 后重读 notice_state 即可安全判断是否重复。 + self.env.cr.execute( + "SELECT notice_state FROM yuthon_notice WHERE id = %s FOR UPDATE", + (self.id,) + ) + db_state = self.env.cr.fetchone() + if db_state and db_state[0] == 'yes': + _logger.info( + f"公告 {self.name} (ID: {self.id}) 已发布(数据库确认),跳过重复发布" + ) return - - self.notice_state = 'yes' - # 查询数据库确认是否已有发送记录(防止同一事务中多次触发) + # 标记已发布并立即刷入数据库,让后续 lock 等待者可见 + self.notice_state = 'yes' + self.env.cr.flush() + + # 二次确认:检查是否已有发送记录 already_sent = self.env['yuthon.confirm.users'].sudo().search_count([ ('notice_id', '=', self.id), ('msg_id', '!=', False), ], limit=1) if already_sent: - _logger.warning(f"公告 {self.name} (ID: {self.id}) 数据库已有发送记录,跳过") + _logger.warning( + f"公告 {self.name} (ID: {self.id}) 数据库已有发送记录,跳过" + ) return - - _logger.info(f"公告 {self.name} (ID: {self.id}) 开始发布,用户数: {len(self.users_ids)}") + + _logger.info( + f"公告 {self.name} (ID: {self.id}) 开始发布,用户数: {len(self.users_ids)}" + ) if self.is_change_notice: conf_old = self.env['yuthon.confirm.users'].sudo().search([ ('notice_id', '=', self.id),