feat(V0.2): 打卡 / 日历 / 清单模块
- 数据: Habit/CheckIn/CheckList/CheckListItem/CheckListTemplate + 迁移 + 重新生成 client
- 后端: server/checkin/period.js 周期计算(日/周/月/年, 周一起始, Asia/Shanghai)
/api/checkin/* 打卡项CRUD/打卡/撤销/补打/某日详情/月历
/api/checklists/* 清单与检查项CRUD/勾选/全选/复制/模板(系统预置出差·回老家)
- 前端: 底部新增「打卡」Tab -> /checkin, 页内三视图
环形进度 RingProgress + 完成特效(粒子/光晕/触感, 可关闭)
打卡视图: 待办列表/乐观更新/撤销/新建编辑
日历视图: 月历网格 + 今天·昨天·明天 + 日期标记/完成率
清单视图: 场景清单/勾选进度条/模板生成/另存模板/归档
- 文档: docs/V0.2/需求文档.md 补充实现约定(附录B)与接口清单
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE `Habit` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`userId` INTEGER NOT NULL,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`period` VARCHAR(191) NOT NULL DEFAULT 'day',
|
||||
`target` INTEGER NOT NULL DEFAULT 1,
|
||||
`color` VARCHAR(191) NOT NULL DEFAULT '#1677ff',
|
||||
`icon` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`note` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`archived` BOOLEAN NOT NULL DEFAULT false,
|
||||
`sortOrder` INTEGER NOT NULL DEFAULT 0,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `Habit_userId_archived_sortOrder_idx`(`userId`, `archived`, `sortOrder`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `CheckIn` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`habitId` INTEGER NOT NULL,
|
||||
`userId` INTEGER NOT NULL,
|
||||
`date` VARCHAR(10) NOT NULL,
|
||||
`seq` INTEGER NOT NULL DEFAULT 1,
|
||||
`note` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`source` VARCHAR(191) NOT NULL DEFAULT 'user',
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
UNIQUE INDEX `CheckIn_habitId_date_seq_key`(`habitId`, `date`, `seq`),
|
||||
INDEX `CheckIn_userId_date_idx`(`userId`, `date`),
|
||||
INDEX `CheckIn_habitId_date_idx`(`habitId`, `date`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `CheckList` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`userId` INTEGER NOT NULL,
|
||||
`title` VARCHAR(191) NOT NULL,
|
||||
`scene` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`note` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`archived` BOOLEAN NOT NULL DEFAULT false,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `CheckList_userId_archived_idx`(`userId`, `archived`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `CheckListItem` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`listId` INTEGER NOT NULL,
|
||||
`title` VARCHAR(191) NOT NULL,
|
||||
`note` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`done` BOOLEAN NOT NULL DEFAULT false,
|
||||
`doneAt` DATETIME(3) NULL,
|
||||
`sortOrder` INTEGER NOT NULL DEFAULT 0,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `CheckListItem_listId_sortOrder_idx`(`listId`, `sortOrder`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `CheckListTemplate` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`userId` INTEGER NULL,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`scene` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`items` JSON NOT NULL,
|
||||
`builtin` BOOLEAN NOT NULL DEFAULT false,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `CheckListTemplate_userId_builtin_idx`(`userId`, `builtin`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Habit` ADD CONSTRAINT `Habit_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `CheckIn` ADD CONSTRAINT `CheckIn_habitId_fkey` FOREIGN KEY (`habitId`) REFERENCES `Habit`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `CheckIn` ADD CONSTRAINT `CheckIn_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `CheckList` ADD CONSTRAINT `CheckList_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `CheckListItem` ADD CONSTRAINT `CheckListItem_listId_fkey` FOREIGN KEY (`listId`) REFERENCES `CheckList`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `CheckListTemplate` ADD CONSTRAINT `CheckListTemplate_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -19,6 +19,12 @@ model User {
|
||||
password String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// V0.2 打卡 / 清单: 用户删除时级联清理
|
||||
habits Habit[]
|
||||
checkIns CheckIn[]
|
||||
checkLists CheckList[]
|
||||
templates CheckListTemplate[]
|
||||
}
|
||||
|
||||
model Article {
|
||||
@@ -281,4 +287,91 @@ model MihomeEnvHour {
|
||||
@@index([deviceId, hour])
|
||||
}
|
||||
|
||||
// ==================== 打卡 / 日历 / 清单 (V0.2) ====================
|
||||
|
||||
// 打卡项: 周期性目标 (每天 / 每周 / 每月 / 每年 n 次)
|
||||
model Habit {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
name String
|
||||
period String @default("day") // day | week | month | year
|
||||
target Int @default(1) // 周期内目标次数 n (>=1)
|
||||
color String @default("#1677ff")
|
||||
icon String @default("") // emoji 或图标名, 预留
|
||||
note String @default("")
|
||||
archived Boolean @default(false) // 归档: 不出现在今日待办, 历史保留
|
||||
sortOrder Int @default(0)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
records CheckIn[]
|
||||
|
||||
@@index([userId, archived, sortOrder])
|
||||
}
|
||||
|
||||
// 打卡记录: 一次打卡 = 一条; 同日多次 => seq 递增 (1..n, 超额继续累加)
|
||||
model CheckIn {
|
||||
id Int @id @default(autoincrement())
|
||||
habitId Int
|
||||
habit Habit @relation(fields: [habitId], references: [id], onDelete: Cascade)
|
||||
userId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
date String @db.VarChar(10) // 本地日历日 "YYYY-MM-DD" (Asia/Shanghai)
|
||||
seq Int @default(1) // 当日第几次
|
||||
note String @default("")
|
||||
source String @default("user") // user | backfill (补打)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([habitId, date, seq])
|
||||
@@index([userId, date])
|
||||
@@index([habitId, date])
|
||||
}
|
||||
|
||||
// 检查清单: 出差 / 回老家 等场景的一次性清单
|
||||
model CheckList {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
title String
|
||||
scene String @default("") // 出差 | 回老家 | 自定义
|
||||
note String @default("")
|
||||
archived Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
items CheckListItem[]
|
||||
|
||||
@@index([userId, archived])
|
||||
}
|
||||
|
||||
// 检查项: 勾选即完成
|
||||
model CheckListItem {
|
||||
id Int @id @default(autoincrement())
|
||||
listId Int
|
||||
list CheckList @relation(fields: [listId], references: [id], onDelete: Cascade)
|
||||
title String
|
||||
note String @default("")
|
||||
done Boolean @default(false)
|
||||
doneAt DateTime?
|
||||
sortOrder Int @default(0)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([listId, sortOrder])
|
||||
}
|
||||
|
||||
// 清单模板: builtin = 系统预置 (userId 为空); 否则为用户自定义
|
||||
model CheckListTemplate {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int?
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
name String
|
||||
scene String @default("")
|
||||
items Json // [{ title, note }]
|
||||
builtin Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([userId, builtin])
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user