96 lines
4.3 KiB
Python
96 lines
4.3 KiB
Python
"""定制简历 Redis 存取 + 数据转换模块
|
|
|
|
提供定制简历的保存(自动回滚备份)、查询、回滚、从 ResumeDetail 构建能力。
|
|
各 Service 统一复用,不直接操作 Redis key。
|
|
"""
|
|
|
|
import random
|
|
import string
|
|
|
|
from app.core.redis import RedisManager
|
|
from app.schemas.customize_resume import (
|
|
CustomizeResume, ResumeProfile, Education, Work, Internship, Project, Competition, Paragraph,
|
|
)
|
|
from app.services.resume_loader import ResumeDetail
|
|
|
|
# Redis 常量
|
|
KEY_PREFIX = "customize:resume:"
|
|
EXPIRE = 12 * 60 * 60 # 12小时
|
|
ROLLBACK_KEY_PREFIX = "customize:resume:rollback:"
|
|
ROLLBACK_EXPIRE = 30 * 60 # 30分钟
|
|
|
|
_CHARS = string.ascii_letters + string.digits
|
|
|
|
|
|
def _rand_id() -> str:
|
|
"""生成随机8位字符串标识"""
|
|
return "".join(random.choices(_CHARS, k=8))
|
|
|
|
|
|
def _build_paragraphs(description: list[dict] | None) -> list[Paragraph]:
|
|
"""将数据库 description [{id, text}] 转为 Paragraph 列表"""
|
|
if not description:
|
|
return []
|
|
return [Paragraph(id=_rand_id(), text=item.get("text", "")) for item in description if isinstance(item, dict)]
|
|
|
|
|
|
def build_from_detail(detail: ResumeDetail) -> CustomizeResume:
|
|
"""从 ResumeDetail 组装 CustomizeResume"""
|
|
resume = detail.resume
|
|
profile = ResumeProfile(
|
|
avatarUrl=resume.avatar_url or "", name=resume.name or "", email=resume.email or "",
|
|
mobileNumber=resume.mobile_number or "", city=resume.city or "",
|
|
wechatNumber=resume.wechat_number or "", portfolioUrl=resume.portfolio_url or "",
|
|
skills=resume.skills or [], certificates=resume.certificates or [],
|
|
summary=resume.summary or "",
|
|
)
|
|
return CustomizeResume(
|
|
resume=profile,
|
|
education=[Education(id=_rand_id(), school=r.school or "", major=r.major or "",
|
|
degree=r.degree or "", studyType=r.study_type or "",
|
|
startDate=r.start_date or "", endDate=r.end_date or "",
|
|
description=_build_paragraphs(r.description)) for r in detail.education],
|
|
work=[Work(id=_rand_id(), companyName=r.company_name or "", position=r.position or "",
|
|
startDate=r.start_date or "", endDate=r.end_date or "",
|
|
description=_build_paragraphs(r.description)) for r in detail.work],
|
|
internship=[Internship(id=_rand_id(), companyName=r.company_name or "", position=r.position or "",
|
|
startDate=r.start_date or "", endDate=r.end_date or "",
|
|
description=_build_paragraphs(r.description)) for r in detail.internship],
|
|
project=[Project(id=_rand_id(), companyName=r.company_name or "", projectName=r.project_name or "",
|
|
role=r.role or "", startDate=r.start_date or "", endDate=r.end_date or "",
|
|
description=_build_paragraphs(r.description)) for r in detail.project],
|
|
competition=[Competition(id=_rand_id(), competitionName=r.competition_name or "", award=r.award or "",
|
|
awardDate=r.award_date or "",
|
|
description=_build_paragraphs(r.description)) for r in detail.competition],
|
|
)
|
|
|
|
|
|
async def save(user_id: int, cr: CustomizeResume) -> None:
|
|
"""保存定制简历,自动备份旧版本到回滚 key"""
|
|
key = f"{KEY_PREFIX}{user_id}"
|
|
rollback_key = f"{ROLLBACK_KEY_PREFIX}{user_id}"
|
|
old_data = await RedisManager.client.get(key)
|
|
if old_data:
|
|
await RedisManager.client.set(rollback_key, old_data, ex=ROLLBACK_EXPIRE)
|
|
await RedisManager.client.set(key, cr.model_dump_json(by_alias=True), ex=EXPIRE)
|
|
|
|
|
|
async def get(user_id: int) -> dict | None:
|
|
"""查询定制简历,返回 dict 或 None"""
|
|
key = f"{KEY_PREFIX}{user_id}"
|
|
data = await RedisManager.client.get(key)
|
|
if data:
|
|
return CustomizeResume.model_validate_json(data).model_dump(by_alias=True)
|
|
return None
|
|
|
|
|
|
async def rollback(user_id: int) -> None:
|
|
"""回滚定制简历到上一版本"""
|
|
rollback_key = f"{ROLLBACK_KEY_PREFIX}{user_id}"
|
|
data = await RedisManager.client.get(rollback_key)
|
|
if not data:
|
|
raise ValueError("没有可回滚的版本")
|
|
key = f"{KEY_PREFIX}{user_id}"
|
|
await RedisManager.client.set(key, data, ex=EXPIRE)
|
|
await RedisManager.client.delete(rollback_key)
|