/** * 五年级上册语文(人教版)第一单元 数据初始化脚本 * * 功能: * 1. 清理现有教材/章节/知识点/题目/试卷相关数据(保留用户/班级/学校) * 2. 创建五年级上册语文教材 * 3. 创建第一单元 4 课(白鹭、落花生、桂花雨、珍珠鸟)的章节与正文 * 4. 创建每课的知识点(生字、朗读、理解、表达等)+ 前置依赖 * 5. 创建题库(生字题、选择题、判断题、填空题、简答题) * 6. 创建第一单元测验试卷并关联题目 * * 运行:npx tsx scripts/seed-grade5-chinese.ts */ import "dotenv/config"; import { db } from "../src/shared/db"; import { textbooks, chapters, knowledgePoints, knowledgePointPrerequisites, questions, questionsToKnowledgePoints, exams, examQuestions, subjects, grades, } from "../src/shared/db/schema"; import { createId } from "@paralleldrive/cuid2"; import { sql } from "drizzle-orm"; // ============ 类型定义 ============ interface SeedOption { id: string; text: string; isCorrect?: boolean; } interface SeedQuestionContent { text?: string; options?: SeedOption[]; correctAnswer?: string | boolean; } interface SeedQuestion { id: string; type: "single_choice" | "multiple_choice" | "text" | "judgment"; difficulty: number; content: SeedQuestionContent; score: number; knowledgePointIds: string[]; } // ============ 常量 ============ const NOW = new Date(); const GRADE_VALUE = "Grade 5"; const SUBJECT_VALUE = "Chinese"; // ============ 主流程 ============ async function main() { console.log("🌱 开始五年级上册语文(人教版)第一单元数据初始化..."); const start = performance.now(); await cleanupTextbookAndQuestions(); const { subjectId, gradeId, authorId } = await ensurePrerequisites(); const { chapterMap } = await seedTextbookAndChapters(); const kpMap = await seedKnowledgePoints(chapterMap); const questionList = await seedQuestions(authorId, kpMap); await seedExam(authorId, subjectId, gradeId, questionList); const elapsed = ((performance.now() - start) / 1000).toFixed(2); console.log(`\n✅ 初始化完成,耗时 ${elapsed}s`); console.log(` 教材:1 本(五年级语文上册)`); console.log(` 章节:1 个单元 + 4 课`); console.log(` 知识点:${Object.keys(kpMap).length} 个`); console.log(` 题目:${questionList.length} 道`); console.log(` 试卷:1 套(第一单元测验)`); } main().catch((err) => { console.error("❌ 初始化失败:", err); process.exit(1); }); // ============ 0. 清理 ============ async function cleanupTextbookAndQuestions() { console.log("🧹 清理现有教材/题目/试卷数据(保留用户/班级/学校)..."); await db.execute(sql`SET FOREIGN_KEY_CHECKS = 0;`); const tables = [ "homework_answers", "homework_submissions", "homework_assignment_targets", "homework_assignment_questions", "homework_assignments", "submission_answers", "exam_submissions", "exam_questions", "exams", "questions_to_knowledge_points", "questions", "knowledge_point_prerequisites", "knowledge_points", "chapters", "textbooks", ]; for (const t of tables) { try { await db.execute(sql.raw(`TRUNCATE TABLE \`${t}\`;`)); } catch { // 表可能不存在,跳过 } } await db.execute(sql`SET FOREIGN_KEY_CHECKS = 1;`); console.log("✓ 清理完成"); } // ============ 1. 前置数据 ============ async function ensurePrerequisites(): Promise<{ subjectId: string; gradeId: string; authorId: string; }> { console.log("🔍 检查学科/年级/教师..."); // 学科:查询或创建 Chinese(用原生 SQL 避免 DB schema 不同步问题) let subjectId: string | null = null; try { const subjRows = await db.execute(sql`SELECT id FROM subjects WHERE code = 'CHINESE' OR name = '语文' LIMIT 1`); const subjRow = subjRows[0] as Array<{ id: string }> | undefined; if (subjRow && subjRow.length > 0) { subjectId = subjRow[0].id; } } catch { // subjects 表可能不存在 } if (!subjectId) { subjectId = "subj_chinese"; try { await db.execute(sql` INSERT INTO subjects (id, name, code, \`order\`, created_at, updated_at) VALUES (${subjectId}, '语文', 'CHINESE', 1, NOW(), NOW()) `); console.log(" + 创建学科:语文(CHINESE)"); } catch { // 可能已存在或表结构不同,尝试不带 order try { await db.execute(sql` INSERT INTO subjects (id, name, code, created_at, updated_at) VALUES (${subjectId}, '语文', 'CHINESE', NOW(), NOW()) `); console.log(" + 创建学科:语文(CHINESE)"); } catch { // 如果仍然失败,尝试用 Drizzle insert try { await db.insert(subjects).values({ id: subjectId, name: "语文", code: "CHINESE", order: 1 }); console.log(" + 创建学科:语文(CHINESE)"); } catch { console.log(" ! 学科创建失败,尝试继续..."); } } } } else { console.log(` ✓ 学科 ID:${subjectId}`); } const school = await db.query.schools.findFirst(); if (!school) { throw new Error("数据库中不存在任何学校,请先运行 npm run db:seed 初始化基础数据"); } // 年级:查询或创建五年级(用原生 SQL 避免 DB schema 不同步问题) let gradeId: string | null = null; try { const gradeRows = await db.execute(sql` SELECT id FROM grades WHERE school_id = ${school.id} AND (name = '五年级' OR name = ${GRADE_VALUE}) LIMIT 1 `); const gradeRow = gradeRows[0] as Array<{ id: string }> | undefined; if (gradeRow && gradeRow.length > 0) { gradeId = gradeRow[0].id; } } catch { // grades 表可能不存在 } if (!gradeId) { gradeId = `grade_g5_${school.id.slice(0, 8)}`; try { await db.execute(sql` INSERT INTO grades (id, school_id, name, \`order\`, created_at, updated_at) VALUES (${gradeId}, ${school.id}, '五年级', 5, NOW(), NOW()) `); console.log(" + 创建年级:五年级"); } catch { try { await db.insert(grades).values({ id: gradeId, schoolId: school.id, name: "五年级", order: 5, }); console.log(" + 创建年级:五年级"); } catch { console.log(" ! 年级创建失败,尝试继续..."); } } } else { console.log(` ✓ 年级 ID:${gradeId}`); } // 教师:查询第一个有教师角色的用户(用原生 SQL 避免 DB schema 不同步问题) let authorId: string | null = null; try { const roleRows = await db.execute(sql` SELECT r.id FROM roles r WHERE r.name = 'teacher' LIMIT 1 `); const roleRow = roleRows[0] as Array<{ id: string }> | undefined; if (roleRow && roleRow.length > 0) { const linkRows = await db.execute(sql` SELECT utr.user_id FROM users_to_roles utr WHERE utr.role_id = ${roleRow[0].id} LIMIT 1 `); const linkRow = linkRows[0] as Array<{ user_id: string }> | undefined; if (linkRow && linkRow.length > 0) { authorId = linkRow[0].user_id; } } } catch { // roles 表可能不存在或 schema 不同步,忽略 } if (!authorId) { // 兜底:取第一个用户 const userRows = await db.execute(sql`SELECT id FROM users LIMIT 1`); const userRow = userRows[0] as Array<{ id: string }> | undefined; if (!userRow || userRow.length === 0) { throw new Error("数据库中不存在任何用户,请先运行 npm run db:seed 初始化基础数据"); } authorId = userRow[0].id; console.log(" ! 未找到教师用户,使用第一个用户作为题目作者"); } else { console.log(` ✓ 教师 ID:${authorId}`); } return { subjectId: subjectId!, gradeId: gradeId!, authorId: authorId! }; } // ============ 2. 教材 + 章节 ============ async function seedTextbookAndChapters(): Promise<{ chapterMap: Record; }> { console.log("📖 创建五年级上册语文教材与章节..."); const chapterMap: Record = {}; const textbookId = "tb_chinese_g5_up"; await db.insert(textbooks).values({ id: textbookId, title: "五年级语文(上册)", subject: SUBJECT_VALUE, grade: GRADE_VALUE, publisher: "人民教育出版社", }); const unitId = "ch_chinese_g5_u1"; await db.insert(chapters).values({ id: unitId, textbookId, title: "第一单元 课文", order: 1, parentId: null, content: `# 第一单元 课文 本单元围绕「万物有灵」主题,通过《白鹭》《落花生》《桂花雨》《珍珠鸟》四篇课文,学习借物喻人、借物抒情的方法,体会作者表达的情感。`, }); // 第 1 课 白鹭 const lesson1Id = "ch_chinese_g5_u1_l1"; chapterMap.l1_bailu = lesson1Id; await db.insert(chapters).values({ id: lesson1Id, textbookId, title: "1 白鹭", order: 1, parentId: unitId, content: lesson1Content, }); // 第 2 课 落花生 const lesson2Id = "ch_chinese_g5_u1_l2"; chapterMap.l2_luohuasheng = lesson2Id; await db.insert(chapters).values({ id: lesson2Id, textbookId, title: "2 落花生", order: 2, parentId: unitId, content: lesson2Content, }); // 第 3 课 桂花雨 const lesson3Id = "ch_chinese_g5_u1_l3"; chapterMap.l3_guihuayu = lesson3Id; await db.insert(chapters).values({ id: lesson3Id, textbookId, title: "3 桂花雨", order: 3, parentId: unitId, content: lesson3Content, }); // 第 4 课 珍珠鸟 const lesson4Id = "ch_chinese_g5_u1_l4"; chapterMap.l4_zhenzhuniao = lesson4Id; await db.insert(chapters).values({ id: lesson4Id, textbookId, title: "4* 珍珠鸟(略读课文)", order: 4, parentId: unitId, content: lesson4Content, }); console.log("✓ 创建 1 本教材,含第一单元 4 课"); return { chapterMap }; } // ============ 3. 知识点 + 前置依赖 ============ async function seedKnowledgePoints(chapterMap: Record): Promise> { console.log("🧠 创建知识点..."); const map: Record = {}; const kpDefs: Array<{ key: string; name: string; chapterKey: keyof typeof chapterMap; desc: string; level: number; order: number; }> = [ // 第 1 课 白鹭 { key: "l1_char", name: "生字认读:鹭嫌嵌匣嗜韵", chapterKey: "l1_bailu", desc: "认识鹭、嫌、嵌、匣、嗜、韵 6 个生字,正确书写并理解含义", level: 1, order: 1 }, { key: "l1_read", name: "朗读与背诵", chapterKey: "l1_bailu", desc: "正确、流利、有感情地朗读课文,背诵全文", level: 1, order: 2 }, { key: "l1_understand", name: "体会白鹭是一首精巧的诗", chapterKey: "l1_bailu", desc: "从色素配合、身段大小、蓑毛/喙/脚的描写中体会白鹭的精巧与适宜", level: 2, order: 3 }, { key: "l1_image", name: "为三幅图画起名字", chapterKey: "l1_bailu", desc: "为第 6~8 自然段描绘的清水田钓鱼、清晨望哨、黄昏低飞三幅图起名字", level: 2, order: 4 }, { key: "l1_express", name: "借物喻人/借物抒情", chapterKey: "l1_bailu", desc: "学习作者把白鹭比作诗,通过具体描写表达赞美之情的方法", level: 3, order: 5 }, // 第 2 课 落花生 { key: "l2_char", name: "生字认读:亩吩榨榴矮", chapterKey: "l2_luohuasheng", desc: "认识亩、吩、榨、榴、矮 5 个生字,正确书写并理解含义", level: 1, order: 1 }, { key: "l2_read", name: "分角色朗读", chapterKey: "l2_luohuasheng", desc: "分角色朗读课文,读出人物对话的语气", level: 1, order: 2 }, { key: "l2_understand", name: "体会花生的特点", chapterKey: "l2_luohuasheng", desc: "从对话中体会花生埋在地里、不好看但有用的特点", level: 2, order: 3 }, { key: "l2_truth", name: "理解父亲借花生说的道理", chapterKey: "l2_luohuasheng", desc: "理解人要做有用的人,不要做只讲体面而对别人没有好处的人", level: 3, order: 4 }, { key: "l2_write", name: "小练笔:借物喻人", chapterKey: "l2_luohuasheng", desc: "选择竹子、梅花、蜜蜂、路灯等事物写一段话,运用借物喻人的方法", level: 3, order: 5 }, // 第 3 课 桂花雨 { key: "l3_char", name: "生字认读:萝杭", chapterKey: "l3_guihuayu", desc: "认识萝、杭 2 个生字,正确书写并理解含义", level: 1, order: 1 }, { key: "l3_read", name: "有感情地朗读", chapterKey: "l3_guihuayu", desc: "有感情地朗读课文,读出对桂花的喜爱和对故乡的思念", level: 1, order: 2 }, { key: "l3_memory", name: "桂花带来的美好回忆", chapterKey: "l3_guihuayu", desc: "说说桂花给我带来的美好回忆(摇花乐、桂花雨、桂花香)", level: 2, order: 3 }, { key: "l3_feeling", name: "体会句子蕴含的感情", chapterKey: "l3_guihuayu", desc: "理解这里的桂花再香也比不上家乡院子里的桂花蕴含的思乡之情", level: 3, order: 4 }, // 第 4 课 珍珠鸟 { key: "l4_char", name: "生字认读:蔓幽悉雏哟柜享陪待趴脸眸", chapterKey: "l4_zhenzhuniao", desc: "认识蔓、幽、悉、雏、哟、柜、享、陪、待、趴、脸、眸 12 个生字", level: 1, order: 1 }, { key: "l4_read", name: "默读课文", chapterKey: "l4_zhenzhuniao", desc: "默读课文,理清我逐渐得到珍珠鸟信赖的过程", level: 1, order: 2 }, { key: "l4_trust", name: "体会信赖创造美好境界", chapterKey: "l4_zhenzhuniao", desc: "找出描写珍珠鸟可爱的语句,体会我和珍珠鸟之间的情意", level: 2, order: 3 }, // 单元综合 { key: "unit_theme", name: "单元主题:万物有灵", chapterKey: "l1_bailu", desc: "理解本单元万物有灵主题,体会作者对事物的细致观察与真挚情感", level: 3, order: 1 }, { key: "unit_method", name: "单元方法:借物抒情", chapterKey: "l1_bailu", desc: "综合运用借物喻人、借物抒情的方法进行阅读与写作", level: 3, order: 2 }, ]; for (const kp of kpDefs) { const id = `kp_g5_${kp.key}`; map[kp.key] = id; await db.insert(knowledgePoints).values({ id, name: kp.name, description: kp.desc, level: kp.level, order: kp.order, chapterId: chapterMap[kp.chapterKey], }); } // 前置依赖:生字 → 朗读 → 理解 → 表达(每课内部递进) const prereqPairs: Array<[string, string]> = [ ["l1_char", "l1_read"], ["l1_read", "l1_understand"], ["l1_understand", "l1_image"], ["l1_understand", "l1_express"], ["l2_char", "l2_read"], ["l2_read", "l2_understand"], ["l2_understand", "l2_truth"], ["l2_truth", "l2_write"], ["l3_char", "l3_read"], ["l3_read", "l3_memory"], ["l3_memory", "l3_feeling"], ["l4_char", "l4_read"], ["l4_read", "l4_trust"], ["l1_express", "unit_method"], ["l2_write", "unit_method"], ["l1_understand", "unit_theme"], ]; for (const [from, to] of prereqPairs) { const fromId = map[from]; const toId = map[to]; if (fromId && toId) { await db.insert(knowledgePointPrerequisites).values({ knowledgePointId: toId, prerequisiteKpId: fromId, }); } } console.log(`✓ 创建 ${kpDefs.length} 个知识点,${prereqPairs.length} 条前置依赖`); return map; } // ============ 4. 题库 ============ async function seedQuestions( authorId: string, kpMap: Record ): Promise { console.log("❓ 创建题库..."); const all: SeedQuestion[] = []; // ---- 第 1 课 白鹭 ---- const l1Questions: SeedQuestion[] = [ { id: createId(), type: "single_choice", difficulty: 1, content: { text: "「白鹭是一首精巧的诗」中,「精巧」的意思最接近?", options: [ { id: "A", text: "精美巧妙", isCorrect: true }, { id: "B", text: "精明能干", isCorrect: false }, { id: "C", text: "精心制作", isCorrect: false }, { id: "D", text: "精打细算", isCorrect: false }, ], }, score: 5, knowledgePointIds: [kpMap.l1_understand], }, { id: createId(), type: "single_choice", difficulty: 2, content: { text: "下列哪个字不是《白鹭》一课的生字?", options: [ { id: "A", text: "鹭", isCorrect: false }, { id: "B", text: "嫌", isCorrect: false }, { id: "C", text: "嵌", isCorrect: false }, { id: "D", text: "鹤", isCorrect: true }, ], }, score: 5, knowledgePointIds: [kpMap.l1_char], }, { id: createId(), type: "judgment", difficulty: 1, content: { text: "「增之一分则嫌长,减之一分则嫌短」说明白鹭的身段大小非常适宜。", correctAnswer: true, }, score: 5, knowledgePointIds: [kpMap.l1_understand], }, { id: createId(), type: "judgment", difficulty: 2, content: { text: "作者认为白鹭不会唱歌是它的美中不足,所以白鹭不如歌好听。", correctAnswer: false, }, score: 5, knowledgePointIds: [kpMap.l1_express], }, { id: createId(), type: "text", difficulty: 2, content: { text: "为第 6~8 自然段描绘的三幅图画分别起一个名字。", correctAnswer: "清水田钓鱼图;清晨望哨图;黄昏低飞图", }, score: 10, knowledgePointIds: [kpMap.l1_image], }, { id: createId(), type: "text", difficulty: 3, content: { text: "为什么说「白鹭实在是一首诗,一首韵在骨子里的散文诗」?结合课文谈谈你的理解。", correctAnswer: "白鹭外形精巧适宜,色素配合得当,像一首精巧的诗;它在清水田钓鱼、清晨望哨、黄昏低飞的画面富有诗意;它的美是内在的、含蓄的,像韵在骨子里的散文诗。", }, score: 10, knowledgePointIds: [kpMap.l1_express, kpMap.l1_understand], }, ]; // ---- 第 2 课 落花生 ---- const l2Questions: SeedQuestion[] = [ { id: createId(), type: "single_choice", difficulty: 1, content: { text: "父亲借花生告诉我们的道理是?", options: [ { id: "A", text: "花生很好吃", isCorrect: false }, { id: "B", text: "人要做有用的人,不要做只讲体面而对别人没有好处的人", isCorrect: true }, { id: "C", text: "花生可以榨油", isCorrect: false }, { id: "D", text: "花生价钱便宜", isCorrect: false }, ], }, score: 5, knowledgePointIds: [kpMap.l2_truth], }, { id: createId(), type: "single_choice", difficulty: 2, content: { text: "父亲把花生和桃子、石榴、苹果作对比,是为了说明花生?", options: [ { id: "A", text: "果实埋在地里,不炫耀,朴实无华", isCorrect: true }, { id: "B", text: "比桃子石榴苹果更好吃", isCorrect: false }, { id: "C", text: "长得矮小不好看", isCorrect: false }, { id: "D", text: "价钱比它们便宜", isCorrect: false }, ], }, score: 5, knowledgePointIds: [kpMap.l2_understand], }, { id: createId(), type: "judgment", difficulty: 1, content: { text: "「榨」字的读音是 zhà。", correctAnswer: true, }, score: 5, knowledgePointIds: [kpMap.l2_char], }, { id: createId(), type: "judgment", difficulty: 2, content: { text: "父亲认为花生虽然不好看可是很有用,所以我们要像花生一样只做有用的人不要讲体面。", correctAnswer: false, }, score: 5, knowledgePointIds: [kpMap.l2_truth], }, { id: createId(), type: "text", difficulty: 2, content: { text: "课文围绕「落花生」写了哪些内容?(请简要概括)", correctAnswer: "种花生、收花生;尝花生(过收获节);议花生(父亲借花生讲道理)", }, score: 10, knowledgePointIds: [kpMap.l2_understand], }, { id: createId(), type: "text", difficulty: 3, content: { text: "小练笔:选择竹子、梅花、蜜蜂或路灯中的一种,仿照《落花生》的写法,写一段话(借物喻人)。", correctAnswer: "(开放题)示例:竹子虽然外表朴素,没有牡丹的艳丽,但它虚心有节、坚韧不拔,无论风吹雨打都挺直腰杆。我们要像竹子一样,做一个虚心、坚强、有骨气的人。", }, score: 10, knowledgePointIds: [kpMap.l2_write], }, ]; // ---- 第 3 课 桂花雨 ---- const l3Questions: SeedQuestion[] = [ { id: createId(), type: "single_choice", difficulty: 1, content: { text: "「这里的桂花再香,也比不上家乡院子里的桂花」表达了作者怎样的感情?", options: [ { id: "A", text: "觉得杭州的桂花不够香", isCorrect: false }, { id: "B", text: "对故乡和童年生活的思念之情", isCorrect: true }, { id: "C", text: "不喜欢杭州的桂花", isCorrect: false }, { id: "D", text: "觉得母亲挑剔", isCorrect: false }, ], }, score: 5, knowledgePointIds: [kpMap.l3_feeling], }, { id: createId(), type: "single_choice", difficulty: 2, content: { text: "下列哪个词最能概括「摇花」给「我」带来的感受?", options: [ { id: "A", text: "害怕", isCorrect: false }, { id: "B", text: "摇花乐", isCorrect: true }, { id: "C", text: "辛苦", isCorrect: false }, { id: "D", text: "无聊", isCorrect: false }, ], }, score: 5, knowledgePointIds: [kpMap.l3_memory], }, { id: createId(), type: "judgment", difficulty: 1, content: { text: "「萝」和「杭」都是《桂花雨》一课的生字。", correctAnswer: true, }, score: 5, knowledgePointIds: [kpMap.l3_char], }, { id: createId(), type: "judgment", difficulty: 2, content: { text: "桂花摇下来比落在泥土里的香味差。", correctAnswer: false, }, score: 5, knowledgePointIds: [kpMap.l3_memory], }, { id: createId(), type: "text", difficulty: 2, content: { text: "说说桂花给「我」带来了哪些美好的回忆?", correctAnswer: "摇花乐(缠着母亲摇桂花,桂花纷纷落下像下雨);桂花雨(满头满身都是桂花,好香的雨);桂花香(全年整个村子都浸在桂花香里)", }, score: 10, knowledgePointIds: [kpMap.l3_memory], }, ]; // ---- 第 4 课 珍珠鸟 ---- const l4Questions: SeedQuestion[] = [ { id: createId(), type: "single_choice", difficulty: 1, content: { text: "「信赖,往往创造出美好的境界」这句话的含义是?", options: [ { id: "A", text: "珍珠鸟很可爱", isCorrect: false }, { id: "B", text: "人与动物(人与人)之间真诚的信赖,能带来和谐美好的关系", isCorrect: true }, { id: "C", text: "鸟笼很舒适", isCorrect: false }, { id: "D", text: "写作需要安静", isCorrect: false }, ], }, score: 5, knowledgePointIds: [kpMap.l4_trust], }, { id: createId(), type: "single_choice", difficulty: 2, content: { text: "珍珠鸟逐渐信赖「我」的过程,正确顺序是?", options: [ { id: "A", text: "落在肩上 → 在屋里飞 → 落在书桌上", isCorrect: false }, { id: "B", text: "在屋里飞 → 落在书桌上 → 落在肩上睡着", isCorrect: true }, { id: "C", text: "落在书桌上 → 在屋里飞 → 落在肩上", isCorrect: false }, { id: "D", text: "落在肩上 → 落在书桌上 → 在屋里飞", isCorrect: false }, ], }, score: 5, knowledgePointIds: [kpMap.l4_read], }, { id: createId(), type: "judgment", difficulty: 1, content: { text: "「雏」字的读音是 chú。", correctAnswer: true, }, score: 5, knowledgePointIds: [kpMap.l4_char], }, { id: createId(), type: "text", difficulty: 2, content: { text: "从课文中找出两句描写珍珠鸟可爱的语句,抄写下来。", correctAnswer: "(示例)1. 它好肥,整个身子好像一个蓬松的球儿。2. 小红爪子在纸上发出嚓嚓的响声。", }, score: 10, knowledgePointIds: [kpMap.l4_trust], }, ]; // ---- 单元综合 ---- const unitQuestions: SeedQuestion[] = [ { id: createId(), type: "multiple_choice", difficulty: 3, content: { text: "本单元四篇课文都运用了「借物抒情」的方法,下列说法正确的有?(多选)", options: [ { id: "A", text: "《白鹭》借白鹭表达对平凡之美的赞美", isCorrect: true }, { id: "B", text: "《落花生》借花生说明要做有用的人", isCorrect: true }, { id: "C", text: "《桂花雨》借桂花表达思乡之情", isCorrect: true }, { id: "D", text: "《珍珠鸟》借珍珠鸟说明信赖创造美好境界", isCorrect: true }, ], }, score: 8, knowledgePointIds: [kpMap.unit_method, kpMap.unit_theme], }, { id: createId(), type: "text", difficulty: 3, content: { text: "本单元的主题是「万物有灵」,请结合四篇课文谈谈你的理解。", correctAnswer: "白鹭的精巧、花生的朴实、桂花的芬芳、珍珠鸟的信赖,都体现了万物各有其美。作者通过细致观察和真挚情感,把事物写得生动感人,启示我们要善于发现美、做有用的人、珍惜情感。", }, score: 12, knowledgePointIds: [kpMap.unit_theme], }, ]; const groups = [l1Questions, l2Questions, l3Questions, l4Questions, unitQuestions]; for (const g of groups) { all.push(...g); } for (const q of all) { await db.insert(questions).values({ id: q.id, type: q.type, difficulty: q.difficulty, content: q.content, authorId, }); if (q.knowledgePointIds.length > 0) { await db.insert(questionsToKnowledgePoints).values( q.knowledgePointIds.map((kpId) => ({ questionId: q.id, knowledgePointId: kpId, })) ); } } console.log(`✓ 创建 ${all.length} 道题目`); return all; } // ============ 5. 试卷 ============ async function seedExam( creatorId: string, subjectId: string, gradeId: string, questionList: SeedQuestion[] ): Promise { console.log("📝 创建第一单元测验试卷..."); const examId = "exam_chinese_g5_u1"; // 选取 10 题组卷 const selected = [ questionList[0], // l1 选择 questionList[5], // l1 简答 questionList[6], // l2 选择 questionList[9], // l2 判断 questionList[12], // l3 选择 questionList[15], // l3 判断 questionList[17], // l4 选择 questionList[19], // l4 判断 questionList[21], // 单元多选 questionList[22], // 单元简答 ]; const makeGroup = (title: string, children: Array<{ questionId: string; score: number }>) => ({ id: createId(), type: "group" as const, title, children: children.map((c) => ({ id: createId(), type: "question" as const, questionId: c.questionId, score: c.score, })), }); const structure = [ makeGroup("一、选择题(每题 5 分)", [ { questionId: selected[0].id, score: 5 }, { questionId: selected[2].id, score: 5 }, { questionId: selected[4].id, score: 5 }, { questionId: selected[6].id, score: 5 }, ]), makeGroup("二、判断题(每题 5 分)", [ { questionId: selected[3].id, score: 5 }, { questionId: selected[5].id, score: 5 }, { questionId: selected[7].id, score: 5 }, ]), makeGroup("三、多选题(8 分)", [ { questionId: selected[8].id, score: 8 }, ]), makeGroup("四、简答题(共 32 分)", [ { questionId: selected[1].id, score: 10 }, { questionId: selected[9].id, score: 12 }, ]), ]; const totalScore = selected.reduce((sum, q) => sum + q.score, 0); await db.insert(exams).values({ id: examId, title: "五年级语文(上册)第一单元测验", description: JSON.stringify({ subject: SUBJECT_VALUE, grade: GRADE_VALUE, difficulty: 2, totalScore, durationMin: 60, questionCount: selected.length, tags: ["第一单元", "白鹭", "落花生", "桂花雨", "珍珠鸟"], }), creatorId, subjectId, gradeId, status: "published", startTime: NOW, examMode: "homework", durationMinutes: 60, structure, }); await db.insert(examQuestions).values( selected.map((q, i) => ({ examId, questionId: q.id, score: q.score, order: i, })) ); console.log(`✓ 创建试卷「${examId}」,含 ${selected.length} 题,总分 ${totalScore} 分`); } // ============ 课文正文内容(模板字符串) ============ const lesson1Content = `# 1 白鹭 白鹭是一首精巧的诗。 色素的配合,身段的大小,一切都很适宜。 白鹤太大而嫌生硬,即使如粉红的朱鹭或灰色的苍鹭,也觉得大了一些,而且太不寻常了。 然而白鹭却因为它的常见,而被人忘却了它的美。 那雪白的蓑毛,那全身的流线型结构,那铁色的长喙,那青色的脚,增之一分则嫌长,减之一分则嫌短,素之一忽则嫌白,黛之一忽则嫌黑。 在清水田里,时有一只两只白鹭站着钓鱼,整个的田便成了一幅嵌在玻璃框里的画。田的大小好像是有心人为白鹭设计的镜匣。 晴天的清晨,每每看见它孤独地站立于小树的绝顶,看来像是不安稳,而它却很悠然。这是别的鸟很难表现的一种嗜好。人们说它是在望哨,可它真是在望哨吗? 黄昏的空中偶见白鹭的低飞,更是乡居生活中的一种恩惠。那是清澄的形象化,而且具有生命了。 或许有人会感到美中不足,白鹭不会唱歌。但是白鹭本身不就是一首很优美的歌吗? 不,歌未免太铿锵了。 白鹭实在是一首诗,一首韵在骨子里的散文诗。 --- **生字**:鹭 嫌 嵌 匣 嗜 韵 **课后要求**:朗读课文,说说从哪些地方感受到「白鹭是一首精巧的诗」;为第6~8自然段描绘的三幅图画起名字;背诵课文,抄写喜欢的自然段。`; const lesson2Content = `# 2 落花生 我们家的后园有半亩空地。母亲说:「让它荒着怪可惜的,你们那么爱吃花生,就开辟出来种花生吧。」我们姐弟几个都很高兴,买种,翻地,播种,浇水,没过几个月,居然收获了。 母亲说:「今晚我们过一个收获节,请你们的父亲也来尝尝我们的新花生,好不好?」母亲把花生做成了好几样食品,还吩咐就在后园的茅亭里过这个节。 那晚的天色不大好,可是父亲也来了,实在很难得。 父亲说:「你们爱吃花生吗?」 我们争着回答:「爱!」 「谁能把花生的好处说出来?」 姐姐说:「花生的味道很美。」 哥哥说:「花生可以榨油。」 我说:「花生的价钱便宜,谁都可以买来吃,都喜欢吃。这就是它的好处。」 父亲说:「花生的好处很多,有一样最可贵。它的果实埋在地里,不像桃子、石榴、苹果那样,把鲜红嫩绿的果实高高地挂在枝上,使人一见就生爱慕之心。你们看它矮矮地长在地上,等到成熟了,也不能立刻分辨出来它有没有果实,必须挖起来才知道。」 我们都说是,母亲也点点头。 父亲接下去说:「所以你们要像花生,它虽然不好看,可是很有用。」 我说:「那么,人要做有用的人,不要做只讲体面,而对别人没有好处的人。」 父亲说:「对。这是我对你们的希望。」 我们谈到深夜才散。花生做的食品都吃完了,父亲的话却深深地印在我的心上。 --- **生字**:亩 吩 榨 榴 矮 **课后要求**:分角色朗读课文,说说课文围绕「落花生」写了哪些内容;从对话中体会花生的特点及父亲借花生告诉的道理;小练笔:选择竹子、梅花、蜜蜂、路灯等事物写一段话。`; const lesson3Content = `# 3 桂花雨 中秋节前后,正是故乡桂花盛开的时节。 小时候,我无论对什么花,都不懂得欣赏。父亲总是指指点点地告诉我,这是梅花,那是木兰花……但我除了记些名字外,并不喜欢。我喜欢的是桂花。桂花树的样子笨笨的,不像梅树那样有姿态。不开花时,只见到满树的叶子;开花时,仔细地在树丛里寻找,才能看到那些小花。可是桂花的香气,太迷人了。 故乡靠海,八月是台风季节。桂花一开,母亲就开始担心了:「可别来台风啊!」母亲每天都要在前后院子走一回,嘴里念着:「只要不来台风,我就可以收几大箩。送一箩给胡家老爷爷,送一箩给毛家老婆婆,他们两家糕饼做得多。」 桂花盛开的时候,不说香飘十里,至少前后左右十几家邻居,没有不浸在桂花香里的。桂花成熟时,就应当「摇」。摇下来的桂花,朵朵完整、新鲜。如果让它开过了,落在泥土里,尤其是被风吹落,比摇下来的香味就差多了。 摇花对我来说是件大事,我总是缠着母亲问:「妈,怎么还不摇桂花呢?」母亲说:「还早呢,花开的时间太短,摇不下来的。」可是母亲一看天上布满阴云,就知道要来台风了,赶紧叫大家提前摇桂花。这下,我可乐了,帮大人抱着桂花树,使劲地摇。摇哇摇,桂花纷纷落下来,我们满头满身都是桂花。我喊着:「啊!真像下雨,好香的雨呀!」 桂花摇落以后,挑去小枝小叶,晒上几天太阳,收在铁盒子里,可以加在茶叶里泡茶,过年时还可以做糕饼。全年,整个村子都浸在桂花的香气里。 我念中学的时候,全家到了杭州。杭州有一处小山,全是桂花树,花开时那才是香飘十里。秋天,我常到那儿去赏桂花。回家时,总要捡一大袋桂花给母亲。可是母亲说:「这里的桂花再香,也比不上家乡院子里的桂花。」 于是,我又想起了在故乡童年时代的「摇花乐」,还有那摇落的阵阵桂花雨。 --- **生字**:萝 杭 **课后要求**:有感情地朗读课文,说说桂花给「我」带来哪些美好回忆;体会句子蕴含的感情;联系「阅读链接」理解「这里的桂花再香,也比不上家乡院子里的桂花」的含义。`; const lesson4Content = `# 4* 珍珠鸟(略读课文) 真好!朋友送我一对珍珠鸟,放在一个简易的竹条编成的笼子里,笼内还有一卷干草,那是小鸟舒适又温暖的巢。 有人说,这是一种怕人的鸟。 我把鸟笼挂在窗前。那儿还有一大盆异常茂盛的法国吊兰。我便用吊兰长长的、串生着小绿叶的垂蔓蒙盖在鸟笼上。它们就像躲进幽深的丛林一样安全,从中传出的笛儿般又细又亮的叫声,也就格外轻松自在了。小鸟的影子就在这中间隐约闪动,看不完整,有时连笼子也看不出,却见它们可爱的鲜红小嘴从绿叶中伸出来。 我很少扒开垂蔓瞧它们,它们便渐渐敢伸出小脑袋瞅瞅我。我们就这样一点点熟悉了。 三个月后,那一团越发繁茂的垂蔓里边,发出一种尖细又娇嫩的鸣叫。我猜到,是它们有了雏儿。我呢,决不掀开叶片往里看,连添食加水时也不睁大好奇的眼睛去惊动它们。过不多久,忽然有一个更小的脑袋从叶间探出来。哟,雏儿!正是这小家伙! 它小,就能轻易地由疏格的笼子里钻出来。瞧,多么像它的父母:红嘴红脚,灰蓝色的毛,只是后背还没生出珍珠似的圆圆的白点。它好肥,整个身子好像一个蓬松的球儿。 起先,这小家伙只在笼子四周活动,随后就在屋里飞来飞去,一会儿落在柜顶上,一会儿神气十足地站在书架上,啄着书脊上那些大文豪的名字,一会儿把灯绳撞得来回摇动,跟着逃到画框上去了。只要大鸟在笼里生气地叫一声,它就立即飞回笼里去。 我不管它。这样久了,打开窗子,它最多只在窗框上站一会儿,决不飞出去。 渐渐它胆子大了,就落在我的书桌上。它先是离我较远,见我不去伤害它,便一点点挨近,然后蹦到我的杯子上,俯下头来喝茶,再偏过脸瞧瞧我的反应。我只是微微一笑,依旧写东西。它就放开胆子跑到稿纸上,绕着我的笔尖蹦来蹦去,跳动的小红爪子在纸上发出嚓嚓的响声。 我不动声色地写,默默享受着这小家伙亲近的情意。这样,它完全放心了,索性用那涂了蜡似的小红嘴,嗒嗒地啄着我颤动的笔尖。我用手抚一抚它细腻的绒毛,它也不怕,反而友好地啄两下我的手指。 白天,它这样淘气地陪伴我;天色入暮,它就在父母再三的呼唤声中,飞向笼子,扭动滚圆的身子,挤开那些绿叶钻进去。 有一天,我伏案写作时,它居然落到我的肩上。我手中的笔不觉停了,生怕惊跑它。待一会儿,扭头看,这小家伙竟趴在我的肩头睡着了,银灰色的眼睑盖住眸子,小红爪子刚好被胸脯上长长的绒毛盖住。我轻轻抬一抬肩,它没醒,睡得好熟!还咂咂嘴,难道在做梦? 我笔尖一动,流泻下一时的感受: 信赖,往往创造出美好的境界。 --- **生字**:蔓 幽 悉 雏 哟 柜 享 陪 待 趴 脸 眸 **课前提示**:默读课文,想想「我」是怎样逐渐得到珍珠鸟的信赖的;找出描写珍珠鸟可爱的语句,体会「我」和珍珠鸟之间的情意。`;