fix: scope transactional locks to primary records

This commit is contained in:
hailin
2026-07-27 20:31:35 +08:00
parent 6a4239e7bb
commit 8296f3c96f
6 changed files with 21 additions and 13 deletions

View File

@@ -146,7 +146,7 @@ def start_ticket_knowledge_distillation(
"""从可信解决结果启动幂等蒸馏运行,并冻结工单输入快照。"""
ticket = (
Ticket.objects.select_for_update()
Ticket.objects.select_for_update(of=("self",))
.select_related("category", "resolution")
.get(id=ticket.id)
)
@@ -255,7 +255,7 @@ def process_knowledge_distillation_result(
if run.definition.code != KNOWLEDGE_DISTILLATION_AGENT_CODE or run.status != "succeeded":
return None
distillation = (
TicketKnowledgeDistillation.objects.select_for_update()
TicketKnowledgeDistillation.objects.select_for_update(of=("self",))
.select_related("ticket__category", "ticket__resolution", "run__created_by")
.get(run=run)
)

View File

@@ -671,7 +671,7 @@ def _auto_create_ticket(
# Agent/Celery 重试可能并发进入;锁定受理记录后再判断是否已经完成建单。
draft = (
TicketDraft.objects.select_for_update()
TicketDraft.objects.select_for_update(of=("self",))
.select_related("created_by", "confirmed_ticket")
.prefetch_related("materials")
.get(id=draft_id)
@@ -944,7 +944,7 @@ def cancel_draft_analysis(
user,
) -> TicketDraft:
draft = (
TicketDraft.objects.select_for_update()
TicketDraft.objects.select_for_update(of=("self",))
.select_related("latest_agent_run", "created_by")
.get(public_id=draft_public_id, created_by=user)
)

View File

@@ -38,7 +38,7 @@ def ensure_customer_reply_task(
if not normalized:
return None
analysis = (
TicketSupportAnalysis.objects.select_for_update()
TicketSupportAnalysis.objects.select_for_update(of=("self",))
.select_related("ticket", "run", "created_by")
.get(id=analysis.id)
)
@@ -106,7 +106,7 @@ def update_customer_reply(
"""使用乐观版本号修订回复,已批准或已发送版本不可被静默覆盖。"""
task = (
CustomerReplyTask.objects.select_for_update()
CustomerReplyTask.objects.select_for_update(of=("self",))
.select_related("ticket", "support_analysis")
.get(id=task.id)
)
@@ -162,7 +162,7 @@ def approve_customer_reply_for_channel(
"""批准指定回复版本进入唯一渠道会话,不在此函数直接发送消息。"""
task = (
CustomerReplyTask.objects.select_for_update()
CustomerReplyTask.objects.select_for_update(of=("self",))
.select_related("ticket", "support_analysis")
.get(id=task.id)
)
@@ -227,7 +227,7 @@ def confirm_customer_reply_sent(
"""仅在人工确认或渠道明确送达后标记回复完成并记录审计。"""
task = (
CustomerReplyTask.objects.select_for_update()
CustomerReplyTask.objects.select_for_update(of=("self",))
.select_related("ticket", "support_analysis")
.get(id=task.id)
)

View File

@@ -493,7 +493,11 @@ def update_ticket_fields(
if unknown_fields:
raise TicketDataError(f"不允许修改字段: {', '.join(sorted(unknown_fields))}")
ticket = Ticket.objects.select_for_update().select_related("category").get(id=ticket.id)
ticket = (
Ticket.objects.select_for_update(of=("self",))
.select_related("category")
.get(id=ticket.id)
)
evidence_by_field = evidence_by_field or {}
confidence_by_field = confidence_by_field or {}
before: dict = {}
@@ -841,7 +845,7 @@ def adopt_solution_for_ticket(
if locked_solution.status != "published":
raise TicketDataError("只能采纳已发布的解决方案")
locked_ticket = (
Ticket.objects.select_for_update()
Ticket.objects.select_for_update(of=("self",))
.select_related("category", "resolution")
.get(id=ticket.id)
)

View File

@@ -508,7 +508,7 @@ def process_support_analysis_result(run: AgentRun) -> TicketSupportAnalysis | No
if run.definition.code != SUPPORT_AGENT_CODE or run.status != "succeeded":
return None
analysis = (
TicketSupportAnalysis.objects.select_for_update()
TicketSupportAnalysis.objects.select_for_update(of=("self",))
.select_related(
"ticket__category",
"ticket__resolution",
@@ -583,7 +583,11 @@ def process_support_analysis_result(run: AgentRun) -> TicketSupportAnalysis | No
def start_ticket_support_analysis(ticket: Ticket, *, created_by=None) -> TicketSupportAnalysis:
"""为工单创建唯一活动分析并投递绑定正式 Agent 版本的运行。"""
ticket = Ticket.objects.select_for_update().select_related("category").get(id=ticket.id)
ticket = (
Ticket.objects.select_for_update(of=("self",))
.select_related("category")
.get(id=ticket.id)
)
if ticket.archived_at is not None:
raise SupportAnalysisError("已归档工单不能启动支持分析")
if ticket.status in {"resolved", "closed"}:

View File

@@ -257,7 +257,7 @@ def sync_ticket_intake_failure_work_item(
actor=None,
) -> WorkItem | None:
draft = (
TicketDraft.objects.select_for_update()
TicketDraft.objects.select_for_update(of=("self",))
.select_related("created_by", "latest_agent_run")
.get(id=draft.id)
)