| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- """
- 游戏化服务 - XP、等级、徽章、连击系统
- """
- from datetime import date, datetime
- from typing import List, Optional, Dict, Tuple
- from ..models.learning import GamificationProfile
- from .data_store import data_store
- # ========== 配置 ==========
- XP_PER_LEVEL = 100
- # 徽章定义
- BADGE_DEFINITIONS: Dict[str, dict] = {
- "first_lesson": {
- "id": "first_lesson",
- "name": "第一步",
- "description": "完成第一节课",
- "icon": "🌱",
- "condition": "完成1节课",
- },
- "ten_lessons": {
- "id": "ten_lessons",
- "name": "勤学苦练",
- "description": "累计完成10节课",
- "icon": "📚",
- "condition": "完成10节课",
- },
- "twenty_lessons": {
- "id": "twenty_lessons",
- "name": "学富五车",
- "description": "累计完成20节课",
- "icon": "🧠",
- "condition": "完成20节课",
- },
- "first_assessment": {
- "id": "first_assessment",
- "name": "自我认知",
- "description": "完成第一次水平检测",
- "icon": "📊",
- "condition": "完成1次测试",
- },
- "perfect_score": {
- "id": "perfect_score",
- "name": "完美主义者",
- "description": "水平检测获得满分",
- "icon": "💯",
- "condition": "测试得分100",
- },
- "speed_demon": {
- "id": "speed_demon",
- "name": "神速",
- "description": "同一天完成5节课",
- "icon": "⚡",
- "condition": "单日5节课",
- },
- "first_path": {
- "id": "first_path",
- "name": "选择方向",
- "description": "选择一条学习路径",
- "icon": "🛤️",
- "condition": "选择路径",
- },
- "all_modules": {
- "id": "all_modules",
- "name": "开拓者",
- "description": "完成一个路径的所有模块",
- "icon": "🏆",
- "condition": "完成全部模块",
- },
- "week_streak": {
- "id": "week_streak",
- "name": "坚持不懈",
- "description": "连续学习7天",
- "icon": "🔥",
- "condition": "连续7天",
- },
- "month_streak": {
- "id": "month_streak",
- "name": "铁杆学员",
- "description": "连续学习30天",
- "icon": "💎",
- "condition": "连续30天",
- },
- }
- class GamificationService:
- """游戏化服务"""
- def get_profile(self, user_id: str) -> GamificationProfile:
- """获取用户游戏化档案"""
- profile = data_store.get_gamification(user_id)
- if not profile:
- profile = GamificationProfile(user_id=user_id)
- data_store.save_gamification(profile)
- return profile
- def award_xp(self, user_id: str, amount: int, reason: str) -> GamificationProfile:
- """给用户增加XP"""
- profile = self.get_profile(user_id)
- profile.total_xp += amount
- profile.level = max(1, profile.total_xp // XP_PER_LEVEL + 1)
- if len(profile.xp_log) > 500:
- profile.xp_log = profile.xp_log[-500:]
- profile.xp_log.append({
- "amount": amount,
- "reason": reason,
- "timestamp": datetime.now().isoformat(),
- })
- # 更新连击
- today = date.today().isoformat()
- if profile.last_active_date == today:
- pass # 今天已经活跃过
- elif profile.last_active_date == _yesterday():
- profile.streak += 1
- else:
- profile.streak = 1
- profile.last_active_date = today
- # 检查新徽章
- new_badges = self._check_new_badges(profile, user_id)
- data_store.save_gamification(profile)
- return profile, new_badges
- def _check_new_badges(self, profile: GamificationProfile, user_id: str) -> List[dict]:
- """检查是否有新徽章获得"""
- progress = data_store.get_user_progress(user_id)
- if not progress:
- return []
- earned = set(profile.badges)
- new_badges = []
- # 按条件检查
- checks = [
- ("first_lesson", lambda: len(progress.completed_lessons) >= 1),
- ("ten_lessons", lambda: len(progress.completed_lessons) >= 10),
- ("twenty_lessons", lambda: len(progress.completed_lessons) >= 20),
- ("first_assessment", lambda: len(data_store.get_user_assessments(user_id)) >= 1),
- ("first_path", lambda: progress.current_path is not None),
- ("all_modules", lambda: progress.current_path is not None and
- _all_modules_completed(progress)),
- ("week_streak", lambda: profile.streak >= 7),
- ("month_streak", lambda: profile.streak >= 30),
- ("speed_demon", lambda: False), # 由外部触发
- ("perfect_score", lambda: False), # 由外部触发
- ]
- for badge_id, check_fn in checks:
- if badge_id not in earned and check_fn():
- badge = dict(BADGE_DEFINITIONS[badge_id])
- badge["awarded_at"] = datetime.now().isoformat()
- profile.badges.append(badge_id)
- new_badges.append(badge)
- return new_badges
- def check_perfect_score(self, user_id: str):
- """检查是否获得完美得分徽章"""
- profile = self.get_profile(user_id)
- if "perfect_score" in profile.badges:
- return
- assessments = data_store.get_user_assessments(user_id)
- if any(a.score >= 100 for a in assessments):
- profile.badges.append("perfect_score")
- data_store.save_gamification(profile)
- def check_speed_demon(self, user_id: str):
- """检查单日5课成就"""
- profile = self.get_profile(user_id)
- if "speed_demon" in profile.badges:
- return
- progress = data_store.get_user_progress(user_id)
- if not progress:
- return
- # 完成5节课即授予
- if len(progress.completed_lessons) >= 5:
- profile.badges.append("speed_demon")
- data_store.save_gamification(profile)
- def _yesterday() -> str:
- from datetime import timedelta
- return (date.today() - timedelta(days=1)).isoformat()
- def _all_modules_completed(progress) -> bool:
- """检查一个路径的所有模块是否完成"""
- if not progress.current_path:
- return False
- from .learning_content import get_learning_path
- path_data = get_learning_path(progress.current_path)
- if not path_data:
- return False
- return all(m.id in progress.completed_modules for m in path_data.modules)
- # 全局单例
- _gamification_service: Optional[GamificationService] = None
- def get_gamification_service() -> GamificationService:
- global _gamification_service
- if _gamification_service is None:
- _gamification_service = GamificationService()
- return _gamification_service
|