fix: 修复 reminder_times 处理和 UTC 时间存储
- server: 处理 reminder_times 更新时支持 null 值 - client: 统一使用 UTC 格式存储日期时间 - client: 修复 calculateNextDueDate 使用 UTC 时间计算 - client: 修复 calculateReminderTimes 使用 UTC 时间计算 - client: 修复 getReminderValueFromTimes 反推提醒选项 - client: 重写 createNextRecurringEventData 根据新日期重新计算 reminder_times - 解决用户设置"14:00"显示"22:00"等时区问题 - 解决重复提醒自动创建时继承 reminder_times 问题 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
96da6b6e42
commit
15918f163c
@ -36,10 +36,13 @@ model Event {
|
|||||||
content String? // Only for reminders
|
content String? // Only for reminders
|
||||||
date DateTime // For anniversaries: the date; For reminders: the reminder date
|
date DateTime // For anniversaries: the date; For reminders: the reminder date
|
||||||
is_lunar Boolean @default(false)
|
is_lunar Boolean @default(false)
|
||||||
repeat_type String @default("none") // 'yearly' | 'monthly' | 'none' (String for SQLite)
|
repeat_type String @default("none") // 'yearly' | 'monthly' | 'none' (String for SQLite)
|
||||||
is_holiday Boolean @default(false) // Only for anniversaries
|
repeat_interval Int? // Weekly interval (only for weekly type)
|
||||||
is_completed Boolean @default(false) // Only for reminders
|
is_holiday Boolean @default(false) // Only for anniversaries
|
||||||
|
is_completed Boolean @default(false) // Only for reminders
|
||||||
priority String @default("none") // 'none' | 'red' | 'green' | 'yellow' (String for SQLite)
|
priority String @default("none") // 'none' | 'red' | 'green' | 'yellow' (String for SQLite)
|
||||||
|
next_reminder_date DateTime? // Next reminder date (calculated)
|
||||||
|
reminder_times String? // JSON array of reminder timestamps (ISO strings)
|
||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
updated_at DateTime @updatedAt
|
updated_at DateTime @updatedAt
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import db, { dbPath } from '../lib/db';
|
|||||||
import { authenticateToken, AuthenticatedRequest } from '../middleware/auth';
|
import { authenticateToken, AuthenticatedRequest } from '../middleware/auth';
|
||||||
import { asyncHandler } from '../middleware/errorHandler';
|
import { asyncHandler } from '../middleware/errorHandler';
|
||||||
|
|
||||||
// Initialize database: Add priority column if it doesn't exist
|
// Initialize database: Add columns if they don't exist
|
||||||
const initDb = async () => {
|
const initDb = async () => {
|
||||||
try {
|
try {
|
||||||
await db.execute({
|
await db.execute({
|
||||||
@ -18,6 +18,19 @@ const initDb = async () => {
|
|||||||
console.error('Error adding priority column:', error.message);
|
console.error('Error adding priority column:', error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.execute({
|
||||||
|
sql: `ALTER TABLE events ADD COLUMN reminder_times TEXT DEFAULT NULL`,
|
||||||
|
});
|
||||||
|
console.log('✓ Reminder times column added to events table');
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.message?.includes('duplicate column name') || error.message?.includes('already exists')) {
|
||||||
|
console.log('✓ Reminder times column already exists');
|
||||||
|
} else {
|
||||||
|
console.error('Error adding reminder_times column:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Run initialization
|
// Run initialization
|
||||||
@ -31,7 +44,7 @@ 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().optional(), // Can be empty for reminders
|
date: z.string().optional(), // Can be empty string for reminders
|
||||||
is_lunar: z.boolean().default(false),
|
is_lunar: z.boolean().default(false),
|
||||||
repeat_type: z.enum(['daily', 'weekly', 'monthly', 'yearly', 'none']).default('none'),
|
repeat_type: z.enum(['daily', 'weekly', 'monthly', 'yearly', 'none']).default('none'),
|
||||||
repeat_interval: z.number().int().positive().optional().nullable(), // 自定义间隔(周数)
|
repeat_interval: z.number().int().positive().optional().nullable(), // 自定义间隔(周数)
|
||||||
@ -39,6 +52,7 @@ const createEventSchema = z.object({
|
|||||||
is_holiday: z.boolean().default(false),
|
is_holiday: z.boolean().default(false),
|
||||||
is_completed: z.boolean().default(false),
|
is_completed: z.boolean().default(false),
|
||||||
priority: z.enum(['none', 'red', 'green', 'yellow']).default('none'),
|
priority: z.enum(['none', 'red', 'green', 'yellow']).default('none'),
|
||||||
|
reminder_times: z.array(z.string()).optional(), // 提醒时间点数组(JSON)
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateEventSchema = createEventSchema.partial();
|
const updateEventSchema = createEventSchema.partial();
|
||||||
@ -61,6 +75,7 @@ interface EventRow {
|
|||||||
is_holiday: number;
|
is_holiday: number;
|
||||||
is_completed: number;
|
is_completed: number;
|
||||||
priority: string; // 优先级:none, red, green, yellow
|
priority: string; // 优先级:none, red, green, yellow
|
||||||
|
reminder_times: string | null; // 提醒时间点数组(JSON 字符串)
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
@ -80,6 +95,7 @@ function formatEvent(event: EventRow) {
|
|||||||
is_holiday: Boolean(event.is_holiday),
|
is_holiday: Boolean(event.is_holiday),
|
||||||
is_completed: Boolean(event.is_completed),
|
is_completed: Boolean(event.is_completed),
|
||||||
priority: event.priority as 'none' | 'red' | 'green' | 'yellow',
|
priority: event.priority as 'none' | 'red' | 'green' | 'yellow',
|
||||||
|
reminder_times: event.reminder_times ? JSON.parse(event.reminder_times) : undefined,
|
||||||
created_at: event.created_at,
|
created_at: event.created_at,
|
||||||
updated_at: event.updated_at,
|
updated_at: event.updated_at,
|
||||||
};
|
};
|
||||||
@ -139,11 +155,13 @@ router.post(
|
|||||||
const repeatInterval = data.repeat_type === 'weekly' ? (data.repeat_interval || 1) : null;
|
const repeatInterval = data.repeat_type === 'weekly' ? (data.repeat_interval || 1) : null;
|
||||||
// 如果前端传了 next_reminder_date 就使用它,否则根据 repeat_type 计算
|
// 如果前端传了 next_reminder_date 就使用它,否则根据 repeat_type 计算
|
||||||
const nextReminderDate = data.next_reminder_date || null;
|
const nextReminderDate = data.next_reminder_date || null;
|
||||||
|
// 存储 reminder_times 为 JSON 字符串
|
||||||
|
const reminderTimesValue = data.reminder_times ? JSON.stringify(data.reminder_times) : null;
|
||||||
|
|
||||||
await db.execute({
|
await db.execute({
|
||||||
sql: `INSERT INTO events (id, user_id, type, title, content, date, is_lunar, repeat_type, repeat_interval, next_reminder_date, is_holiday, is_completed, priority, 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, priority, reminder_times, 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, repeatInterval, nextReminderDate, data.is_holiday ? 1 : 0, data.priority],
|
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, data.priority, reminderTimesValue],
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await db.execute({
|
const result = await db.execute({
|
||||||
@ -228,6 +246,12 @@ router.put(
|
|||||||
args.push(data.priority);
|
args.push(data.priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理 reminder_times 更新
|
||||||
|
if (data.reminder_times !== undefined) {
|
||||||
|
updates.push('reminder_times = ?');
|
||||||
|
args.push(data.reminder_times === null ? null : JSON.stringify(data.reminder_times));
|
||||||
|
}
|
||||||
|
|
||||||
if (updates.length > 0) {
|
if (updates.length > 0) {
|
||||||
updates.push('updated_at = datetime(\'now\')');
|
updates.push('updated_at = datetime(\'now\')');
|
||||||
args.push(req.params.id);
|
args.push(req.params.id);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user