From e49b6b7b810fd442eba20d92b3baab162fa75f46 Mon Sep 17 00:00:00 2001 From: rosercode Date: Fri, 18 Sep 2026 16:18:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E8=B4=A6=E5=8D=95=E5=AF=BC=E5=85=A5=E6=8F=90=E9=86=92=E4=B8=8E?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E7=9B=B4=E8=BE=BE=E5=AF=BC=E5=85=A5=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增通知配置、渠道、发送日志三张表(MySQL/SQLite 双建表脚本)及实体、Mapper、Service、Controller - 支持邮箱(SMTP)与 Server 酱两种推送渠道,抽象 NotificationSender 工厂统一发送 - 新增 NotificationScheduler 定时任务,按每天/每周/每月周期检查并推送账单导入提醒 - 新增全局通知链接地址配置(/api/app/notification-url),随提醒一并下发 - 前端系统设置页新增「定时通知」面板,支持周期配置、渠道增删改测、立即发送与发送日志 - 新增 /bill-import 直达页,供提醒链接一键打开账单上传弹窗 - 登录支持 redirect 回跳,仅允许站内路径以防开放重定向 --- build.gradle | 1 + frontend/src/api/notification.ts | 105 ++++ frontend/src/components/AiConfigPanel.vue | 2 +- .../src/components/DatabaseConfigPanel.vue | 2 +- .../components/NotificationConfigPanel.vue | 548 ++++++++++++++++++ .../components/RegistrationConfigPanel.vue | 2 +- frontend/src/locales/messages.ts | 68 +++ frontend/src/router/index.ts | 9 +- frontend/src/views/BillImport.vue | 37 ++ frontend/src/views/BillImportModal.vue | 508 +--------------- frontend/src/views/BillImportPanel.vue | 511 ++++++++++++++++ frontend/src/views/Login.vue | 15 +- frontend/src/views/Settings.vue | 13 +- .../config/NotificationSchedulingConfig.java | 16 + .../finance/config/app/AppConfig.java | 3 + .../controller/AppConfigController.java | 58 ++ .../controller/NotificationController.java | 182 ++++++ .../dto/NotificationChannelRequest.java | 26 + .../dto/NotificationConfigRequest.java | 27 + .../finance/dto/NotificationUrlRequest.java | 15 + .../finance/entity/NotificationChannel.java | 43 ++ .../finance/entity/NotificationConfig.java | 49 ++ .../finance/entity/NotificationLog.java | 40 ++ .../enums/NotificationChannelType.java | 15 + .../finance/enums/NotificationFrequency.java | 18 + .../mapper/NotificationChannelMapper.java | 14 + .../mapper/NotificationConfigMapper.java | 14 + .../finance/mapper/NotificationLogMapper.java | 14 + .../AbstractHttpNotificationSender.java | 160 +++++ .../notification/EmailNotificationSender.java | 147 +++++ .../notification/NotificationSender.java | 32 + .../NotificationSenderFactory.java | 52 ++ .../ServerChanNotificationSender.java | 73 +++ .../scheduler/NotificationScheduler.java | 169 ++++++ .../service/NotificationChannelService.java | 66 +++ .../service/NotificationConfigService.java | 37 ++ .../finance/service/NotificationService.java | 62 ++ .../impl/NotificationChannelServiceImpl.java | 233 ++++++++ .../impl/NotificationConfigServiceImpl.java | 166 ++++++ .../service/impl/NotificationServiceImpl.java | 234 ++++++++ .../finance/vo/NotificationChannelVO.java | 39 ++ .../finance/vo/NotificationConfigVO.java | 32 + .../finance/vo/NotificationLogVO.java | 32 + src/main/resources/sql/init.sql | 40 ++ src/main/resources/sql/schema-sqlite.sql | 53 ++ 45 files changed, 3469 insertions(+), 513 deletions(-) create mode 100644 frontend/src/api/notification.ts create mode 100644 frontend/src/components/NotificationConfigPanel.vue create mode 100644 frontend/src/views/BillImport.vue create mode 100644 frontend/src/views/BillImportPanel.vue create mode 100644 src/main/java/cn/rosercode/finance/config/NotificationSchedulingConfig.java create mode 100644 src/main/java/cn/rosercode/finance/controller/AppConfigController.java create mode 100644 src/main/java/cn/rosercode/finance/controller/NotificationController.java create mode 100644 src/main/java/cn/rosercode/finance/dto/NotificationChannelRequest.java create mode 100644 src/main/java/cn/rosercode/finance/dto/NotificationConfigRequest.java create mode 100644 src/main/java/cn/rosercode/finance/dto/NotificationUrlRequest.java create mode 100644 src/main/java/cn/rosercode/finance/entity/NotificationChannel.java create mode 100644 src/main/java/cn/rosercode/finance/entity/NotificationConfig.java create mode 100644 src/main/java/cn/rosercode/finance/entity/NotificationLog.java create mode 100644 src/main/java/cn/rosercode/finance/enums/NotificationChannelType.java create mode 100644 src/main/java/cn/rosercode/finance/enums/NotificationFrequency.java create mode 100644 src/main/java/cn/rosercode/finance/mapper/NotificationChannelMapper.java create mode 100644 src/main/java/cn/rosercode/finance/mapper/NotificationConfigMapper.java create mode 100644 src/main/java/cn/rosercode/finance/mapper/NotificationLogMapper.java create mode 100644 src/main/java/cn/rosercode/finance/notification/AbstractHttpNotificationSender.java create mode 100644 src/main/java/cn/rosercode/finance/notification/EmailNotificationSender.java create mode 100644 src/main/java/cn/rosercode/finance/notification/NotificationSender.java create mode 100644 src/main/java/cn/rosercode/finance/notification/NotificationSenderFactory.java create mode 100644 src/main/java/cn/rosercode/finance/notification/ServerChanNotificationSender.java create mode 100644 src/main/java/cn/rosercode/finance/scheduler/NotificationScheduler.java create mode 100644 src/main/java/cn/rosercode/finance/service/NotificationChannelService.java create mode 100644 src/main/java/cn/rosercode/finance/service/NotificationConfigService.java create mode 100644 src/main/java/cn/rosercode/finance/service/NotificationService.java create mode 100644 src/main/java/cn/rosercode/finance/service/impl/NotificationChannelServiceImpl.java create mode 100644 src/main/java/cn/rosercode/finance/service/impl/NotificationConfigServiceImpl.java create mode 100644 src/main/java/cn/rosercode/finance/service/impl/NotificationServiceImpl.java create mode 100644 src/main/java/cn/rosercode/finance/vo/NotificationChannelVO.java create mode 100644 src/main/java/cn/rosercode/finance/vo/NotificationConfigVO.java create mode 100644 src/main/java/cn/rosercode/finance/vo/NotificationLogVO.java diff --git a/build.gradle b/build.gradle index bd20214..7aee8c4 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-mail' implementation 'org.springframework.security:spring-security-crypto' implementation 'com.baomidou:mybatis-plus-spring-boot3-starter:3.5.12' diff --git a/frontend/src/api/notification.ts b/frontend/src/api/notification.ts new file mode 100644 index 0000000..259160d --- /dev/null +++ b/frontend/src/api/notification.ts @@ -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 + 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 +}) { + return request.post('/notification/channels', data) +} + +/** 修改通知渠道,邮箱授权码留空表示不修改 */ +export function updateNotificationChannel(id: number, data: { + type: string + name: string + enabled: boolean + config: Record +}) { + 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 }) +} diff --git a/frontend/src/components/AiConfigPanel.vue b/frontend/src/components/AiConfigPanel.vue index 9b8a034..feadb86 100644 --- a/frontend/src/components/AiConfigPanel.vue +++ b/frontend/src/components/AiConfigPanel.vue @@ -146,7 +146,7 @@ onMounted(loadConfig) - + - +