1
0
forked from erp-dev/erp

fux: mdy_plate_order sync pk conflict

This commit is contained in:
2026-01-21 18:16:43 +08:00
parent 43399385a8
commit 49ac17cf37
6 changed files with 414 additions and 9 deletions

View File

@@ -4,6 +4,8 @@ from datetime import datetime
import time
from typing import Literal
from django.db import connection
from django.db.utils import IntegrityError
from django.utils import timezone
from api_v1 import models as api_models
@@ -19,6 +21,42 @@ from flower.utils.mingdaoyun.relations import (
logger = logging.getLogger(__name__)
_MDY_PLATE_ORDER_STAGING_PK_CONSTRAINT = "api_mdy_plate_order_staging_pkey"
def _reset_mdy_plate_order_staging_id_sequence() -> None:
"""修复 PostgreSQL 自增序列落后导致的主键重复duplicate key violates ..._pkey
常见场景:数据库 restore/copy 后sequence 未随数据同步到最新值。
"""
table_name = api_models.MDYPlateOrderStaging._meta.db_table # 'api_mdy_plate_order_staging'
pk_column = api_models.MDYPlateOrderStaging._meta.pk.column # 'id'
with connection.cursor() as cursor:
cursor.execute(f"SELECT COALESCE(MAX({pk_column}), 1) FROM {table_name}")
max_id = cursor.fetchone()[0] or 1
cursor.execute("SELECT pg_get_serial_sequence(%s, %s)", [table_name, pk_column])
seq_name = cursor.fetchone()[0]
if not seq_name:
logger.warning("未找到 %s.%s 的 serial sequence跳过 setval", table_name, pk_column)
return
# is_called=true => nextval 会返回 max_id+1
cursor.execute("SELECT setval(%s, %s, true)", [seq_name, max_id])
logger.warning(
"已重置 sequence=%s 到 max(id)=%s(下次插入将从 %s 开始)",
seq_name,
max_id,
max_id + 1,
)
def _is_pk_duplicate_error(exc: Exception) -> bool:
"""判断是否为主键约束冲突(多见于 sequence 未对齐)。"""
msg = str(exc) or ""
return _MDY_PLATE_ORDER_STAGING_PK_CONSTRAINT in msg
def _parse_mdy_datetime(value: str | None):
if not value:
@@ -196,15 +234,33 @@ def sync_mdy_plate_orders_to_staging(
if not rowid:
continue
api_models.MDYPlateOrderStaging.objects.update_or_create(
mdy_rowid=rowid,
defaults={
"ctime": _parse_mdy_datetime(row.get("ctime")),
"utime": _parse_mdy_datetime(row.get("utime")),
"raw": row,
"related": related_map.get(rowid, []),
},
)
defaults = {
"ctime": _parse_mdy_datetime(row.get("ctime")),
"utime": _parse_mdy_datetime(row.get("utime")),
"raw": row,
"related": related_map.get(rowid, []),
}
try:
api_models.MDYPlateOrderStaging.objects.update_or_create(
mdy_rowid=rowid,
defaults=defaults,
)
except IntegrityError as exc:
# 仅对“主键重复(通常为 sequence 未对齐)”做一次自愈重试。
if _is_pk_duplicate_error(exc):
logger.warning(
"检测到暂存表主键冲突尝试重置序列后重试一次rowid=%s err=%s",
rowid,
exc,
)
_reset_mdy_plate_order_staging_id_sequence()
api_models.MDYPlateOrderStaging.objects.update_or_create(
mdy_rowid=rowid,
defaults=defaults,
)
else:
raise
synced_rows += 1
record_ctime = _parse_mdy_datetime(row.get("ctime"))