feat: 前后端功能对齐 - 实现8个对齐差距

P0-1: 分类拖拽排序 - category-manage 添加拖拽 UI
P1-1: 反馈回复展示 - 新增 GET /feedback/mine + 前端我的反馈Tab
P1-2: 备份管理页面 - 新增下载端点 + backup-manage 页面
P2-1: 统计年度/周视图 - stats 支持 period 参数 + 前端维度切换器
P2-2: 数据导入 - 新增 POST /import 端点 + data-import 页面
P2-3: 数据导出服务端化 - 新增 GET /export 流式端点
P3-1: 健康检查展示 - health 移到 auth 前 + admin 状态卡片
P3-2~4: 交易标签系统 - tags CRUD + 交易关联 + 按标签筛选统计

后端: 新增 tag.ts/export.ts 路由, 改造 feedback/backup/transaction/stats
前端: 新增 DragSortList 组件, 3个新页面, 改造 7 个现有页面
QA 修复: 5个严重Bug + 4个潜在问题
This commit is contained in:
2026-06-10 17:30:36 +08:00
parent 5df910c9f1
commit 31f6487d61
36 changed files with 4162 additions and 64 deletions

View File

@@ -5,6 +5,36 @@ import { requireAdmin } from '../middleware/requireAdmin'
const router = Router()
/** 当前用户的反馈列表 */
router.get('/mine', authMiddleware, async (req, res) => {
try {
const userId = (req as any).userId
const page = Math.max(1, parseInt(req.query.page as string) || 1)
const pageSize = Math.min(100, Math.max(1, parseInt(req.query.pageSize as string) || 20))
const offset = (page - 1) * pageSize
const [countResult] = await pool.execute(
'SELECT COUNT(*) as total FROM feedbacks WHERE user_id = ?',
[userId]
)
const total = (countResult as any[])[0].total
const [rows] = await pool.execute(
`SELECT id, type, content, contact, status, admin_reply, created_at
FROM feedbacks
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?`,
[userId, pageSize, offset]
)
res.json({ code: 0, data: { list: rows, total, page, pageSize } })
} catch (error) {
console.error('Get my feedback error:', error)
res.status(500).json({ code: 50000, message: '获取失败' })
}
})
/** 用户提交反馈 */
router.post('/', authMiddleware, async (req, res) => {
try {
@@ -52,15 +82,15 @@ router.get('/', requireAdmin, async (req, res) => {
)
const total = (countResult as any[])[0].total
// LIMIT/OFFSET 直接嵌入 SQLexecute 对参数类型要求严格)
// LIMIT/OFFSET 使用参数化查询
const [rows] = await pool.execute(
`SELECT f.*, u.nickname, u.avatar_url
FROM feedbacks f
LEFT JOIN users u ON f.user_id = u.id
WHERE ${where}
ORDER BY f.created_at DESC
LIMIT ${pageSize} OFFSET ${offset}`,
params
LIMIT ? OFFSET ?`,
[...params, pageSize, offset]
)
res.json({ code: 0, data: { list: rows, total, page, pageSize } })