fix: 删书即删对话——清理已删除书籍遗留的孤儿对话
根因:Conversation 与 Book 无 ORM 级联关系,且 SQLite 外键未强制开启, delete_book 只删书/章节/段落,书的对话变成孤儿残留(book_id 指向不存在的书), 前端确认文案「书、目录和对话都会被删除」与事实不符,AI 助手会把这些幽灵 对话当作阅读记录。 - books.py delete_book: 显式删除该书的全部对话(消息经 ORM cascade 一并删) - database.py _migrate: 启动时清理孤儿对话及其消息(book_id 不在 books 表内; assistant 全局对话 book_id 为空不受影响),幂等,本地/服务器下次启动即生效 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ from sqlalchemy import select, func
|
||||
from app.core.database import get_db, async_session
|
||||
from app.core.config import settings
|
||||
from app.models.book import Book, Chapter, Segment
|
||||
from app.models.chat import Conversation
|
||||
from app.schemas.book import BookOut, BookDetailOut, ChapterOut, SegmentOut
|
||||
from app.epub.parser import parse_epub
|
||||
from app.rag.vector import ensure_embeddings
|
||||
@@ -139,8 +140,16 @@ async def delete_book(book_id: str, db: AsyncSession = Depends(get_db)):
|
||||
if not book:
|
||||
raise HTTPException(404, "书不存在")
|
||||
file_path = book.file_path
|
||||
# 删书即删对话(与前端确认文案「书、目录和对话都会被删除」一致)。
|
||||
# Conversation 与 Book 没有 ORM 级联关系、SQLite 外键又未强制开启,
|
||||
# 必须显式删除该书的对话;其消息经 Conversation.messages 的 cascade 一并删除。
|
||||
convs = (await db.execute(
|
||||
select(Conversation).where(Conversation.book_id == book_id)
|
||||
)).scalars().all()
|
||||
for c in convs:
|
||||
await db.delete(c)
|
||||
await db.delete(book)
|
||||
await db.commit()
|
||||
if file_path and os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
return {"deleted": book_id}
|
||||
return {"deleted": book_id, "conversations": len(convs)}
|
||||
|
||||
@@ -53,6 +53,15 @@ async def _migrate():
|
||||
)
|
||||
await conn.exec_driver_sql("DROP TABLE conversations_old")
|
||||
|
||||
# 数据完整性:删书即删对话。清理由已删除书籍遗留的孤儿对话及其消息
|
||||
#(book_id 指向不存在的书;assistant 全局对话 book_id 为空,不受影响)。
|
||||
await conn.exec_driver_sql(
|
||||
"DELETE FROM conversations WHERE book_id IS NOT NULL AND book_id NOT IN (SELECT id FROM books)"
|
||||
)
|
||||
await conn.exec_driver_sql(
|
||||
"DELETE FROM messages WHERE conversation_id NOT IN (SELECT id FROM conversations)"
|
||||
)
|
||||
|
||||
|
||||
async def get_db():
|
||||
async with async_session() as session:
|
||||
|
||||
Reference in New Issue
Block a user