先查后写无锁时,两管理员并发提交重叠区间互判不重叠而双双入库 (区间重叠无唯一键可拦,同一总分命中多等级且结果取决于插入顺序); 判重叠的 list_all 加 for_update 锁全表行串行化并发判定,只读直出 list_grades 不加锁。SQL 形态用例锁定两条口径。 Co-Authored-By: Claude Code <noreply@anthropic.com>
66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
"""客户评级等级配置数据访问。
|
||
|
||
本层只 flush 不 commit —— 事务边界由 services 层控制。
|
||
"""
|
||
|
||
from sqlalchemy import select
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.models import CustomerRatingGrade
|
||
|
||
# ==================== 读取 ====================
|
||
|
||
|
||
def list_all(session: Session, *, for_update: bool = False) -> list[CustomerRatingGrade]:
|
||
"""全量等级,按 sort_order 升序、id 兜底(同序次序稳定)。
|
||
|
||
不分页:等级映射是判定表,数量个位数,分页无意义。
|
||
|
||
for_update 供区间重叠判定使用:先查后写在无锁时,两管理员并发提交
|
||
重叠区间会互判不重叠而双双入库(区间重叠无唯一键可拦)——锁全表行
|
||
串行化并发的重叠判定(先到者提交后,后到者在锁下读到新区间即判重叠)。
|
||
只读直出(list_grades)不加锁。
|
||
"""
|
||
stmt = select(CustomerRatingGrade).order_by(
|
||
CustomerRatingGrade.sort_order, CustomerRatingGrade.id
|
||
)
|
||
if for_update:
|
||
stmt = stmt.with_for_update()
|
||
return list(session.execute(stmt).scalars())
|
||
|
||
|
||
def get_by_id(session: Session, grade_id: int) -> CustomerRatingGrade | None:
|
||
"""按主键查询。
|
||
|
||
等级表无下游引用(评级是纯计算器不落评分记录),无需 ``for_update``
|
||
锁行 —— product_repository 锁行防的是并发引用悬空,此处不存在该风险。
|
||
"""
|
||
return session.get(CustomerRatingGrade, grade_id)
|
||
|
||
|
||
def get_by_name(session: Session, grade_name: str) -> CustomerRatingGrade | None:
|
||
"""按等级名称查询。
|
||
|
||
grade_name 是 uk_customer_rating_grades_grade_name 唯一键的键值,先查
|
||
用于给出即时业务提示;并发窗口仍存在,最终由 IntegrityError 兜底
|
||
(见 rating_service)。
|
||
"""
|
||
stmt = select(CustomerRatingGrade).where(CustomerRatingGrade.grade_name == grade_name)
|
||
return session.execute(stmt).scalar_one_or_none()
|
||
|
||
|
||
# ==================== 写入 ====================
|
||
|
||
|
||
def create(session: Session, grade: CustomerRatingGrade) -> CustomerRatingGrade:
|
||
"""新增等级。只 flush 以取得自增 id。"""
|
||
session.add(grade)
|
||
session.flush()
|
||
return grade
|
||
|
||
|
||
def delete(session: Session, grade: CustomerRatingGrade) -> None:
|
||
"""物理删除等级。等级表无下游引用,调用方无需引用保护校验。"""
|
||
session.delete(grade)
|
||
session.flush()
|