第一期:家庭自学打卡系统

- 家长手机号登录 + 孩子设备一次性激活(长期会话)
- 周模板自动生成每日任务快照,支持拍照凭证直传对象存储
- 连击计算(空白日自动达成)、家长补卡带标记
- 家长端三标签:今日概览 / 任务模板 / 设备管理
- Docker 独立输出,经 Traefik 接入 jump.hn12301.net

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jacobxu666
2026-08-02 23:19:13 +08:00
co-authored by Claude Opus 4.8
commit af7a144d58
57 changed files with 8875 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
// 种子数据:徐家 + 家长账号 + 两个孩子。可重复执行,已有数据则跳过。
import dotenv from "dotenv";
dotenv.config({ path: [".env.local", ".env"] });
import bcrypt from "bcryptjs";
import { drizzle } from "drizzle-orm/mysql2";
import { eq } from "drizzle-orm";
import mysql from "mysql2/promise";
import { families, users } from "../src/db/schema";
const url = process.env.DATABASE_URL;
if (!url) throw new Error("缺少 DATABASE_URL");
async function main(url: string) {
const connection = await mysql.createConnection(url);
const db = drizzle(connection);
try {
const [existingParent] = await db
.select()
.from(users)
.where(eq(users.phone, "18684839400"))
.limit(1);
if (existingParent) {
console.log("种子数据已存在,跳过");
} else {
const [family] = await db
.insert(families)
.values({ name: "徐家" })
.$returningId();
const passwordHash = await bcrypt.hash("123456Aa", 10);
await db.insert(users).values({
familyId: family.id,
role: "parent",
displayName: "家长",
phone: "18684839400",
passwordHash,
});
await db.insert(users).values([
{ familyId: family.id, role: "child", displayName: "徐卿玮", sortOrder: 0 },
{ familyId: family.id, role: "child", displayName: "徐子惠", sortOrder: 1 },
]);
console.log("种子数据写入完成:徐家 + 家长 + 徐卿玮 + 徐子惠");
}
} finally {
await connection.end();
}
}
main(url);