# 智能笑话生成器实现计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 实现用户通过选择场景和输入关键词,调用 AI 生成笑话的功能,支持收藏到本地。 **Architecture:** 后端新增 `/api/generate` 接口调用 AI 服务;前端新增 `/generate` 页面,复用现有笑话卡片样式,支持 localStorage 收藏。 **Tech Stack:** FastAPI + OpenAI SDK (NVIDIA NIM) / Vue3 + Element Plus + localStorage --- ## 文件变更概览 ### 新增文件 - `api/app/routers/generate.py` - 生成器 API 路由 - `api/app/schemas/joke.py` - 添加 GenerateRequest/GenerateResponse schema - `web/src/views/generate/index.vue` - 生成器页面 - `web/src/api/generate.js` - 前端 API 模块 ### 修改文件 - `api/main.py` - 注册新路由 - `web/src/router/index.js` - 添加 /generate 路由 - `web/src/components/AppHeader.vue` - 导航栏添加 AI 生成入口按钮 --- ## Task 1: 后端 Schema 定义 **Files:** - Modify: `api/app/schemas/joke.py:1-45` - [ ] **Step 1: 添加 GenerateRequest 和 GenerateResponse schema** 在 `api/app/schemas/joke.py` 文件末尾添加: ```python class GenerateRequest(BaseModel): """笑话生成请求""" keywords: list[str] = [] scenarios: list[str] = [] class Config: json_schema_extra = { "example": { "keywords": ["加班"], "scenarios": ["职场"] } } class GenerateResponse(BaseModel): """笑话生成响应""" title: str content: str created_at: datetime | None = None class Config: from_attributes = True ``` - [ ] **Step 2: 验证文件语法** Run: `cd api && python -c "from app.schemas.joke import GenerateRequest, GenerateResponse; print('OK')"` Expected: `OK` - [ ] **Step 3: 提交** ```bash git add api/app/schemas/joke.py git commit -m "feat(api): add GenerateRequest/GenerateResponse schemas" ``` --- ## Task 2: 后端 AI 生成路由 **Files:** - Create: `api/app/routers/generate.py` - Modify: `api/main.py:26-33` - [ ] **Step 1: 创建生成器路由文件** 创建 `api/app/routers/generate.py`: ```python """智能笑话生成器 API""" from datetime import datetime from fastapi import APIRouter, 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(), ) ``` - [ ] **Step 2: 修改 main.py 注册路由** 在 `api/main.py` 第 5 行添加导入: ```python from app.routers import jokes_router, categories_router, auth_router, admin_router, settings_router, links_router, feedback_router, generate_router ``` 在 `api/main.py` 第 32 行后添加: ```python app.include_router(generate_router, prefix="/api") ``` - [ ] **Step 3: 手动验证 API** Run: `cd api && python -m uvicorn main:app --reload --port 8001` Expected: 服务启动无错误 访问 `http://localhost:8001/docs` 验证 `/api/generate` 接口存在 - [ ] **Step 4: 测试无 AI 配置情况** Run: `curl -X POST http://localhost:8001/api/generate -H "Content-Type: application/json" -d "{}"` Expected: `{"detail":"AI 服务未配置,请联系管理员"}` - [ ] **Step 5: 提交** ```bash git add api/app/routers/generate.py api/main.py git commit -m "feat(api): add joke generate endpoint with AI" ``` --- ## Task 3: 前端 API 模块 **Files:** - Create: `web/src/api/generate.js` - [ ] **Step 1: 创建前端 API 模块** 创建 `web/src/api/generate.js`: ```javascript import request from './request' /** * 调用 AI 生成笑话 * @param {string[]} keywords - 关键词列表 * @param {string[]} scenarios - 场景列表 * @returns {Promise<{title: string, content: string, created_at: string}>} */ export const generateJoke = (keywords = [], scenarios = []) => { return request.post('/generate', { keywords, scenarios }) } ``` - [ ] **Step 2: 验证语法** Run: `cd web && node -c src/api/generate.js` Expected: 无语法错误(注意:ES module 语法可能有警告,可跳过) - [ ] **Step 3: 提交** ```bash git add web/src/api/generate.js git commit -m "feat(web): add generateJoke API function" ``` --- ## Task 4: 前端路由配置 **Files:** - Modify: `web/src/router/index.js:1-17` - [ ] **Step 1: 添加 /generate 路由** 在 `web/src/router/index.js` 第 7 行后添加: ```javascript { path: '/generate', name: 'Generate', component: () => import('@/views/generate/index.vue') }, ``` - [ ] **Step 2: 提交** ```bash git add web/src/router/index.js git commit -m "feat(web): add /generate route" ``` --- ## Task 5: 头部导航 AI 入口按钮 **Files:** - Modify: `web/src/components/AppHeader.vue` - [ ] **Step 1: 在导航栏添加 AI 生成按钮** 在 `web/src/components/AppHeader.vue` 的 `header-nav` 区域,找到 `random-btn` 后添加: ```vue ✨ ``` 并在样式区域添加(在 `.random-btn:hover` 后): ```css .generate-btn { font-size: 16px; padding: 8px 12px; } .generate-btn:hover { background: rgba(147, 51, 234, 0.1); } .generate-btn.active { background: rgba(147, 51, 234, 0.15); color: #9333ea; } ``` - [ ] **Step 2: 提交** ```bash git add web/src/components/AppHeader.vue git commit -m "feat(web): add AI generate button to header nav" ``` --- ## Task 6: 生成器页面 **Files:** - Create: `web/src/views/generate/index.vue` - [ ] **Step 1: 创建生成器页面** 创建 `web/src/views/generate/index.vue`: ```vue ✨ 智能笑话生成器 选择场景(可多选) {{ s }} 添加关键词(可选) 添加 {{ kw }} 🎲 开始生成 生成中... 清空 {{ generatedJoke.title }} {{ generatedJoke.content }} 🔄 重新生成 ❤ 收藏 ✔ 已收藏 📬 我的收藏 ({{ favorites.length }}) {{ fav.title }} {{ fav.content.substring(0, 100) }}... {{ formatDate(fav.created_at) }} 删除 ``` - [ ] **Step 2: 验证页面加载** 启动服务后访问 `http://localhost:3000/generate`,验证: - 页面正常显示 - 场景选择显示正常 - 关键词输入正常 - [ ] **Step 3: 提交** ```bash git add web/src/views/generate/index.vue git commit -m "feat(web): add AI joke generator page with favorites" ``` --- ## Task 7: 更新 CLAUDE.md API 文档 **Files:** - Modify: `CLAUDE.md` - [ ] **Step 1: 添加新 API 文档** 在 API 接口一览部分添加: ``` - `POST /api/generate` — 公开:AI 生成笑话(keywords + scenarios) ``` - [ ] **Step 2: 提交** ```bash git add CLAUDE.md git commit -m "docs: add /api/generate to API docs" ``` --- ## 验证清单 完成所有任务后,请验证: - [ ] API 文档 `http://localhost:8001/docs` 显示 `/api/generate` 接口 - [ ] 头部导航显示 AI 生成按钮 - [ ] 访问 `/generate` 页面正常 - [ ] 选择场景 + 输入关键词可正常调用 AI 生成 - [ ] 生成结果正确显示 - [ ] 收藏按钮可用,收藏数据正确存储到 localStorage - [ ] 收藏列表正确显示,可删除收藏 - [ ] 页面样式正常(浅色/深色主题)
{{ generatedJoke.content }}