feat: 新增定时账单导入提醒与通知直达导入页

- 新增通知配置、渠道、发送日志三张表(MySQL/SQLite 双建表脚本)及实体、Mapper、Service、Controller
- 支持邮箱(SMTP)与 Server 酱两种推送渠道,抽象 NotificationSender 工厂统一发送
- 新增 NotificationScheduler 定时任务,按每天/每周/每月周期检查并推送账单导入提醒
- 新增全局通知链接地址配置(/api/app/notification-url),随提醒一并下发
- 前端系统设置页新增「定时通知」面板,支持周期配置、渠道增删改测、立即发送与发送日志
- 新增 /bill-import 直达页,供提醒链接一键打开账单上传弹窗
- 登录支持 redirect 回跳,仅允许站内路径以防开放重定向
This commit is contained in:
2026-09-18 16:47:09 +08:00
parent c1f5d02564
commit e49b6b7b81
45 changed files with 3469 additions and 513 deletions
+105
View File
@@ -0,0 +1,105 @@
// 定时通知相关 API 接口:提醒周期配置、推送渠道管理与发送日志
import request from './request'
/** 通知周期配置 */
export interface NotificationConfig {
enabled: boolean
frequency: string
dayOfMonth: number
dayOfWeek: number
sendTime: string
lastSentAt: string | null
}
/** 通知渠道 */
export interface NotificationChannel {
id: number
type: string
name: string
enabled: boolean
config: Record<string, any>
hasPassword: boolean
createdAt: string
updatedAt: string
}
/** 通知发送日志 */
export interface NotificationLog {
id: number
channelId: number | null
channelType: string | null
success: boolean
message: string | null
createdAt: string
}
/** 获取当前用户的通知配置 */
export function getNotificationConfig() {
return request.get('/notification/config')
}
/** 保存当前用户的通知配置 */
export function saveNotificationConfig(data: {
enabled: boolean
frequency: string
dayOfMonth: number
dayOfWeek: number
sendTime: string
}) {
return request.put('/notification/config', data)
}
/** 查询通知渠道列表 */
export function listNotificationChannels() {
return request.get('/notification/channels')
}
/** 新增通知渠道 */
export function createNotificationChannel(data: {
type: string
name: string
enabled: boolean
config: Record<string, any>
}) {
return request.post('/notification/channels', data)
}
/** 修改通知渠道,邮箱授权码留空表示不修改 */
export function updateNotificationChannel(id: number, data: {
type: string
name: string
enabled: boolean
config: Record<string, any>
}) {
return request.put(`/notification/channels/${id}`, data)
}
/** 删除通知渠道 */
export function deleteNotificationChannel(id: number) {
return request.delete(`/notification/channels/${id}`)
}
/** 向指定渠道发送测试消息(耗时取决于渠道,放宽超时) */
export function testNotificationChannel(id: number) {
return request.post(`/notification/channels/${id}/test`, {}, { timeout: 60000 })
}
/** 立即向所有已启用渠道发送账单导入提醒 */
export function sendNotificationNow() {
return request.post('/notification/send-now', {}, { timeout: 60000 })
}
/** 查询最近的发送日志 */
export function listNotificationLogs(limit = 20) {
return request.get('/notification/logs', { params: { limit } })
}
/** 获取通知链接地址(全局应用配置) */
export function getNotificationUrl() {
return request.get('/app/notification-url')
}
/** 保存通知链接地址(全局应用配置) */
export function updateNotificationUrl(url: string) {
return request.put('/app/notification-url', { url })
}