const express = require('express'); const db = require('../db'); const { getMonthRange } = require('../utils/helpers'); const router = express.Router(); // 格式化本地日期为 YYYY-MM-DD function formatLocalDate(date) { const y = date.getFullYear(); const m = String(date.getMonth() + 1).padStart(2, '0'); const d = String(date.getDate()).padStart(2, '0'); return `${y}-${m}-${d}`; } // 本月统计 router.get('/monthly', (req, res) => { const { startDate, endDate } = getMonthRange(req.query.year, req.query.month); const stats = db.prepare(` SELECT COALESCE(SUM(CASE WHEN category = '接单' THEN quantity ELSE 0 END), 0) as take_order_count, COALESCE(SUM(CASE WHEN category = '接单' THEN amount ELSE 0 END), 0) as take_order_income, COALESCE(SUM(CASE WHEN category = '派单' THEN quantity ELSE 0 END), 0) as dispatch_order_count, COALESCE(SUM(CASE WHEN category = '派单' THEN amount ELSE 0 END), 0) as dispatch_order_income, COALESCE(SUM(CASE WHEN category = '红包收入' THEN amount ELSE 0 END), 0) as redEnvelope_income, COALESCE(SUM(CASE WHEN category = '红包收入' THEN 1 ELSE 0 END), 0) as redEnvelope_count, COALESCE(SUM(CASE WHEN category = '奶茶' THEN amount ELSE 0 END), 0) as milkTea_income, COALESCE(SUM(CASE WHEN category = '奶茶' THEN 1 ELSE 0 END), 0) as milkTea_count, COALESCE(SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END), 0) as total_income, COALESCE(SUM(CASE WHEN type = 'expense' THEN amount ELSE 0 END), 0) as total_expense FROM records WHERE user_id = ? AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? `).get(req.user.id, startDate, endDate); res.json(stats); }); // 月收入/支出记录列表 router.get('/monthly-records', (req, res) => { const { type, page = 1, limit = 20 } = req.query; if (!type || !['income', 'expense'].includes(type)) return res.status(400).json({ error: '缺少类型参数' }); const { startDate, endDate } = getMonthRange(req.query.year, req.query.month); const offset = (Number(page) - 1) * Number(limit); // 合并 summary 和 total 为一次查询 const stats = db.prepare(` SELECT COALESCE(SUM(amount), 0) as total, COUNT(*) as count FROM records WHERE user_id = ? AND type = ? AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? `).get(req.user.id, type, startDate, endDate); const records = db.prepare(` SELECT * FROM records WHERE user_id = ? AND type = ? AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? ORDER BY created_at DESC LIMIT ? OFFSET ? `).all(req.user.id, type, startDate, endDate, Number(limit), offset); res.json({ total: stats.total, records, page: Number(page), limit: Number(limit), totalCount: stats.count }); }); // 年度净收入按年统计 router.get('/yearly-net-income', (req, res) => { // 获取所有年份数据 const yearlyStats = db.prepare(` SELECT strftime('%Y', created_at) as year, COALESCE(SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END), 0) as total_income, COALESCE(SUM(CASE WHEN type = 'expense' THEN amount ELSE 0 END), 0) as total_expense FROM records WHERE user_id = ? GROUP BY year ORDER BY year DESC `).all(req.user.id); // 获取所有年份的每月数据 const monthlyByYear = db.prepare(` SELECT strftime('%Y', created_at) as year, strftime('%m', created_at) as month, COALESCE(SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END), 0) as total_income, COALESCE(SUM(CASE WHEN type = 'expense' THEN amount ELSE 0 END), 0) as total_expense FROM records WHERE user_id = ? GROUP BY year, month ORDER BY year DESC, month `).all(req.user.id); // 按年份分组每月数据 const monthlyData = {}; monthlyByYear.forEach(m => { if (!monthlyData[m.year]) monthlyData[m.year] = []; monthlyData[m.year].push({ month: m.month, total_income: m.total_income, total_expense: m.total_expense }); }); const grandTotal = db.prepare(` SELECT COALESCE(SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END), 0) as total_income, COALESCE(SUM(CASE WHEN type = 'expense' THEN amount ELSE 0 END), 0) as total_expense FROM records WHERE user_id = ? `).get(req.user.id); res.json({ yearlyStats, monthlyData, grandTotal }); }); // 陪玩派单榜预览 router.get('/partner-ranking/preview', (req, res) => { const { startDate, endDate } = getMonthRange(req.query.year, req.query.month); const rows = db.prepare(` SELECT partner, COALESCE(SUM(quantity), 0) as dispatch_count, COALESCE(SUM(amount), 0) as dispatch_income FROM records WHERE user_id = ? AND category = '派单' AND partner IS NOT NULL AND partner != '' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? GROUP BY partner ORDER BY dispatch_count DESC LIMIT 10 `).all(req.user.id, startDate, endDate); res.json(rows); }); // 陪玩派单榜详情 router.get('/partner-ranking', (req, res) => { const { startDate, endDate } = req.query; let start, end; if (startDate && endDate) { start = startDate; end = endDate; } else { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); start = `${year}-${month}-01`; const endMonth = now.getMonth() + 2 > 12 ? 1 : now.getMonth() + 2; const endYear = endMonth === 1 ? year + 1 : year; end = `${endYear}-${String(endMonth).padStart(2, '0')}-01`; } const rows = db.prepare(` SELECT partner, COALESCE(SUM(quantity), 0) as dispatch_count, COALESCE(SUM(quantity * COALESCE(unit_price, 0)), 0) as dispatch_income, COALESCE(SUM(amount), 0) as rebate_income FROM records WHERE user_id = ? AND category = '派单' AND partner IS NOT NULL AND partner != '' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? GROUP BY partner ORDER BY dispatch_count DESC `).all(req.user.id, start, end); // 计算每个陪玩的陪玩总收入:(单价-返点)*单数 = 单价*单数 - 返点 rows.forEach(row => { row.partner_income = (row.dispatch_income || 0) - (row.rebate_income || 0); }); const summary = db.prepare(` SELECT COALESCE(SUM(quantity), 0) as total_dispatch_count, COALESCE(SUM(quantity * COALESCE(unit_price, 0)), 0) as total_dispatch_income, COALESCE(SUM(amount), 0) as total_rebate_income, COALESCE(SUM(quantity * COALESCE(unit_price, 0)), 0) - COALESCE(SUM(amount), 0) as total_partner_income FROM records WHERE user_id = ? AND category = '派单' AND partner IS NOT NULL AND partner != '' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? `).get(req.user.id, start, end); // 获取奶茶收入 const milkTeaStats = db.prepare(` SELECT COALESCE(SUM(amount), 0) as total_milkTea_income FROM records WHERE user_id = ? AND type = 'income' AND category = '奶茶' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? `).get(req.user.id, start, end); summary.total_milkTea_income = milkTeaStats.total_milkTea_income; res.json({ rows, summary }); }); // 总接单数按老板排行 router.get('/take-ranking', (req, res) => { const rows = db.prepare(` SELECT boss as name, COALESCE(SUM(quantity), 0) as count, COALESCE(SUM(amount), 0) as income FROM records WHERE user_id = ? AND category = '接单' AND boss IS NOT NULL AND boss != '' GROUP BY boss ORDER BY count DESC `).all(req.user.id); // 获取接单总收入和红包收入 const stats = db.prepare(` SELECT COALESCE(SUM(quantity), 0) as total_take_count, COALESCE(SUM(amount), 0) as total_take_income FROM records WHERE user_id = ? AND category = '接单' `).get(req.user.id); const redEnvelopeStats = db.prepare(` SELECT COALESCE(SUM(amount), 0) as total_redEnvelope_income FROM records WHERE user_id = ? AND type = 'income' AND category = '红包收入' `).get(req.user.id); const result = { rows: rows, total_take_count: stats.total_take_count, total_take_income: stats.total_take_income, total_redEnvelope_income: redEnvelopeStats.total_redEnvelope_income }; res.json(result); }); // 总派单数按陪玩排行 router.get('/dispatch-ranking', (req, res) => { const rows = db.prepare(` SELECT partner as name, COALESCE(SUM(quantity), 0) as dispatch_count, COALESCE(SUM(quantity * COALESCE(unit_price, 0)), 0) as dispatch_income, COALESCE(SUM(amount), 0) as rebate_income FROM records WHERE user_id = ? AND category = '派单' AND partner IS NOT NULL AND partner != '' GROUP BY partner ORDER BY dispatch_count DESC `).all(req.user.id); // 计算每个陪玩的陪玩总收入:(单价-返点)*单数 = 单价*单数 - 返点 rows.forEach(row => { row.partner_income = (row.dispatch_income || 0) - (row.rebate_income || 0); }); // 获取派单总数和奶茶收入 const dispatchTotal = db.prepare(` SELECT COALESCE(SUM(quantity), 0) as total_dispatch_count FROM records WHERE user_id = ? AND category = '派单' `).get(req.user.id); const milkTeaStats = db.prepare(` SELECT COALESCE(SUM(amount), 0) as total_milkTea_income FROM records WHERE user_id = ? AND type = 'income' AND category = '奶茶' `).get(req.user.id); const result = { rows: rows, total_dispatch_count: dispatchTotal.total_dispatch_count, total_milkTea_income: milkTeaStats.total_milkTea_income }; res.json(result); }); // 用户总统计 router.get('/total', (req, res) => { const stats = db.prepare(` SELECT COALESCE(SUM(CASE WHEN category = '接单' THEN quantity ELSE 0 END), 0) as take_count, COALESCE(SUM(CASE WHEN category = '派单' THEN quantity ELSE 0 END), 0) as dispatch_count, COALESCE(SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END), 0) as total_income, COALESCE(SUM(CASE WHEN type = 'expense' THEN amount ELSE 0 END), 0) as total_expense FROM records WHERE user_id = ? `).get(req.user.id); stats.net_income = stats.total_income - stats.total_expense; res.json(stats); }); // 老板交易记录 router.get('/boss-records', (req, res) => { const boss = req.query.boss; const { page = 1, limit = 20, year, month } = req.query; if (!boss) return res.status(400).json({ error: '缺少老板参数' }); const offset = (Number(page) - 1) * Number(limit); // 日期过滤条件 let dateFilter = ''; let dateParams = []; if (year && month) { const startDate = `${year}-${String(month).padStart(2, '0')}-01`; const endDate = `${year}-${String(month).padStart(2, '0')}-31`; dateFilter = ` AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') <= ?`; dateParams = [startDate, endDate]; } // 合并 summary 查询为一次 const summary = db.prepare(` SELECT COALESCE(SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END), 0) as my_income, COALESCE(SUM(CASE WHEN category = '派单' THEN quantity * COALESCE(unit_price, 0) ELSE 0 END), 0) as dispatch_expense, COALESCE(SUM(CASE WHEN category = '接单' THEN quantity * COALESCE(unit_price, 0) ELSE 0 END), 0) as order_expense, COALESCE(SUM(CASE WHEN category = '红包收入' THEN amount ELSE 0 END), 0) as redEnvelope_expense, COALESCE(SUM(CASE WHEN category = '派单' THEN quantity ELSE 0 END), 0) as dispatch_count, COALESCE(SUM(CASE WHEN category = '接单' THEN quantity ELSE 0 END), 0) as take_count, COUNT(*) as count FROM records WHERE user_id = ? AND COALESCE(boss, source) = ?${dateFilter} `).get(req.user.id, boss, ...dateParams); const records = db.prepare(` SELECT * FROM records WHERE user_id = ? AND COALESCE(boss, source) = ?${dateFilter} ORDER BY created_at DESC LIMIT ? OFFSET ? `).all(req.user.id, boss, ...dateParams, Number(limit), offset); res.json({ my_income: summary.my_income, boss_total_expense: summary.dispatch_expense + summary.order_expense + summary.redEnvelope_expense, take_count: summary.take_count, dispatch_count: summary.dispatch_count, records, page: Number(page), limit: Number(limit), totalCount: summary.count }); }); // 陪玩交易记录 router.get('/partner-records', (req, res) => { const partner = req.query.partner; const { page = 1, limit = 20, year, month } = req.query; if (!partner) return res.status(400).json({ error: '缺少陪玩参数' }); const offset = (Number(page) - 1) * Number(limit); // 日期过滤条件 let dateFilter = ''; let dateParams = []; if (year && month) { const startDate = `${year}-${String(month).padStart(2, '0')}-01`; const endDate = `${year}-${String(month).padStart(2, '0')}-31`; dateFilter = ` AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') <= ?`; dateParams = [startDate, endDate]; } // 合并 summary 查询为一次 const summary = db.prepare(` SELECT COALESCE(SUM(CASE WHEN category = '派单' THEN quantity ELSE 0 END), 0) as dispatch_count, COALESCE(SUM(CASE WHEN category = '派单' THEN quantity * (COALESCE(unit_price, 0) - COALESCE(rebate, amount * 1.0 / CASE WHEN quantity > 0 THEN quantity ELSE 1 END)) ELSE 0 END), 0) as spread_income, COALESCE(SUM(CASE WHEN category = '派单' THEN amount ELSE 0 END), 0) as rebate_income, COALESCE(SUM(CASE WHEN type = 'income' AND category != '派单' AND category != '接单' AND source = ? THEN amount ELSE 0 END), 0) as other_income, COUNT(*) as count FROM records WHERE user_id = ? AND ( (category = '派单' AND partner = ?) OR (type = 'income' AND category != '派单' AND category != '接单' AND source = ?) )${dateFilter} `).get(partner, req.user.id, partner, ...dateParams); const records = db.prepare(` SELECT * FROM records WHERE user_id = ? AND ( (category = '派单' AND partner = ?) OR (type = 'income' AND category != '派单' AND category != '接单' AND source = ?) )${dateFilter} ORDER BY created_at DESC LIMIT ? OFFSET ? `).all(req.user.id, partner, partner, ...dateParams, Number(limit), offset); res.json({ dispatch_count: dispatchStats.dispatch_count, dispatch_count: summary.dispatch_count, spread_income: summary.spread_income, rebate_income: summary.rebate_income, other_income: summary.other_income, records, page: Number(page), limit: Number(limit), totalCount: summary.count }); }); // 最近7天每日收入 router.get('/daily-income', (req, res) => { const today = new Date(); const days = []; for (let i = 6; i >= 0; i--) { const d = new Date(today); d.setDate(d.getDate() - i); days.push(formatLocalDate(d)); } const startDate = days[0]; const endDay = new Date(today); endDay.setDate(endDay.getDate() + 1); const endDate = formatLocalDate(endDay); const rows = db.prepare(` SELECT DATE(created_at, 'localtime') as date, COALESCE(SUM(amount), 0) as income FROM records WHERE user_id = ? AND type = 'income' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? GROUP BY DATE(created_at, 'localtime') `).all(req.user.id, startDate, endDate); const map = {}; rows.forEach(r => { map[r.date] = r.income; }); res.json(days.map(d => ({ date: d, income: map[d] || 0 }))); }); // 某天收入记录详情 router.get('/daily-detail', (req, res) => { const { date, page = 1, limit = 20 } = req.query; if (!date) return res.status(400).json({ error: '缺少日期参数' }); const offset = (Number(page) - 1) * Number(limit); const nextDay = new Date(date); nextDay.setDate(nextDay.getDate() + 1); const endDate = formatLocalDate(nextDay); const summary = db.prepare(` SELECT COALESCE(SUM(amount), 0) as total_income FROM records WHERE user_id = ? AND type = 'income' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? `).get(req.user.id, date, endDate); const total = db.prepare(` SELECT COUNT(*) as count FROM records WHERE user_id = ? AND type = 'income' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? `).get(req.user.id, date, endDate).count; const records = db.prepare(` SELECT * FROM records WHERE user_id = ? AND type = 'income' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? ORDER BY created_at DESC LIMIT ? OFFSET ? `).all(req.user.id, date, endDate, Number(limit), offset); res.json({ total_income: summary.total_income, records, page: Number(page), limit: Number(limit), totalCount: total }); }); // 老板收入排行 - 首页预览 router.get('/boss-ranking/preview', (req, res) => { const { startDate, endDate } = getMonthRange(req.query.year, req.query.month); const rows = db.prepare(` SELECT COALESCE(boss, source) as boss, SUM(amount) as total_income, COALESCE(SUM(CASE WHEN category = '接单' THEN quantity ELSE 0 END), 0) as take_count, COALESCE(SUM(CASE WHEN category = '派单' THEN quantity ELSE 0 END), 0) as dispatch_count, COALESCE(SUM(CASE WHEN category = '红包收入' THEN amount ELSE 0 END), 0) as redEnvelope_income FROM records WHERE user_id = ? AND type = 'income' AND (boss IS NOT NULL AND boss != '' OR source IS NOT NULL AND source != '') AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? GROUP BY COALESCE(boss, source) ORDER BY total_income DESC LIMIT 10 `).all(req.user.id, startDate, endDate); res.json(rows); }); // 老板收入排行 - 详情页(支持筛选) router.get('/boss-ranking', (req, res) => { const { startDate, endDate, sortBy = 'total_income' } = req.query; let start, end; if (startDate && endDate) { start = startDate; end = endDate; } else { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); start = `${year}-${month}-01`; const endMonth = now.getMonth() + 2 > 12 ? 1 : now.getMonth() + 2; const endYear = endMonth === 1 ? year + 1 : year; end = `${endYear}-${String(endMonth).padStart(2, '0')}-01`; } const allowedSorts = ['total_income', 'take_count', 'dispatch_count']; const orderCol = allowedSorts.includes(sortBy) ? sortBy : 'total_income'; if (sortBy === 'dispatch_count') { const rows = db.prepare(` SELECT COALESCE(boss, source) as boss, COALESCE(SUM(CASE WHEN category = '派单' THEN quantity ELSE 0 END), 0) as dispatch_count, COALESCE(SUM(CASE WHEN category = '派单' THEN amount ELSE 0 END), 0) as dispatch_income, SUM(amount) as total_income, COALESCE(SUM(CASE WHEN category = '接单' THEN quantity ELSE 0 END), 0) as take_count, COALESCE(SUM(CASE WHEN category = '接单' THEN amount ELSE 0 END), 0) as take_income, COALESCE(SUM(CASE WHEN category = '红包收入' THEN amount ELSE 0 END), 0) as redEnvelope_income FROM records WHERE user_id = ? AND type = 'income' AND (boss IS NOT NULL AND boss != '' OR source IS NOT NULL AND source != '') AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? GROUP BY COALESCE(boss, source) ORDER BY dispatch_count DESC `).all(req.user.id, start, end); return res.json(rows.filter(r => r.dispatch_count > 0)); } const rows = db.prepare(` SELECT COALESCE(boss, source) as boss, SUM(amount) as total_income, COALESCE(SUM(CASE WHEN category = '接单' THEN quantity ELSE 0 END), 0) as take_count, COALESCE(SUM(CASE WHEN category = '接单' THEN amount ELSE 0 END), 0) as take_income, COALESCE(SUM(CASE WHEN category = '派单' THEN quantity ELSE 0 END), 0) as dispatch_count, COALESCE(SUM(CASE WHEN category = '派单' THEN amount ELSE 0 END), 0) as dispatch_income, COALESCE(SUM(CASE WHEN category = '红包收入' THEN amount ELSE 0 END), 0) as redEnvelope_income FROM records WHERE user_id = ? AND type = 'income' AND (boss IS NOT NULL AND boss != '' OR source IS NOT NULL AND source != '') AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? GROUP BY COALESCE(boss, source) ORDER BY ${orderCol} DESC `).all(req.user.id, start, end); if (sortBy === 'take_count') { return res.json(rows.filter(r => r.take_count > 0)); } res.json(rows); }); module.exports = router;