Compare commits
2 Commits
e3014a9d4c
...
60fdd4ec2b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60fdd4ec2b | ||
|
|
88fd057155 |
15
src/migrations/001_add_repeat_interval.sql
Normal file
15
src/migrations/001_add_repeat_interval.sql
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
-- 迁移脚本:添加重复提醒相关字段
|
||||||
|
-- 执行时间:2026-02-03
|
||||||
|
-- 说明:为支持每天/每周重复提醒,添加 repeat_interval 字段
|
||||||
|
|
||||||
|
-- 添加 repeat_interval 字段(如果不存在)
|
||||||
|
-- SQLite 不支持 IF NOT EXISTS 语法,需要检查列是否存在
|
||||||
|
-- 这里使用 ALTER TABLE 添加列
|
||||||
|
|
||||||
|
ALTER TABLE events ADD COLUMN repeat_interval INTEGER DEFAULT NULL;
|
||||||
|
|
||||||
|
-- 更新现有数据:将 'none' 以外的旧 repeat_type 保持不变
|
||||||
|
-- 注意:此迁移假设现有数据只有 'yearly', 'monthly', 'none' 三种值
|
||||||
|
|
||||||
|
-- 验证迁移
|
||||||
|
-- SELECT id, repeat_type, repeat_interval FROM events WHERE type = 'reminder';
|
||||||
9
src/migrations/002_add_next_reminder_date.sql
Normal file
9
src/migrations/002_add_next_reminder_date.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- 迁移脚本:添加下一次提醒日期字段
|
||||||
|
-- 执行时间:2026-02-03
|
||||||
|
-- 说明:支持重复提醒的双时间机制(当前提醒时间 + 下一次提醒时间)
|
||||||
|
|
||||||
|
-- 添加 next_reminder_date 字段
|
||||||
|
ALTER TABLE events ADD COLUMN next_reminder_date TEXT DEFAULT NULL;
|
||||||
|
|
||||||
|
-- 验证迁移
|
||||||
|
-- SELECT id, title, date, repeat_type, next_reminder_date FROM events WHERE type = 'reminder';
|
||||||
@ -6,14 +6,17 @@ import { asyncHandler } from '../middleware/errorHandler';
|
|||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
// Validation schemas
|
// Validation schemas - date can be empty string for reminders
|
||||||
|
// repeat_type: 'daily'每天, 'weekly'每周, 'monthly'每月, 'yearly'每年, 'none'不重复
|
||||||
const createEventSchema = z.object({
|
const createEventSchema = z.object({
|
||||||
type: z.enum(['anniversary', 'reminder']),
|
type: z.enum(['anniversary', 'reminder']),
|
||||||
title: z.string().min(1, 'Title is required').max(200),
|
title: z.string().min(1, 'Title is required').max(200),
|
||||||
content: z.string().optional(),
|
content: z.string().optional(),
|
||||||
date: z.string().datetime(), // ISO datetime string
|
date: z.string().optional(), // Can be empty for reminders
|
||||||
is_lunar: z.boolean().default(false),
|
is_lunar: z.boolean().default(false),
|
||||||
repeat_type: z.enum(['yearly', 'monthly', 'none']).default('none'),
|
repeat_type: z.enum(['daily', 'weekly', 'monthly', 'yearly', 'none']).default('none'),
|
||||||
|
repeat_interval: z.number().int().positive().optional().nullable(), // 自定义间隔(周数)
|
||||||
|
next_reminder_date: z.string().optional().nullable(), // 下一次提醒日期
|
||||||
is_holiday: z.boolean().default(false),
|
is_holiday: z.boolean().default(false),
|
||||||
is_completed: z.boolean().default(false),
|
is_completed: z.boolean().default(false),
|
||||||
});
|
});
|
||||||
@ -33,6 +36,8 @@ interface EventRow {
|
|||||||
date: string;
|
date: string;
|
||||||
is_lunar: number;
|
is_lunar: number;
|
||||||
repeat_type: string;
|
repeat_type: string;
|
||||||
|
repeat_interval: number | null; // 周数间隔(仅 weekly 类型使用)
|
||||||
|
next_reminder_date: string | null; // 下一次提醒日期
|
||||||
is_holiday: number;
|
is_holiday: number;
|
||||||
is_completed: number;
|
is_completed: number;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
@ -49,6 +54,8 @@ function formatEvent(event: EventRow) {
|
|||||||
date: event.date,
|
date: event.date,
|
||||||
is_lunar: Boolean(event.is_lunar),
|
is_lunar: Boolean(event.is_lunar),
|
||||||
repeat_type: event.repeat_type,
|
repeat_type: event.repeat_type,
|
||||||
|
repeat_interval: event.repeat_interval,
|
||||||
|
next_reminder_date: event.next_reminder_date,
|
||||||
is_holiday: Boolean(event.is_holiday),
|
is_holiday: Boolean(event.is_holiday),
|
||||||
is_completed: Boolean(event.is_completed),
|
is_completed: Boolean(event.is_completed),
|
||||||
created_at: event.created_at,
|
created_at: event.created_at,
|
||||||
@ -104,12 +111,17 @@ router.post(
|
|||||||
const data = createEventSchema.parse(req.body);
|
const data = createEventSchema.parse(req.body);
|
||||||
|
|
||||||
const eventId = crypto.randomUUID();
|
const eventId = crypto.randomUUID();
|
||||||
const dateValue = new Date(data.date).toISOString();
|
// Handle empty date - store as null for reminders without specific time
|
||||||
|
// 直接存储前端发送的原始日期字符串,不做 toISOString 转换(避免时区问题)
|
||||||
|
const dateValue = data.date || null;
|
||||||
|
const repeatInterval = data.repeat_type === 'weekly' ? (data.repeat_interval || 1) : null;
|
||||||
|
// 如果前端传了 next_reminder_date 就使用它,否则根据 repeat_type 计算
|
||||||
|
const nextReminderDate = data.next_reminder_date || null;
|
||||||
|
|
||||||
await db.execute({
|
await db.execute({
|
||||||
sql: `INSERT INTO events (id, user_id, type, title, content, date, is_lunar, repeat_type, is_holiday, is_completed, created_at, updated_at)
|
sql: `INSERT INTO events (id, user_id, type, title, content, date, is_lunar, repeat_type, repeat_interval, next_reminder_date, is_holiday, is_completed, created_at, updated_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0, datetime('now'), datetime('now'))`,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, datetime('now'), datetime('now'))`,
|
||||||
args: [eventId, req.user!.userId, data.type, data.title, data.content || null, dateValue, data.is_lunar ? 1 : 0, data.repeat_type, data.is_holiday ? 1 : 0],
|
args: [eventId, req.user!.userId, data.type, data.title, data.content || null, dateValue, data.is_lunar ? 1 : 0, data.repeat_type, repeatInterval, nextReminderDate, data.is_holiday ? 1 : 0],
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await db.execute({
|
const result = await db.execute({
|
||||||
@ -154,9 +166,10 @@ router.put(
|
|||||||
updates.push('content = ?');
|
updates.push('content = ?');
|
||||||
args.push(data.content);
|
args.push(data.content);
|
||||||
}
|
}
|
||||||
if (data.date) {
|
if (data.date !== undefined) {
|
||||||
updates.push('date = ?');
|
updates.push('date = ?');
|
||||||
args.push(new Date(data.date).toISOString());
|
// 直接存储原始字符串,不做时区转换
|
||||||
|
args.push(data.date || null);
|
||||||
}
|
}
|
||||||
if (data.is_lunar !== undefined) {
|
if (data.is_lunar !== undefined) {
|
||||||
updates.push('is_lunar = ?');
|
updates.push('is_lunar = ?');
|
||||||
@ -165,6 +178,18 @@ router.put(
|
|||||||
if (data.repeat_type) {
|
if (data.repeat_type) {
|
||||||
updates.push('repeat_type = ?');
|
updates.push('repeat_type = ?');
|
||||||
args.push(data.repeat_type);
|
args.push(data.repeat_type);
|
||||||
|
// 如果设置为 weekly 重复,设置默认间隔
|
||||||
|
if (data.repeat_type === 'weekly') {
|
||||||
|
updates.push('repeat_interval = ?');
|
||||||
|
args.push(data.repeat_interval || 1);
|
||||||
|
} else {
|
||||||
|
updates.push('repeat_interval = NULL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理 next_reminder_date 更新
|
||||||
|
if (data.next_reminder_date !== undefined) {
|
||||||
|
updates.push('next_reminder_date = ?');
|
||||||
|
args.push(data.next_reminder_date);
|
||||||
}
|
}
|
||||||
if (data.is_holiday !== undefined) {
|
if (data.is_holiday !== undefined) {
|
||||||
updates.push('is_holiday = ?');
|
updates.push('is_holiday = ?');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user