From bd7af8e512dff44a5d77f09a298b4c705d486b9f Mon Sep 17 00:00:00 2001 From: wangxiaogang Date: Sun, 7 Jun 2026 15:35:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=85=A8=E9=9D=A2=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E4=B8=8E=E4=BF=AE=E5=A4=8D=20=E2=80=94=2051?= =?UTF-8?q?=E9=A1=B9=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 重大修复 ### 安全性修复 - 修复 notification read-all SQL 运算符优先级 bug - 修复 link_url XSS 漏洞(验证 URL 协议) - 修复 LIKE 通配符注入(转义 % 和 _) - 修复 uploadImageIfNeeded 运算符优先级 bug - 401 时清理所有本地存储(token/nickname/avatar/role/group) ### 数据完整性修复 - 修复公告已读状态共享问题(新建 notification_reads 表) - 修复群组账单数据缺失(退出群组时保留 group_id) - 修复群组解散后邀请码仍可加入 - 修复分类迁移未校验目标类型 - 修复群组公告缺少 group_id 必填校验 ### 功能修复 - 修复 category PUT /sort 路由冲突 - 修复 GROUP BY 不完整问题 - 修复 budget API 类型不匹配 - 修复 categoryStore.migrateCategory 不刷新本地数据 - 修复 groupStore 并发请求问题 - 修复账单页覆盖 store 数据 - 修复群组预算查询返回 0 而非 null - 修复通知页面 onShow 不刷新列表 - 修复统计页面不必要的重复请求 ### 用户体验优化 - 添加通知详情查看功能(弹窗) - 添加通知图片服务器上传 - 添加 Markdown 富文本工具栏 - 添加管理页面客户端认证检查 - 添加管理员公告页面下拉刷新 - 添加数据导出进度反馈 - 添加账单删除后筛选金额更新 - Numpad 添加安全区域 padding ### 代码质量提升 - 提取 requireAdmin 为共享中间件 - filter-panel 使用设计 token - 修复 getCurrentMonth 时区不一致 - 备份功能使用分页查询避免内存问题 - 管理后台仪表盘添加缓存 - 邀请码碰撞重试后报错 ## 新增文件 - server/src/middleware/requireAdmin.ts — 共享管理员权限中间件 - client/src/pages/admin/notifications/index.vue — 公告管理页面 ## 数据库变更 - 新增 notification_reads 表(公告已读记录) - 群组解散时保留 groups 记录和 transactions.group_id --- client/src/api/admin.ts | 4 + client/src/api/auth.ts | 1 + client/src/api/budget.ts | 2 +- client/src/api/notification.ts | 37 + client/src/components/Numpad/Numpad.vue | 32 +- .../TransactionItem/TransactionItem.vue | 2 +- client/src/pages.json | 10 +- client/src/pages/add/index.vue | 15 +- client/src/pages/admin/index.vue | 367 +------- .../src/pages/admin/notifications/index.vue | 822 ++++++++++++++++++ client/src/pages/bills/filter-panel.vue | 193 ++-- client/src/pages/bills/index.vue | 25 +- client/src/pages/index/index.vue | 5 +- client/src/pages/notifications/index.vue | 171 ++-- client/src/pages/profile-edit/index.vue | 2 +- client/src/pages/profile/index.vue | 5 +- client/src/pages/stats/index.vue | 21 +- client/src/stores/category.ts | 1 + client/src/stores/group.ts | 21 +- client/src/utils/app-ready.ts | 4 +- client/src/utils/format.ts | 5 +- client/src/utils/request.ts | 13 +- server/src/db/init.ts | 17 + server/src/db/schema.sql | 13 +- server/src/index.ts | 36 + server/src/middleware/requireAdmin.ts | 18 + server/src/routes/admin.ts | 61 +- server/src/routes/backup.ts | 22 +- server/src/routes/budget.ts | 13 +- server/src/routes/category.ts | 77 +- server/src/routes/group.ts | 95 +- server/src/routes/notification.ts | 246 +++++- server/src/routes/stats.ts | 7 +- server/src/routes/transaction.ts | 17 +- server/src/utils/backup.ts | 62 +- 35 files changed, 1683 insertions(+), 759 deletions(-) create mode 100644 client/src/pages/admin/notifications/index.vue create mode 100644 server/src/middleware/requireAdmin.ts diff --git a/client/src/api/admin.ts b/client/src/api/admin.ts index 7950be3..73e5a38 100644 --- a/client/src/api/admin.ts +++ b/client/src/api/admin.ts @@ -10,6 +10,10 @@ export interface Dashboard { growthPercent: number lastMonthTxCount: number lastMonthTxAmount: number + monthlyExpense?: number + monthlyIncome?: number + lastMonthExpense?: number + lastMonthIncome?: number } /** 用户信息(管理员视角) */ diff --git a/client/src/api/auth.ts b/client/src/api/auth.ts index fa6adc3..ea02ca2 100644 --- a/client/src/api/auth.ts +++ b/client/src/api/auth.ts @@ -6,6 +6,7 @@ export interface LoginResult { userId: number nickname: string avatar_url: string + role?: string } /** H5 演示登录 */ diff --git a/client/src/api/budget.ts b/client/src/api/budget.ts index 8cb4966..18725b3 100644 --- a/client/src/api/budget.ts +++ b/client/src/api/budget.ts @@ -15,5 +15,5 @@ export function getBudget(month?: string, group_id?: number | null) { /** 设置预算 */ export function setBudget(amount: number, month: string) { - return request<{ id: number }>({ url: '/budget', method: 'POST', data: { amount, month } }) + return request({ url: '/budget', method: 'POST', data: { amount, month } }) } diff --git a/client/src/api/notification.ts b/client/src/api/notification.ts index f8d9f51..dac0ef5 100644 --- a/client/src/api/notification.ts +++ b/client/src/api/notification.ts @@ -1,4 +1,5 @@ import { request } from '@/utils/request' +import { API_BASE } from '@/config' /** 通知 */ export interface Notification { @@ -64,6 +65,42 @@ export function markAllRead() { return request({ url: '/notifications/read-all', method: 'PUT' }) } +/** 上传通知图片(返回文件名) */ +export function uploadNotificationImage(filePath: string): Promise<{ image_url: string }> { + return new Promise((resolve, reject) => { + const token = uni.getStorageSync('xc:token') + uni.uploadFile({ + url: `${API_BASE}/notifications/upload-image`, + filePath, + name: 'file', + header: { + ...(token ? { Authorization: `Bearer ${token}` } : {}) + }, + success: (res) => { + try { + const data = JSON.parse(res.data) + if (data.code === 0) { + resolve(data.data) + } else { + reject(data) + } + } catch { + reject({ message: '上传失败' }) + } + }, + fail: () => reject({ message: '网络错误' }) + }) + }) +} + +/** 获取通知图片完整 URL */ +export function getNotificationImageUrl(filename: string): string { + if (!filename) return '' + // 如果已经是完整 URL 或 blob URL,直接返回 + if (filename.startsWith('http') || filename.startsWith('blob:')) return filename + return `${API_BASE}/notifications/image/${filename}` +} + /** 发布公告 */ export function publishNotification(data: PublishParams) { return request<{ id: number }>({ url: '/notifications', method: 'POST', data }) diff --git a/client/src/components/Numpad/Numpad.vue b/client/src/components/Numpad/Numpad.vue index 588397d..1b820ab 100644 --- a/client/src/components/Numpad/Numpad.vue +++ b/client/src/components/Numpad/Numpad.vue @@ -90,40 +90,42 @@ function onConfirm() { diff --git a/client/src/pages/admin/notifications/index.vue b/client/src/pages/admin/notifications/index.vue new file mode 100644 index 0000000..2964df7 --- /dev/null +++ b/client/src/pages/admin/notifications/index.vue @@ -0,0 +1,822 @@ +