Files
NextEdu/scripts/seed-grade5-chinese.ts
SpecialX 7387d70289 chore(scripts): update check-db-state and seed-grade5-chinese scripts
- Update scripts/check-db-state.mjs

- Update scripts/seed-grade5-chinese.ts
2026-07-07 16:22:54 +08:00

993 lines
38 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 五年级上册语文(人教版)第一单元 数据初始化脚本
*
* 功能:
* 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<string, string>;
}> {
console.log("📖 创建五年级上册语文教材与章节...");
const chapterMap: Record<string, string> = {};
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<string, string>): Promise<Record<string, string>> {
console.log("🧠 创建知识点...");
const map: Record<string, string> = {};
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<string, string>
): Promise<SeedQuestion[]> {
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<void> {
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* 珍珠鸟(略读课文)
真好!朋友送我一对珍珠鸟,放在一个简易的竹条编成的笼子里,笼内还有一卷干草,那是小鸟舒适又温暖的巢。
有人说,这是一种怕人的鸟。
我把鸟笼挂在窗前。那儿还有一大盆异常茂盛的法国吊兰。我便用吊兰长长的、串生着小绿叶的垂蔓蒙盖在鸟笼上。它们就像躲进幽深的丛林一样安全,从中传出的笛儿般又细又亮的叫声,也就格外轻松自在了。小鸟的影子就在这中间隐约闪动,看不完整,有时连笼子也看不出,却见它们可爱的鲜红小嘴从绿叶中伸出来。
我很少扒开垂蔓瞧它们,它们便渐渐敢伸出小脑袋瞅瞅我。我们就这样一点点熟悉了。
三个月后,那一团越发繁茂的垂蔓里边,发出一种尖细又娇嫩的鸣叫。我猜到,是它们有了雏儿。我呢,决不掀开叶片往里看,连添食加水时也不睁大好奇的眼睛去惊动它们。过不多久,忽然有一个更小的脑袋从叶间探出来。哟,雏儿!正是这小家伙!
它小,就能轻易地由疏格的笼子里钻出来。瞧,多么像它的父母:红嘴红脚,灰蓝色的毛,只是后背还没生出珍珠似的圆圆的白点。它好肥,整个身子好像一个蓬松的球儿。
起先,这小家伙只在笼子四周活动,随后就在屋里飞来飞去,一会儿落在柜顶上,一会儿神气十足地站在书架上,啄着书脊上那些大文豪的名字,一会儿把灯绳撞得来回摇动,跟着逃到画框上去了。只要大鸟在笼里生气地叫一声,它就立即飞回笼里去。
我不管它。这样久了,打开窗子,它最多只在窗框上站一会儿,决不飞出去。
渐渐它胆子大了,就落在我的书桌上。它先是离我较远,见我不去伤害它,便一点点挨近,然后蹦到我的杯子上,俯下头来喝茶,再偏过脸瞧瞧我的反应。我只是微微一笑,依旧写东西。它就放开胆子跑到稿纸上,绕着我的笔尖蹦来蹦去,跳动的小红爪子在纸上发出嚓嚓的响声。
我不动声色地写,默默享受着这小家伙亲近的情意。这样,它完全放心了,索性用那涂了蜡似的小红嘴,嗒嗒地啄着我颤动的笔尖。我用手抚一抚它细腻的绒毛,它也不怕,反而友好地啄两下我的手指。
白天,它这样淘气地陪伴我;天色入暮,它就在父母再三的呼唤声中,飞向笼子,扭动滚圆的身子,挤开那些绿叶钻进去。
有一天,我伏案写作时,它居然落到我的肩上。我手中的笔不觉停了,生怕惊跑它。待一会儿,扭头看,这小家伙竟趴在我的肩头睡着了,银灰色的眼睑盖住眸子,小红爪子刚好被胸脯上长长的绒毛盖住。我轻轻抬一抬肩,它没醒,睡得好熟!还咂咂嘴,难道在做梦?
我笔尖一动,流泻下一时的感受:
信赖,往往创造出美好的境界。
---
**生字**:蔓 幽 悉 雏 哟 柜 享 陪 待 趴 脸 眸
**课前提示**:默读课文,想想「我」是怎样逐渐得到珍珠鸟的信赖的;找出描写珍珠鸟可爱的语句,体会「我」和珍珠鸟之间的情意。`;