feat: 迭代三大模块 + 全系统 Bug 修复

新功能:
- 账单筛选统计:多维度筛选面板 + 保存方案 + 结果统计
- 通知公告:系统公告/群组通知/个人提醒 + 通知中心页面
- 管理员后台:数据看板 + 用户管理 + 发布公告

Bug 修复:
- 统计页 fetchPrevMonthData/fetchMaxSingle 添加 group_id
- 群组视图添加 t.group_id 过滤,防止私人数据泄露
- 通知列表添加群组通知条件
- 通知标记已读添加错误处理
- 管理员 API 添加 affectedRows 检查
- 筛选面板金额为 0 时正确回填
- profile onShow 补充 userStore.fetchUserInfo
- 全系统样式修复(overflow/box-sizing)
This commit is contained in:
wangxiaogang
2026-06-06 17:55:29 +08:00
parent bc6eca1e9b
commit 83571de723
35 changed files with 2255 additions and 37 deletions

View File

@@ -11,6 +11,14 @@ async function columnExists(conn: mysql.Connection, table: string, column: strin
return (rows as any[])[0].cnt > 0
}
async function tableExists(conn: mysql.Connection, table: string): Promise<boolean> {
const [rows] = await conn.query(
`SELECT COUNT(*) as cnt FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`,
[table]
)
return (rows as any[])[0].cnt > 0
}
async function runMigrations(conn: mysql.Connection) {
console.log('[DB] Checking migrations...')
@@ -83,6 +91,49 @@ async function runMigrations(conn: mysql.Connection) {
await conn.query("ALTER TABLE transactions ADD INDEX idx_group_date (group_id, date)")
console.log('[DB] Groups migration complete')
}
// users 表添加 role管理员角色
const hasRole = await columnExists(conn, 'users', 'role')
if (!hasRole) {
console.log('[DB] Migrating: adding role to users')
await conn.query("ALTER TABLE users ADD COLUMN role ENUM('user', 'admin') DEFAULT 'user' AFTER avatar_url")
}
// 通知表
const hasNotifications = await tableExists(conn, 'notifications')
if (!hasNotifications) {
console.log('[DB] Migrating: creating notifications table')
await conn.query(`
CREATE TABLE IF NOT EXISTS notifications (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT DEFAULT NULL COMMENT 'NULL=系统公告非NULL=个人通知',
group_id INT DEFAULT NULL COMMENT '群组通知关联',
type ENUM('system', 'group', 'personal') NOT NULL,
title VARCHAR(100) NOT NULL,
content TEXT,
is_read TINYINT(1) DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_read (user_id, is_read),
INDEX idx_type (type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
`)
}
// 保存的筛选方案表
const hasSavedFilters = await tableExists(conn, 'saved_filters')
if (!hasSavedFilters) {
console.log('[DB] Migrating: creating saved_filters table')
await conn.query(`
CREATE TABLE IF NOT EXISTS saved_filters (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
name VARCHAR(50) NOT NULL,
filters JSON NOT NULL COMMENT '筛选条件',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
`)
}
}
export async function initDatabase() {

View File

@@ -4,6 +4,7 @@ CREATE TABLE IF NOT EXISTS users (
session_key VARCHAR(100),
nickname VARCHAR(50) DEFAULT '小菜' COMMENT '用户昵称',
avatar_url VARCHAR(500) DEFAULT '' COMMENT '头像URL',
role ENUM('user', 'admin') DEFAULT 'user' COMMENT '角色',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@@ -72,3 +73,25 @@ CREATE TABLE IF NOT EXISTS group_members (
FOREIGN KEY (group_id) REFERENCES `groups`(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS notifications (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT DEFAULT NULL COMMENT 'NULL=系统公告非NULL=个人通知',
group_id INT DEFAULT NULL COMMENT '群组通知关联',
type ENUM('system', 'group', 'personal') NOT NULL,
title VARCHAR(100) NOT NULL,
content TEXT,
is_read TINYINT(1) DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_read (user_id, is_read),
INDEX idx_type (type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS saved_filters (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
name VARCHAR(50) NOT NULL,
filters JSON NOT NULL COMMENT '筛选条件:{type, categories[], startDate, endDate, minAmount, maxAmount, keyword}',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;