"""Platform APIs: services, videos, weather, AI, stats, content.""" from fastapi import APIRouter, Header, HTTPException, Query from app.schemas import ( AiAssistantRequest, AiAssistantResponse, ContentSubmissionRequest, NewsletterRequest, ReportRequest, ServiceItem, ServiceLeadRequest, VideoItem, VolunteerRequest, ) from app.data import platform_data from app.services import community_store, social_store from app.services.auth import get_user_by_token from app.services import platform_store from app.services.weather_service import get_city_weather, list_weather from app.services.ai_service import assistant_reply from app.services.pocketbase import pb router = APIRouter(tags=["platform"]) def _optional_user(authorization: str | None) -> dict | None: if authorization and authorization.startswith("Bearer "): return get_user_by_token(authorization[7:]) return None def _user(authorization: str | None) -> dict: u = _optional_user(authorization) if not u: raise HTTPException(401, "未登录") return u @router.get("/services", response_model=list[ServiceItem]) async def list_services(): return [ServiceItem(**s) for s in platform_data.SERVICES] @router.post("/services/leads") async def service_lead(body: ServiceLeadRequest, authorization: str | None = Header(None)): user = _optional_user(authorization) item = platform_store.add_service_lead( body.service_id, user["id"] if user else None, body.name, body.email, body.message, ) return {"success": True, "id": item["id"]} @router.get("/videos", response_model=list[VideoItem]) async def list_videos(): return [VideoItem(**v) for v in platform_data.VIDEOS] @router.get("/videos/{slug}", response_model=VideoItem) async def get_video(slug: str): v = next((x for x in platform_data.VIDEOS if x["slug"] == slug), None) if not v: raise HTTPException(404, "视频不存在") return VideoItem(**v) @router.get("/content/home") async def content_home(): return { "featured": platform_data.VIDEOS[:2], "videos_count": len(platform_data.VIDEOS), "services_count": len(platform_data.SERVICES), } @router.post("/content/submissions") async def content_submission(body: ContentSubmissionRequest, authorization: str | None = Header(None)): user = _optional_user(authorization) item = platform_store.add_content_submission( user["id"] if user else None, body.name, body.title, body.content_type, body.url, body.notes, ) return {"success": True, "id": item["id"]} @router.get("/weather/cities") async def weather_cities(): return await list_weather() @router.get("/weather/cities/{slug}") async def weather_city(slug: str): w = await get_city_weather(slug) if not w: raise HTTPException(404, "城市不存在") return w @router.post("/ai/assistant", response_model=AiAssistantResponse) async def ai_assistant(body: AiAssistantRequest): dests = await pb.get_destinations() result = assistant_reply(body.message, dests) return AiAssistantResponse(**result) @router.post("/newsletter/subscribe") async def newsletter_subscribe(body: NewsletterRequest): res = platform_store.subscribe_newsletter(body.email, body.source) return res @router.get("/stats/member-map") async def member_map(): return {"members": platform_data.MEMBER_MAP} @router.get("/stats/city-ranking") async def city_ranking(): return {"items": platform_data.CITY_RANKING} @router.post("/cities/{slug}/volunteer-applications") async def volunteer_apply(slug: str, body: VolunteerRequest, authorization: str | None = Header(None)): user = _optional_user(authorization) item = platform_store.add_volunteer(slug, user["id"] if user else None, body.name, body.email, body.message) return {"success": True, "id": item["id"]} @router.post("/reports") async def submit_report(body: ReportRequest, authorization: str | None = Header(None)): user = _optional_user(authorization) item = platform_store.add_report( user["id"] if user else None, body.target_type, body.target_id, body.reason, ) return {"success": True, "id": item["id"]} @router.get("/digital/day/{day_id}") async def digital_day(day_id: str): tip = platform_data.DAILY_TIPS.get(day_id) if not tip: raise HTTPException(404, "每日指南不存在") return {"id": day_id, **tip} @router.get("/notifications/unread-count") async def unread_count(authorization: str | None = Header(None)): user = _user(authorization) return {"count": community_store.notification_unread_count(user["id"])} @router.get("/notifications/preferences") async def get_notif_prefs(authorization: str | None = Header(None)): user = _user(authorization) return platform_store.get_notif_prefs(user["id"]) @router.put("/notifications/preferences") async def set_notif_prefs(body: dict, authorization: str | None = Header(None)): user = _user(authorization) return platform_store.set_notif_prefs(user["id"], body) @router.get("/discussions/search") async def search_discussions(q: str = Query(..., min_length=1)): return community_store.search_discussions(q) @router.get("/meetups/{meetup_id}/social-suggestions") async def meetup_social(meetup_id: str): return {"items": community_store.meetup_social_suggestions(meetup_id)} @router.delete("/meetups/{meetup_id}/rsvp") async def cancel_rsvp(meetup_id: str, authorization: str | None = Header(None)): user = _user(authorization) res = community_store.cancel_rsvp(meetup_id, user["id"]) if not res.get("ok"): raise HTTPException(400, "未报名此活动") return res