import hashlib
|
import string
|
|
from sqlalchemy import select
|
from sqlalchemy.orm import Session
|
|
from app.models import ExaminationExamExaminee
|
|
_ALPHABET = string.digits + string.ascii_uppercase # 0-9A-Z, 36 chars
|
|
|
def _to_base36(num: int, length: int) -> str:
|
"""将非负整数编码为定长 base36 字符串。"""
|
chars: list[str] = []
|
for _ in range(length):
|
num, rem = divmod(num, 36)
|
chars.append(_ALPHABET[rem])
|
return "".join(reversed(chars))
|
|
|
def generate_student_no(examinee_id: int, exam_id: int) -> str:
|
"""根据考生ID + 考试ID 生成 10 位字母数字编码。"""
|
raw = f"{examinee_id}:{exam_id}".encode()
|
digest = hashlib.sha256(raw).digest()
|
num = int.from_bytes(digest[:8], "big")
|
return _to_base36(num, 10)
|
|
|
def ensure_student_no(db: Session, attempt: ExaminationExamExaminee) -> str:
|
"""获取或生成 attempt 的 student_no(幂等,已有则直接返回)。"""
|
if attempt.student_no:
|
return attempt.student_no
|
code = generate_student_no(attempt.examinee_id, attempt.exam_id)
|
# 极小概率碰撞时追加区分
|
existing = db.scalar(
|
select(ExaminationExamExaminee.id).where(
|
ExaminationExamExaminee.student_no == code,
|
ExaminationExamExaminee.id != attempt.id,
|
)
|
)
|
if existing is not None:
|
digest = hashlib.sha256(
|
f"{attempt.examinee_id}:{attempt.exam_id}:{attempt.id}".encode()
|
).digest()
|
code = _to_base36(int.from_bytes(digest[:8], "big"), 10)
|
attempt.student_no = code
|
db.flush()
|
return code
|
|
|
def find_attempt_by_student_no(
|
db: Session, student_no: str
|
) -> ExaminationExamExaminee | None:
|
"""通过编码反查答卷记录。"""
|
return db.scalar(
|
select(ExaminationExamExaminee).where(
|
ExaminationExamExaminee.student_no == student_no.strip().upper()
|
)
|
)
|