feat: 新增作息报时器功能并替换短链接前缀为/to/

- 新增作息报时器独立模块,包含完整的日程管理、语音播报和待机功能
- 短链接正式前缀由/l/改为/to/,解决手机小屏下与I、i、1字形混淆问题
- 保留旧前缀/l/、/L/、/I/、/i/兼容已分发的旧短链
- 新增短码大小写兜底匹配,仅唯一匹配时生效避免歧义
- 新增防爆紧急替换功能,可轮换首页鉴权并替换短链接
- 新增多个调试和集成测试脚本,保障数据安全和功能回归
This commit is contained in:
yangxiangyuan
2026-10-09 14:04:37 +08:00
parent ac475b6659
commit a3e8911deb
131 changed files with 7695 additions and 60 deletions
@@ -0,0 +1,56 @@
/**
* 恢复被误删的作息日程(一次性数据恢复脚本)
*
* 背景:调试脚本的"清理"步骤误删了用户真实创建的日程。
* 作息日程的删除是**软删除**(schedules.deleted = 1),数据行与 items_json 都还在,
* 因此可直接把 deleted 改回 0 完成恢复,无需重建。
*
* 用法:
* node dev_test_scripts/debug/tool_restore_deleted_schedule.js # 列出所有已软删的日程
* node dev_test_scripts/debug/tool_restore_deleted_schedule.js <scheduleId> # 恢复指定 id
*/
const path = require('path')
const Database = require('better-sqlite3')
const DB_PATH = path.join(process.cwd(), 'data', 'app_order_box_schedule_reminder.db')
const db = new Database(DB_PATH)
db.pragma('journal_mode = WAL')
const targetId = String(process.argv[2] || '').trim()
const safeParse = raw => {
try { return JSON.parse(raw || '[]') } catch { return [] }
}
if (!targetId) {
const rows = db.prepare(`
SELECT id, title, weekdays, items_json, created_at, updated_at
FROM schedules WHERE deleted = 1 ORDER BY datetime(created_at) DESC
`).all()
console.log(`已软删除的日程共 ${rows.length} 条:\n`)
rows.forEach(r => {
const items = safeParse(r.items_json)
console.log(`id=${r.id}`)
console.log(` 标题: ${r.title} 生效星期: [${r.weekdays}] 时刻数: ${items.length}`)
console.log(` 创建: ${r.created_at} 最近更新: ${r.updated_at}`)
console.log(` 时刻: ${items.map(i => i.time + ' ' + i.name).join(' | ')}`)
console.log('')
})
console.log('恢复某一条:node dev_test_scripts/debug/tool_restore_deleted_schedule.js <id>')
} else {
const row = db.prepare('SELECT id, title, deleted FROM schedules WHERE id = ?').get(targetId)
if (!row) {
console.log(`未找到 id=${targetId}`)
process.exit(1)
}
if (!row.deleted) {
console.log(`id=${targetId}「${row.title}」本来就是正常状态,无需恢复`)
process.exit(0)
}
db.prepare('UPDATE schedules SET deleted = 0 WHERE id = ?').run(targetId)
const after = db.prepare('SELECT id, title, deleted, items_json FROM schedules WHERE id = ?').get(targetId)
console.log(`已恢复:id=${after.id} 标题=${after.title} deleted=${after.deleted}`)
console.log(`时刻:${safeParse(after.items_json).map(i => i.time + ' ' + i.name).join(' | ')}`)
}
db.close()