"""将 backend/data/books/*.json 导入 word_books / word_book_entries 表。""" from __future__ import annotations import json import sys from pathlib import Path BACKEND_DIR = Path(__file__).resolve().parents[1] if str(BACKEND_DIR) not in sys.path: sys.path.insert(0, str(BACKEND_DIR)) from database import SessionLocal # noqa: E402 from models import WordBook, WordBookEntry # noqa: E402 from services.book_service import DEPRECATED_SLUGS # noqa: E402 BOOKS_DIR = BACKEND_DIR / "data" / "books" def upsert_book(session, data: dict) -> WordBook: book = session.query(WordBook).filter(WordBook.slug == data["slug"]).first() if not book: book = WordBook(slug=data["slug"]) session.add(book) book.title = data["title"] book.description = data.get("description") book.level = data.get("level", "general") book.word_count = data.get("word_count", 0) book.unit_count = data.get("unit_count", 0) book.sort_order = data.get("sort_order", 0) book.daily_target = data.get("daily_target", 15) book.master_required_count = data.get("master_required_count", 4) book.weak_wrong_threshold = data.get("weak_wrong_threshold", 2) book.learn_mode = data.get("learn_mode", "sequential") book.is_published = 1 session.flush() return book def import_book_file(session, path: Path) -> tuple[str, int]: data = json.loads(path.read_text(encoding="utf-8")) book = upsert_book(session, data) session.query(WordBookEntry).filter(WordBookEntry.book_id == book.id).delete( synchronize_session=False ) count = 0 for unit in data.get("units", []): unit_no = unit.get("unit", 1) unit_title = unit.get("title") for w in unit.get("words", []): entry = WordBookEntry( book_id=book.id, unit=unit_no, unit_title=unit_title, sort_index=w["sort_index"], lemma_en=w["lemma_en"].strip().lower()[:128], zh=w["zh"].strip()[:256], phonetic=(w.get("phonetic") or "")[:128] or None, example_en=w.get("example_en"), example_cn=w.get("example_cn"), ) session.add(entry) count += 1 book.word_count = count book.unit_count = len(data.get("units", [])) session.commit() return book.slug, count def import_all(books_dir: Path = BOOKS_DIR) -> None: files = sorted(books_dir.glob("*.json")) files = [ f for f in files if f.name != "manifest.json" and f.stem not in DEPRECATED_SLUGS ] if not files: print(f"未找到词书 JSON: {books_dir}") return session = SessionLocal() try: for path in files: slug, count = import_book_file(session, path) print(f" 导入 {slug}: {count} 词条") for slug in DEPRECATED_SLUGS: book = session.query(WordBook).filter(WordBook.slug == slug).first() if book and book.is_published: book.is_published = 0 session.commit() print(f" 下架旧词书 {slug}") finally: session.close() def main() -> None: import argparse parser = argparse.ArgumentParser(description="导入词书 JSON 到数据库") parser.add_argument("--dir", type=Path, default=BOOKS_DIR) args = parser.parse_args() import_all(args.dir) if __name__ == "__main__": main()