修改定制简历保存逻辑

This commit is contained in:
zk
2026-04-28 11:16:50 +08:00
parent dbbc97a836
commit 9daeea9fc5
6 changed files with 102 additions and 48 deletions
+59 -30
View File
@@ -1,22 +1,25 @@
"""定制简历 Redis 存取 + 数据转换模块
"""定制简历存取 + 数据转换模块
提供定制简历的保存(自动回滚备份)、查询、回滚、从 ResumeDetail 构建能力。
各 Service 统一复用,不直接操作 Redis key。
提供定制简历的保存(数据库持久化)、查询、回滚Redis临时备份)、从 ResumeDetail 构建能力。
各 Service 统一复用,不直接操作数据库表和 Redis key。
"""
import random
import string
from sqlalchemy import select, update
from app.core.database import async_session_factory
from app.core.redis import RedisManager
from app.models.user_job_customize_resume import UserJobCustomizeResume
from app.schemas.customize_resume import (
CustomizeResume, ResumeProfile, Education, Work, Internship, Project, Competition, Paragraph,
)
from app.services.resume_loader import ResumeDetail
from app.services.resume_loader import ResumeDetail, load_default_resume_detail
from app.tool.snowflake import next_id
# Redis 常量
KEY_PREFIX = "customize:resume:"
EXPIRE = 12 * 60 * 60 # 12小时
ROLLBACK_KEY_PREFIX = "customize:resume:rollback:"
# Redis 回滚常量
ROLLBACK_PREFIX = "customize:resume:rollback:"
ROLLBACK_EXPIRE = 30 * 60 # 30分钟
_CHARS = string.ascii_letters + string.digits
@@ -65,31 +68,57 @@ def build_from_detail(detail: ResumeDetail) -> CustomizeResume:
)
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)
def _rollback_key(user_id: int, job_id: int) -> str:
return f"{ROLLBACK_PREFIX}{user_id}:{job_id}"
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 save(user_id: int, job_id: int, cr: CustomizeResume) -> None:
"""保存定制简历:备份旧版本到 Redis 用于回滚 + 写数据库"""
content = cr.model_dump(by_alias=True)
async with async_session_factory() as session:
# 查已有记录,备份旧版本到 Redis 用于回滚
result = await session.execute(select(UserJobCustomizeResume).where(UserJobCustomizeResume.user_id == user_id, UserJobCustomizeResume.job_id == job_id))
record = result.scalar_one_or_none()
if record:
old_cr = CustomizeResume.model_validate(record.content)
await RedisManager.client.set(_rollback_key(user_id, job_id), old_cr.model_dump_json(by_alias=True), ex=ROLLBACK_EXPIRE)
await session.execute(update(UserJobCustomizeResume).where(
UserJobCustomizeResume.id == record.id).values(content=content))
else:
session.add(UserJobCustomizeResume(id=next_id(), user_id=user_id, job_id=job_id, content=content))
await session.commit()
async def rollback(user_id: int) -> None:
"""回滚定制简历到上一版本"""
rollback_key = f"{ROLLBACK_KEY_PREFIX}{user_id}"
data = await RedisManager.client.get(rollback_key)
async def get(user_id: int, job_id: int) -> dict | None:
"""查询定制简历,查不到则加载默认简历构建返回"""
async with async_session_factory() as session:
result = await session.execute(select(UserJobCustomizeResume).where(
UserJobCustomizeResume.user_id == user_id, UserJobCustomizeResume.job_id == job_id))
record = result.scalar_one_or_none()
if record:
return CustomizeResume.model_validate(record.content).model_dump(by_alias=True)
# 没有定制简历,加载默认简历构建
detail = await load_default_resume_detail(session, user_id)
return build_from_detail(detail).model_dump(by_alias=True)
async def rollback(user_id: int, job_id: int) -> None:
"""回滚定制简历到上一版本:从 Redis 取回滚数据写入数据库"""
rollback_k = _rollback_key(user_id, job_id)
data = await RedisManager.client.get(rollback_k)
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)
content = CustomizeResume.model_validate_json(data).model_dump(by_alias=True)
async with async_session_factory() as session:
result = await session.execute(select(UserJobCustomizeResume).where(
UserJobCustomizeResume.user_id == user_id, UserJobCustomizeResume.job_id == job_id))
record = result.scalar_one_or_none()
if record:
await session.execute(update(UserJobCustomizeResume).where(
UserJobCustomizeResume.id == record.id).values(content=content))
else:
session.add(UserJobCustomizeResume(id=next_id(), user_id=user_id, job_id=job_id, content=content))
await session.commit()
await RedisManager.client.delete(rollback_k)
+2 -2
View File
@@ -96,8 +96,8 @@ class JobAgentChatService:
log.warning(f"岗位简历优化[{key}]失败: {result}")
continue
self._apply_optimize_result(cr, key, result)
# 4. 存 Redis
await customize_resume_store.save(user_id, cr)
# 4. 存数据库
await customize_resume_store.save(user_id, job_id, cr)
# 5. 返回
return cr.model_dump(by_alias=True)
+4 -4
View File
@@ -126,8 +126,8 @@ class SkillGapService:
if "skills" in optimize_modules and add_skills:
existing = set(cr.resume.skills)
cr.resume.skills.extend([s for s in add_skills if s not in existing])
# 5. 存 Redis
await customize_resume_store.save(user_id, cr)
# 5. 存数据库
await customize_resume_store.save(user_id, job_id, cr)
@staticmethod
def _experience_tasks(cr: CustomizeResume, job_title: str, job_desc: str) -> list[tuple[str, str]]:
@@ -161,7 +161,7 @@ class SkillGapService:
instruction: str, chat_history: list) -> dict:
"""AI 对话式编辑定制简历(原子化操作版)"""
# 1. 取当前定制简历
cr_data = await customize_resume_store.get(user_id)
cr_data = await customize_resume_store.get(user_id, job_id)
if not cr_data:
raise ValueError("定制简历不存在,请先生成")
cr = CustomizeResume.model_validate(cr_data)
@@ -228,7 +228,7 @@ class SkillGapService:
elif op_type == "add":
self._apply_record_add(cr, mod_name, result)
# 6. 保存(自动备份回滚)
await customize_resume_store.save(user_id, cr)
await customize_resume_store.save(user_id, job_id, cr)
# 拼接更新模块标签
updated_modules = list(dict.fromkeys(op.get("module", "") for op in operations))
label = "".join(_MODULE_LABELS.get(m, m) for m in updated_modules if m)