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
This commit is contained in:
82
backend/app/tasks/achievements.py
Normal file
82
backend/app/tasks/achievements.py
Normal file
@@ -0,0 +1,82 @@
|
||||
"""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,
|
||||
)
|
||||
Reference in New Issue
Block a user