diff --git a/api/app/routers/__init__.py b/api/app/routers/__init__.py index 4d54495..e78c5f0 100644 --- a/api/app/routers/__init__.py +++ b/api/app/routers/__init__.py @@ -2,5 +2,9 @@ from .jokes import router as jokes_router from .categories import router as categories_router from .auth import router as auth_router from .admin import router as admin_router +from .settings import router as settings_router +from .links import router as links_router +from .feedback import router as feedback_router +from .generate import router as generate_router -__all__ = ["jokes_router", "categories_router", "auth_router", "admin_router"] \ No newline at end of file +__all__ = ["jokes_router", "categories_router", "auth_router", "admin_router", "settings_router", "links_router", "feedback_router", "generate_router"] diff --git a/api/app/routers/generate.py b/api/app/routers/generate.py new file mode 100644 index 0000000..df370b9 --- /dev/null +++ b/api/app/routers/generate.py @@ -0,0 +1,103 @@ +"""智能笑话生成器 API""" + +from datetime import datetime +from fastapi import APIRouter, Depends, HTTPException +from openai import OpenAI +from sqlalchemy.orm import Session + +from app.database import get_db +from app.models.setting import AiSetting +from app.schemas.joke import GenerateRequest, GenerateResponse + + +router = APIRouter(prefix="/generate", tags=["生成器"]) + + +# AI Prompt +GENERATION_PROMPT = """你是一位幽默大师,专门创作轻松搞笑的短笑话。 + +{context} + +要求: +1. 根据场景和关键词创作一条原创笑话 +2. 笑话要有反转或意外结局 +3. 语言简洁,30-150字 +4. 直接输出笑话内容,不需要解释 + +格式: +标题:xxx +内容:xxx +""" + + +def _build_prompt(scenarios: list[str], keywords: list[str]) -> str: + """构建 AI prompt""" + parts = [] + if scenarios: + parts.append(f"场景:{', '.join(scenarios)}") + if keywords: + parts.append(f"关键词:{', '.join(keywords)}") + if not parts: + parts.append("场景:日常生活的各种趣事(不指定具体场景)") + return GENERATION_PROMPT.format(context="\n".join(parts)) + + +@router.post("", response_model=GenerateResponse) +def generate_joke( + req: GenerateRequest, + db: Session = Depends(get_db), +): + """调用 AI 生成笑话""" + # 获取激活的 AI 配置 + setting = db.query(AiSetting).filter(AiSetting.is_active == True).first() + if not setting: + # 如果没有配置,尝试返回默认配置 + setting = db.query(AiSetting).first() + + if not setting or not setting.api_key: + raise HTTPException(status_code=503, detail="AI 服务未配置,请联系管理员") + + # 调用 AI + try: + client = OpenAI(base_url=setting.api_base, api_key=setting.api_key) + prompt = _build_prompt(req.scenarios, req.keywords) + + response = client.chat.completions.create( + model=setting.model_name, + messages=[{"role": "user", "content": prompt}], + temperature=setting.temperature, + max_tokens=setting.max_tokens, + ) + + raw = response.choices[0].message.content + return _parse_response(raw) + + except Exception as e: + raise HTTPException(status_code=500, detail=f"生成失败: {str(e)}") + + +def _parse_response(raw: str) -> GenerateResponse: + """解析 AI 返回内容,提取标题和内容""" + title = "" + content = raw + + # 尝试提取 "标题:xxx" 或 "标题:xxx" + for line in raw.split("\n"): + line = line.strip() + if line.startswith("标题:") or line.startswith("标题:"): + title = line.split(":", 1)[-1].split(":", 1)[-1].strip() + content = content.replace(line, "").strip() + break + + # 如果没有提取到标题,取第一行或前20字 + if not title: + first_line = raw.split("\n")[0].strip() + if first_line.startswith("标题"): + first_line = first_line.split(":", 1)[-1].split(":", 1)[-1].strip() + title = first_line[:30] if len(first_line) > 30 else first_line + + return GenerateResponse( + title=title or "生成的笑话", + content=content.strip(), + created_at=datetime.now(), + ) \ No newline at end of file diff --git a/api/main.py b/api/main.py index a9c145f..6c6101d 100644 --- a/api/main.py +++ b/api/main.py @@ -2,7 +2,14 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.config import API_TITLE, API_VERSION -from app.routers import jokes_router, categories_router, auth_router, admin_router +from app.routers import jokes_router, categories_router, auth_router, admin_router, settings_router, links_router, feedback_router, generate_router +from app.database import Base, engine +from app.models.setting import AiSetting +from app.models.link import Link +from app.models.feedback import Feedback # 注册模型,确保 create_all 能看到 + +# 创建新表(如果不存在) +Base.metadata.create_all(bind=engine) # Create FastAPI application app = FastAPI(title=API_TITLE, version=API_VERSION) @@ -17,10 +24,14 @@ app.add_middleware( ) # Mount routers -app.include_router(jokes_router) -app.include_router(categories_router) -app.include_router(auth_router) -app.include_router(admin_router) +app.include_router(jokes_router, prefix="/api") +app.include_router(categories_router, prefix="/api") +app.include_router(auth_router, prefix="/api") +app.include_router(admin_router, prefix="/api") +app.include_router(settings_router, prefix="/api") +app.include_router(links_router, prefix="/api") +app.include_router(feedback_router, prefix="/api") +app.include_router(generate_router, prefix="/api") @app.get("/")