重构学员证书签发与批量导入流程

This commit is contained in:
Certificate System
2026-08-14 11:37:09 +08:00
parent 522f529c6a
commit 5e33657268
16 changed files with 1047 additions and 370 deletions

View File

@@ -0,0 +1,57 @@
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