抽象redis得定制简历
This commit is contained in:
+4
-10
@@ -6,6 +6,7 @@ from app.core.context import RequestContext
|
|||||||
from app.core.database import get_db
|
from app.core.database import get_db
|
||||||
from app.schemas.skill_gap import SkillGapParam, CustomizeResumeParam, AiEditParam, CustomizeResume
|
from app.schemas.skill_gap import SkillGapParam, CustomizeResumeParam, AiEditParam, CustomizeResume
|
||||||
from app.services.skill_gap_service import SkillGapService
|
from app.services.skill_gap_service import SkillGapService
|
||||||
|
from app.services import customize_resume_store
|
||||||
|
|
||||||
router = APIRouter(prefix="/job", tags=["岗位简历"])
|
router = APIRouter(prefix="/job", tags=["岗位简历"])
|
||||||
|
|
||||||
@@ -37,28 +38,21 @@ async def generate_customize_resume(param: CustomizeResumeParam):
|
|||||||
async def get_customize_resume():
|
async def get_customize_resume():
|
||||||
"""查询当前用户的定制简历"""
|
"""查询当前用户的定制简历"""
|
||||||
user_id = RequestContext.user_id.get()
|
user_id = RequestContext.user_id.get()
|
||||||
async for session in get_db():
|
return await customize_resume_store.get(user_id)
|
||||||
service = SkillGapService(session)
|
|
||||||
result = await service.get_customize_resume(user_id)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("/customize-resume", summary="修改定制简历")
|
@router.put("/customize-resume", summary="修改定制简历")
|
||||||
async def update_customize_resume(data: CustomizeResume):
|
async def update_customize_resume(data: CustomizeResume):
|
||||||
"""手动编辑定制简历(整体覆盖)"""
|
"""手动编辑定制简历(整体覆盖)"""
|
||||||
user_id = RequestContext.user_id.get()
|
user_id = RequestContext.user_id.get()
|
||||||
async for session in get_db():
|
await customize_resume_store.save(user_id, data)
|
||||||
service = SkillGapService(session)
|
|
||||||
await service.update_customize_resume(user_id, data.model_dump(by_alias=True))
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/customize-resume/rollback", summary="回滚定制简历")
|
@router.post("/customize-resume/rollback", summary="回滚定制简历")
|
||||||
async def rollback_customize_resume():
|
async def rollback_customize_resume():
|
||||||
"""回滚到上一版本的定制简历"""
|
"""回滚到上一版本的定制简历"""
|
||||||
user_id = RequestContext.user_id.get()
|
user_id = RequestContext.user_id.get()
|
||||||
async for session in get_db():
|
await customize_resume_store.rollback(user_id)
|
||||||
service = SkillGapService(session)
|
|
||||||
await service.rollback_customize_resume(user_id)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/customize-resume/ai-edit", summary="AI对话编辑定制简历")
|
@router.post("/customize-resume/ai-edit", summary="AI对话编辑定制简历")
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
"""定制简历 Redis 存取模块
|
||||||
|
|
||||||
|
提供定制简历的保存(自动回滚备份)、查询、回滚能力。
|
||||||
|
各 Service 统一复用,不直接操作 Redis key。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from app.core.redis import RedisManager
|
||||||
|
from app.schemas.skill_gap import CustomizeResume
|
||||||
|
|
||||||
|
# Redis 常量
|
||||||
|
KEY_PREFIX = "customize:resume:"
|
||||||
|
EXPIRE = 12 * 60 * 60 # 12小时
|
||||||
|
ROLLBACK_KEY_PREFIX = "customize:resume:rollback:"
|
||||||
|
ROLLBACK_EXPIRE = 30 * 60 # 30分钟
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -20,19 +20,13 @@ from app.ai.skill_gap_analyzer.analyzer import (
|
|||||||
)
|
)
|
||||||
from app.ai.skill_gap_analyzer.prompts import MODULE_SCHEMAS
|
from app.ai.skill_gap_analyzer.prompts import MODULE_SCHEMAS
|
||||||
from app.core.logger import log
|
from app.core.logger import log
|
||||||
from app.core.redis import RedisManager
|
|
||||||
from app.schemas.skill_gap import (
|
from app.schemas.skill_gap import (
|
||||||
CustomizeResume, ResumeProfile, Education, Work, Internship, Project, Competition, Paragraph,
|
CustomizeResume, ResumeProfile, Education, Work, Internship, Project, Competition, Paragraph,
|
||||||
)
|
)
|
||||||
from app.models.job import Job
|
from app.models.job import Job
|
||||||
from app.models.user_resume import UserResume
|
from app.models.user_resume import UserResume
|
||||||
from app.services.resume_loader import ResumeDetail, load_resume_detail, load_default_resume_detail
|
from app.services.resume_loader import ResumeDetail, load_resume_detail, load_default_resume_detail
|
||||||
|
from app.services import customize_resume_store
|
||||||
# Redis 常量
|
|
||||||
CUSTOMIZE_RESUME_KEY_PREFIX = "customize:resume:"
|
|
||||||
CUSTOMIZE_RESUME_EXPIRE = 12 * 60 * 60 # 12小时
|
|
||||||
CUSTOMIZE_RESUME_ROLLBACK_KEY_PREFIX = "customize:resume:rollback:"
|
|
||||||
CUSTOMIZE_RESUME_ROLLBACK_EXPIRE = 30 * 60 # 30分钟
|
|
||||||
|
|
||||||
_CHARS = string.ascii_letters + string.digits
|
_CHARS = string.ascii_letters + string.digits
|
||||||
|
|
||||||
@@ -149,7 +143,7 @@ class SkillGapService:
|
|||||||
existing = set(cr.resume.skills)
|
existing = set(cr.resume.skills)
|
||||||
cr.resume.skills.extend([s for s in add_skills if s not in existing])
|
cr.resume.skills.extend([s for s in add_skills if s not in existing])
|
||||||
# 5. 存 Redis
|
# 5. 存 Redis
|
||||||
await self._save_customize_resume(user_id, cr)
|
await customize_resume_store.save(user_id, cr)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _experience_tasks(cr: CustomizeResume, job_title: str, job_desc: str) -> list[tuple[str, str]]:
|
def _experience_tasks(cr: CustomizeResume, job_title: str, job_desc: str) -> list[tuple[str, str]]:
|
||||||
@@ -177,42 +171,16 @@ class SkillGapService:
|
|||||||
elif key == "competition" and isinstance(result, list):
|
elif key == "competition" and isinstance(result, list):
|
||||||
cr.competition = [Competition.model_validate(item) for item in result]
|
cr.competition = [Competition.model_validate(item) for item in result]
|
||||||
|
|
||||||
# ===== 查询 / 修改 / 回滚 =====
|
|
||||||
|
|
||||||
async def get_customize_resume(self, user_id: int) -> dict | None:
|
|
||||||
"""查询定制简历"""
|
|
||||||
key = f"{CUSTOMIZE_RESUME_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 update_customize_resume(self, user_id: int, data: dict) -> None:
|
|
||||||
"""手动编辑定制简历(整体覆盖)"""
|
|
||||||
cr = CustomizeResume.model_validate(data)
|
|
||||||
await self._save_customize_resume(user_id, cr)
|
|
||||||
|
|
||||||
async def rollback_customize_resume(self, user_id: int) -> None:
|
|
||||||
"""回滚定制简历"""
|
|
||||||
rollback_key = f"{CUSTOMIZE_RESUME_ROLLBACK_KEY_PREFIX}{user_id}"
|
|
||||||
data = await RedisManager.client.get(rollback_key)
|
|
||||||
if not data:
|
|
||||||
raise ValueError("没有可回滚的版本")
|
|
||||||
key = f"{CUSTOMIZE_RESUME_KEY_PREFIX}{user_id}"
|
|
||||||
await RedisManager.client.set(key, data, ex=CUSTOMIZE_RESUME_EXPIRE)
|
|
||||||
await RedisManager.client.delete(rollback_key)
|
|
||||||
|
|
||||||
# ===== AI 对话编辑 =====
|
# ===== AI 对话编辑 =====
|
||||||
|
|
||||||
async def ai_edit_customize_resume(self, user_id: int, job_id: int,
|
async def ai_edit_customize_resume(self, user_id: int, job_id: int,
|
||||||
instruction: str, chat_history: list) -> dict:
|
instruction: str, chat_history: list) -> dict:
|
||||||
"""AI 对话式编辑定制简历(原子化操作版)"""
|
"""AI 对话式编辑定制简历(原子化操作版)"""
|
||||||
# 1. 取当前定制简历
|
# 1. 取当前定制简历
|
||||||
key = f"{CUSTOMIZE_RESUME_KEY_PREFIX}{user_id}"
|
cr_data = await customize_resume_store.get(user_id)
|
||||||
raw = await RedisManager.client.get(key)
|
if not cr_data:
|
||||||
if not raw:
|
|
||||||
raise ValueError("定制简历不存在,请先生成")
|
raise ValueError("定制简历不存在,请先生成")
|
||||||
cr = CustomizeResume.model_validate_json(raw)
|
cr = CustomizeResume.model_validate(cr_data)
|
||||||
resume_json = cr.model_dump_json(by_alias=True)
|
resume_json = cr.model_dump_json(by_alias=True)
|
||||||
# 2. 查岗位
|
# 2. 查岗位
|
||||||
job = await self._get_job(job_id)
|
job = await self._get_job(job_id)
|
||||||
@@ -275,10 +243,8 @@ class SkillGapService:
|
|||||||
self._apply_record_update(cr, mod_name, record_id, result)
|
self._apply_record_update(cr, mod_name, record_id, result)
|
||||||
elif op_type == "add":
|
elif op_type == "add":
|
||||||
self._apply_record_add(cr, mod_name, result)
|
self._apply_record_add(cr, mod_name, result)
|
||||||
# 6. 保存回滚 + 新版本
|
# 6. 保存(自动备份回滚)
|
||||||
rollback_key = f"{CUSTOMIZE_RESUME_ROLLBACK_KEY_PREFIX}{user_id}"
|
await customize_resume_store.save(user_id, cr)
|
||||||
await RedisManager.client.set(rollback_key, raw, ex=CUSTOMIZE_RESUME_ROLLBACK_EXPIRE)
|
|
||||||
await self._save_customize_resume(user_id, cr)
|
|
||||||
# 拼接更新模块标签
|
# 拼接更新模块标签
|
||||||
updated_modules = list(dict.fromkeys(op.get("module", "") for op in operations))
|
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)
|
label = "、".join(_MODULE_LABELS.get(m, m) for m in updated_modules if m)
|
||||||
@@ -404,8 +370,3 @@ class SkillGapService:
|
|||||||
description=_build_paragraphs(r.description)) for r in detail.competition],
|
description=_build_paragraphs(r.description)) for r in detail.competition],
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def _save_customize_resume(user_id: int, cr: CustomizeResume) -> None:
|
|
||||||
"""存定制简历到 Redis"""
|
|
||||||
key = f"{CUSTOMIZE_RESUME_KEY_PREFIX}{user_id}"
|
|
||||||
await RedisManager.client.set(key, cr.model_dump_json(by_alias=True), ex=CUSTOMIZE_RESUME_EXPIRE)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user