Files
offerpai_python_ai/app/services/customize_resume_store.py
T
2026-04-24 20:13:17 +08:00

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)