58 lines
1.8 KiB
Python
58 lines
1.8 KiB
Python
import re
|
||
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.models import Learner, LearnerNameHistory
|
||
|
||
|
||
class LearnerIdentityConflict(ValueError):
|
||
pass
|
||
|
||
|
||
def normalize_name(value: str) -> str:
|
||
return " ".join(value.strip().split())
|
||
|
||
|
||
def normalize_phone(value: str) -> str:
|
||
phone = re.sub(r"[\s-]", "", value.strip())
|
||
if phone.startswith("+86"):
|
||
phone = phone[3:]
|
||
elif phone.startswith("0086"):
|
||
phone = phone[4:]
|
||
if not re.fullmatch(r"1\d{10}", phone):
|
||
raise ValueError("手机号格式错误,请填写11位中国大陆手机号")
|
||
return phone
|
||
|
||
|
||
def resolve_learner(
|
||
db: Session,
|
||
name: str,
|
||
phone: str,
|
||
*,
|
||
source: str,
|
||
create_if_missing: bool = True,
|
||
) -> tuple[Learner | None, bool]:
|
||
normalized_name = normalize_name(name)
|
||
normalized_phone = normalize_phone(phone)
|
||
if not normalized_name:
|
||
raise ValueError("姓名不能为空")
|
||
|
||
learner = db.query(Learner).filter(Learner.phone == normalized_phone, Learner.status != "deleted").first()
|
||
if learner:
|
||
if normalize_name(learner.current_name) != normalized_name:
|
||
raise LearnerIdentityConflict(
|
||
f"手机号 {normalized_phone} 已属于学员“{learner.current_name}”,与本次姓名“{normalized_name}”不一致"
|
||
)
|
||
if learner.status != "active":
|
||
raise LearnerIdentityConflict(f"学员“{normalized_name}”当前已停用,请先在学员管理中启用")
|
||
return learner, False
|
||
|
||
if not create_if_missing:
|
||
return None, False
|
||
|
||
learner = Learner(phone=normalized_phone, current_name=normalized_name)
|
||
db.add(learner)
|
||
db.flush()
|
||
db.add(LearnerNameHistory(learner_id=learner.id, name=normalized_name, source=source))
|
||
return learner, True
|