Files
PayrollManagementSystem/backend/app/repositories/rating_repository.py
T
liuwenxinandClaude Code 111ac5425a feat(customer): 客户列表展示等级徽标并统一等级不落库口径
评级计算器并入客户管理后,列表只有分数看不出档位,本批补上等级徽标:
- 抽出 utils/rating.ts 的 resolveGrade(按 sort_order 升序取首个
  min_score ≤ 总分的等级,单边判定避免区间边界歧义),列表页与弹窗共用
- 等级映射按需拉取并缓存在内存,等级配置改动后回调父页重拉,避免
  「同一分数在弹窗是 B、在列表是 C」的两处口径分歧
- 徽标色调按等级名首字母映射,自定义等级名落回中性灰(等级名可配置,
  不能靠写死枚举判语义色)
- 悬浮提示给出等级区间与评级时间

同时订正三处与实现不符的注释:rate_customer 实际只写 rating_score 与
rating_time,等级由分数按当前映射实时推导,三处却写成「后端判定并固化等级
名快照」,v13 迁移 docstring 还引用了并不存在的 v14(d5b8f2a46c71)。
纯注释改动,无逻辑变更。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-23 16:18:47 +08:00

71 lines
2.6 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:
"""按主键查询。
等级表无外键引用(客户档案只存总分,等级由分数按当前映射实时推导,
不存等级 id),无需 ``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:
"""物理删除等级。
等级表无外键引用(客户档案只存总分,不存等级 id),调用方无需引用保护
校验:删掉等级只是「以后不再判到它」,已存的总分不受影响。
"""
session.delete(grade)
session.flush()