feat: AI改写功能集成角色数据 + UI优化

- 新增story_characters表和seed_characters.sql种子数据(27个角色)
- AI改写/续写功能注入角色信息(性别/年龄/外貌/性格)
- 首页UI下移避让微信退出按钮
- 个人中心页面布局重构
This commit is contained in:
2026-03-11 18:41:56 +08:00
parent 2470cea7e4
commit 4ac47c8474
8 changed files with 478 additions and 47 deletions

View File

@@ -10,11 +10,32 @@ from typing import List, Optional
from datetime import datetime
from app.database import get_db
from app.models.story import Story, StoryDraft, DraftStatus
from app.models.story import Story, StoryDraft, DraftStatus, StoryCharacter
router = APIRouter(prefix="/drafts", tags=["草稿箱"])
# ============ 辅助函数 ============
async def get_story_characters(db: AsyncSession, story_id: int) -> List[dict]:
"""获取故事的所有角色并转为字典列表"""
result = await db.execute(
select(StoryCharacter).where(StoryCharacter.story_id == story_id)
)
characters = result.scalars().all()
return [
{
"name": c.name,
"role_type": c.role_type,
"gender": c.gender,
"age_range": c.age_range,
"appearance": c.appearance,
"personality": c.personality
}
for c in characters
]
# ============ 请求/响应模型 ============
class PathHistoryItem(BaseModel):
@@ -98,6 +119,9 @@ async def process_ai_rewrite(draft_id: int):
await db.commit()
return
# 获取故事角色
characters = await get_story_characters(db, story.id)
# 转换路径历史格式
path_history = draft.path_history or []
@@ -107,7 +131,8 @@ async def process_ai_rewrite(draft_id: int):
story_category=story.category or "未知",
path_history=path_history,
current_content=draft.current_content or "",
user_prompt=draft.user_prompt
user_prompt=draft.user_prompt,
characters=characters
)
if ai_result and ai_result.get("nodes"):
@@ -171,6 +196,9 @@ async def process_ai_rewrite_ending(draft_id: int):
await db.commit()
return
# 获取故事角色
characters = await get_story_characters(db, story.id)
# 从草稿字段获取结局信息
ending_name = draft.current_node_key or "未知结局"
ending_content = draft.current_content or ""
@@ -181,7 +209,8 @@ async def process_ai_rewrite_ending(draft_id: int):
story_category=story.category or "未知",
ending_name=ending_name,
ending_content=ending_content,
user_prompt=draft.user_prompt
user_prompt=draft.user_prompt,
characters=characters
)
if ai_result and ai_result.get("content"):
@@ -266,6 +295,9 @@ async def process_ai_continue_ending(draft_id: int):
await db.commit()
return
# 获取故事角色
characters = await get_story_characters(db, story.id)
# 从草稿字段获取结局信息
ending_name = draft.current_node_key or "未知结局"
ending_content = draft.current_content or ""
@@ -276,7 +308,8 @@ async def process_ai_continue_ending(draft_id: int):
story_category=story.category or "未知",
ending_name=ending_name,
ending_content=ending_content,
user_prompt=draft.user_prompt
user_prompt=draft.user_prompt,
characters=characters
)
if ai_result and ai_result.get("nodes"):

View File

@@ -9,7 +9,7 @@ from typing import Optional, List
from pydantic import BaseModel
from app.database import get_db
from app.models.story import Story, StoryNode, StoryChoice
from app.models.story import Story, StoryNode, StoryChoice, StoryCharacter
router = APIRouter()
@@ -214,6 +214,22 @@ async def ai_rewrite_ending(story_id: int, request: RewriteRequest, db: AsyncSes
if not story:
raise HTTPException(status_code=404, detail="故事不存在")
# 获取故事角色
char_result = await db.execute(
select(StoryCharacter).where(StoryCharacter.story_id == story_id)
)
characters = [
{
"name": c.name,
"role_type": c.role_type,
"gender": c.gender,
"age_range": c.age_range,
"appearance": c.appearance,
"personality": c.personality
}
for c in char_result.scalars().all()
]
# 调用 AI 服务
from app.services.ai import ai_service
@@ -222,7 +238,8 @@ async def ai_rewrite_ending(story_id: int, request: RewriteRequest, db: AsyncSes
story_category=story.category or "未知",
ending_name=request.ending_name or "未知结局",
ending_content=request.ending_content or "",
user_prompt=request.prompt
user_prompt=request.prompt,
characters=characters
)
if ai_result and ai_result.get("content"):
@@ -301,6 +318,22 @@ async def ai_rewrite_branch(
if not story:
raise HTTPException(status_code=404, detail="故事不存在")
# 获取故事角色
char_result = await db.execute(
select(StoryCharacter).where(StoryCharacter.story_id == story_id)
)
characters = [
{
"name": c.name,
"role_type": c.role_type,
"gender": c.gender,
"age_range": c.age_range,
"appearance": c.appearance,
"personality": c.personality
}
for c in char_result.scalars().all()
]
# 将 Pydantic 模型转换为字典列表
path_history = [
{"nodeKey": item.nodeKey, "content": item.content, "choice": item.choice}
@@ -315,7 +348,8 @@ async def ai_rewrite_branch(
story_category=story.category or "未知",
path_history=path_history,
current_content=request.currentContent,
user_prompt=request.prompt
user_prompt=request.prompt,
characters=characters
)
if ai_result and ai_result.get("nodes"):