"""定制简历 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)