修改定制简历保存逻辑
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user