Files
dreamweaver/backend/app/tasks/achievements.py
zhangtuo e9d7f8832a Initial commit: clean project structure
- Backend: FastAPI + SQLAlchemy + Celery (Python 3.11+)
- Frontend: Vue 3 + TypeScript + Pinia + Tailwind
- Admin Frontend: separate Vue 3 app for management
- Docker Compose: 9 services orchestration
- Specs: design prototypes, memory system PRD, product roadmap

Cleanup performed:
- Removed temporary debug scripts from backend root
- Removed deprecated admin_app.py (embedded UI)
- Removed duplicate docs from admin-frontend
- Updated .gitignore for Vite cache and egg-info
2026-01-20 18:20:03 +08:00

83 lines
2.8 KiB
Python

"""Celery tasks for achievements."""
import asyncio
from datetime import datetime
from sqlalchemy import select
from app.core.celery_app import celery_app
from app.core.logging import get_logger
from app.db.database import _get_session_factory
from app.db.models import Story, StoryUniverse
from app.services.achievement_extractor import extract_achievements
logger = get_logger(__name__)
@celery_app.task
def extract_story_achievements(story_id: int, universe_id: str) -> None:
"""Extract achievements and update universe."""
asyncio.run(_extract_story_achievements(story_id, universe_id))
async def _extract_story_achievements(story_id: int, universe_id: str) -> None:
session_factory = _get_session_factory()
async with session_factory() as session:
result = await session.execute(select(Story).where(Story.id == story_id))
story = result.scalar_one_or_none()
if not story:
logger.warning("achievement_task_story_missing", story_id=story_id)
return
result = await session.execute(
select(StoryUniverse).where(StoryUniverse.id == universe_id)
)
universe = result.scalar_one_or_none()
if not universe:
logger.warning("achievement_task_universe_missing", universe_id=universe_id)
return
text_content = story.story_text
if not text_content and story.pages:
# 如果是绘本,拼接每页文本
text_content = "\n".join([str(p.get("text", "")) for p in story.pages])
if not text_content:
logger.warning("achievement_task_empty_content", story_id=story_id)
return
achievements = await extract_achievements(text_content)
if not achievements:
logger.info("achievement_task_no_new", story_id=story_id)
return
existing = {
(str(item.get("type", "")).strip(), str(item.get("description", "")).strip())
for item in (universe.achievements or [])
if isinstance(item, dict)
}
merged = list(universe.achievements or [])
added_count = 0
for item in achievements:
key = (item.get("type", "").strip(), item.get("description", "").strip())
if key in existing:
continue
merged.append({
"type": key[0],
"description": key[1],
"obtained_at": datetime.now().isoformat(),
"source_story_id": story_id,
})
existing.add(key)
added_count += 1
universe.achievements = merged
await session.commit()
logger.info(
"achievement_task_success",
story_id=story_id,
universe_id=universe_id,
added=added_count,
)